mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 06:08:29 +00:00
Add support for cross-compiling in create-exe with zig cc WIP (#3076)
* Add support for cross-compiling in create-exe with zig cc WIP zig version must be at least 0.10.0-dev.3431+4a4f3c50c Closes #3042 * Add support for cross-compiling in create-exe with zig cc WIP zig version must be at least 0.10.0-dev.3431+4a4f3c50c Closes #3042 * Add SSE2 features to the CPU * Add SSE2 features to the CPU * create_exe: locate zig binary and check minimum version Check that `zig` binary version is at least `0.10.0` * create-exe: refactor cross-comp cli parsing and error checks * create-exe: add -lunwind for cross-comp with zig * Set the proper library for windows * create-exe: add path exists check for --tarball value * create-exe: add -msvc environment in triple_to_zig_triple() Co-authored-by: Syrus Akbary <me@syrusakbary.com>
This commit is contained in:
committed by
GitHub
parent
3b67f8d17f
commit
ba543fc4f7
119
Cargo.lock
generated
119
Cargo.lock
generated
@@ -125,6 +125,12 @@ version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@@ -757,6 +763,15 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
@@ -767,6 +782,17 @@ dependencies = [
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
@@ -1112,6 +1138,18 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http_req"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e6cd45a270dff33553602fd84beb02c89460ee32db638715f10d9060389fd6a"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"unicase",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@@ -1916,6 +1954,21 @@ dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.39"
|
||||
@@ -1993,6 +2046,19 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.8"
|
||||
@@ -2026,6 +2092,16 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sdl2"
|
||||
version = "0.35.2"
|
||||
@@ -2219,6 +2295,12 @@ version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@@ -2631,6 +2713,15 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.2"
|
||||
@@ -2655,6 +2746,12 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35abed4630bb800f02451a7428205d1f37b8e125001471bfab259beee6a587ed"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
@@ -2923,9 +3020,12 @@ dependencies = [
|
||||
"bytesize",
|
||||
"cfg-if 1.0.0",
|
||||
"colored 2.0.0",
|
||||
"dirs",
|
||||
"distance",
|
||||
"fern",
|
||||
"http_req",
|
||||
"log",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"tempfile",
|
||||
"unix_mode",
|
||||
@@ -3435,6 +3535,25 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
|
||||
@@ -52,6 +52,9 @@ cfg-if = "1.0"
|
||||
fern = { version = "0.6", features = ["colored"], optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
tempfile = "3"
|
||||
http_req = { version="^0.8", default-features = false, features = ["rust-tls"], optional = true }
|
||||
dirs = { version = "4.0", optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
unix_mode = "0.1.3"
|
||||
@@ -62,6 +65,7 @@ unix_mode = "0.1.3"
|
||||
default = [
|
||||
"wat",
|
||||
"wast",
|
||||
"http",
|
||||
"cache",
|
||||
"wasi",
|
||||
"emscripten",
|
||||
@@ -131,3 +135,9 @@ enable-serde = [
|
||||
"wasmer-types/enable-serde",
|
||||
"wasmer-wasi/enable-serde",
|
||||
]
|
||||
|
||||
http = [
|
||||
"http_req",
|
||||
"dirs",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -24,6 +24,24 @@ const WASMER_MAIN_C_SOURCE: &[u8] = include_bytes!("wasmer_create_exe_main.c");
|
||||
#[cfg(feature = "static-artifact-create")]
|
||||
const WASMER_STATIC_MAIN_C_SOURCE: &[u8] = include_bytes!("wasmer_static_create_exe_main.c");
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CrossCompile {
|
||||
/// Cross-compilation library path.
|
||||
library_path: Option<PathBuf>,
|
||||
|
||||
/// Cross-compilation tarball library path.
|
||||
tarball: Option<PathBuf>,
|
||||
|
||||
/// Specify `zig` binary path
|
||||
zig_binary_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
struct CrossCompileSetup {
|
||||
target: Triple,
|
||||
zig_binary_path: PathBuf,
|
||||
library: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// The options for the `wasmer create-exe` subcommand
|
||||
pub struct CreateExe {
|
||||
@@ -39,6 +57,19 @@ pub struct CreateExe {
|
||||
#[structopt(long = "target")]
|
||||
target_triple: Option<Triple>,
|
||||
|
||||
// Cross-compile with `zig`
|
||||
/// Cross-compilation library path.
|
||||
#[structopt(long = "library-path")]
|
||||
library_path: Option<PathBuf>,
|
||||
|
||||
/// Cross-compilation tarball library path.
|
||||
#[structopt(long = "tarball")]
|
||||
tarball: Option<PathBuf>,
|
||||
|
||||
/// Specify `zig` binary path
|
||||
#[structopt(long = "zig-binary-path")]
|
||||
zig_binary_path: Option<PathBuf>,
|
||||
|
||||
/// Object format options
|
||||
///
|
||||
/// This flag accepts two options: `symbols` or `serialized`.
|
||||
@@ -70,6 +101,27 @@ pub struct CreateExe {
|
||||
impl CreateExe {
|
||||
/// Runs logic for the `compile` subcommand
|
||||
pub fn execute(&self) -> Result<()> {
|
||||
/* Making library_path, tarball zig_binary_path flags require that target_triple flag
|
||||
* is set cannot be encoded with structopt, so we have to perform cli flag validation
|
||||
* manually here */
|
||||
let cross_compile: Option<CrossCompile> = if self.target_triple.is_none()
|
||||
&& (self.library_path.is_some()
|
||||
|| self.tarball.is_some()
|
||||
|| self.zig_binary_path.is_some())
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"To cross-compile an executable, you must specify a target triple with --target"
|
||||
));
|
||||
} else if self.target_triple.is_some() {
|
||||
Some(CrossCompile {
|
||||
library_path: self.library_path.clone(),
|
||||
zig_binary_path: self.zig_binary_path.clone(),
|
||||
tarball: self.tarball.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let target = self
|
||||
.target_triple
|
||||
.as_ref()
|
||||
@@ -81,22 +133,117 @@ impl CreateExe {
|
||||
.fold(CpuFeature::set(), |a, b| a | b);
|
||||
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
|
||||
// usage
|
||||
if target_triple.architecture == Architecture::X86_64 {
|
||||
features |= CpuFeature::SSE2;
|
||||
}
|
||||
Target::new(target_triple.clone(), features)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let (store, compiler_type) = self.compiler.get_store_for_target(target.clone())?;
|
||||
|
||||
let object_format = self.object_format.unwrap_or(ObjectFormat::Symbols);
|
||||
|
||||
println!("Compiler: {}", compiler_type.to_string());
|
||||
println!("Target: {}", target.triple());
|
||||
println!("Format: {:?}", object_format);
|
||||
|
||||
let working_dir = tempfile::tempdir()?;
|
||||
let starting_cd = env::current_dir()?;
|
||||
let output_path = starting_cd.join(&self.output);
|
||||
env::set_current_dir(&working_dir)?;
|
||||
|
||||
let cross_compilation: Option<CrossCompileSetup> = if let Some(mut cross_subc) =
|
||||
cross_compile.or_else(|| {
|
||||
if self.target_triple.is_some() {
|
||||
Some(CrossCompile {
|
||||
library_path: None,
|
||||
tarball: None,
|
||||
zig_binary_path: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
if let ObjectFormat::Serialized = object_format {
|
||||
return Err(anyhow!(
|
||||
"Cross-compilation with serialized object format is not implemented."
|
||||
));
|
||||
}
|
||||
|
||||
let target = if let Some(target_triple) = self.target_triple.clone() {
|
||||
target_triple
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"To cross-compile an executable, you must specify a target triple with --target"
|
||||
));
|
||||
};
|
||||
if let Some(tarball_path) = cross_subc.tarball.as_mut() {
|
||||
if tarball_path.is_relative() {
|
||||
*tarball_path = starting_cd.join(&tarball_path);
|
||||
if !tarball_path.exists() {
|
||||
return Err(anyhow!(
|
||||
"Tarball path `{}` does not exist.",
|
||||
tarball_path.display()
|
||||
));
|
||||
} else if tarball_path.is_dir() {
|
||||
return Err(anyhow!(
|
||||
"Tarball path `{}` is a directory.",
|
||||
tarball_path.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let zig_binary_path =
|
||||
find_zig_binary(cross_subc.zig_binary_path.as_ref().and_then(|p| {
|
||||
if p.is_absolute() {
|
||||
p.canonicalize().ok()
|
||||
} else {
|
||||
starting_cd.join(p).canonicalize().ok()
|
||||
}
|
||||
}))?;
|
||||
let library = if let Some(v) = cross_subc.library_path.clone() {
|
||||
v
|
||||
} else {
|
||||
{
|
||||
let libwasmer_path = if self
|
||||
.target_triple
|
||||
.clone()
|
||||
.unwrap_or(Triple::host())
|
||||
.operating_system
|
||||
== wasmer_types::OperatingSystem::Windows
|
||||
{
|
||||
"lib/wasmer.lib"
|
||||
} else {
|
||||
"lib/libwasmer.a"
|
||||
};
|
||||
let filename = if let Some(local_tarball) = cross_subc.tarball {
|
||||
let files = untar(local_tarball)?;
|
||||
files.into_iter().find(|f| f.contains(libwasmer_path)).ok_or_else(|| {
|
||||
anyhow!("Could not find libwasmer for {} target in the provided tarball path.", target)})?
|
||||
} else {
|
||||
#[cfg(feature = "http")]
|
||||
{
|
||||
let release = http_fetch::get_latest_release()?;
|
||||
let tarball = http_fetch::download_release(release, target.clone())?;
|
||||
let files = untar(tarball)?;
|
||||
files.into_iter().find(|f| f.contains(libwasmer_path)).ok_or_else(|| {
|
||||
anyhow!("Could not find libwasmer for {} target in the fetched release from Github: you can download it manually and specify its path with the --cross-compilation-library-path LIBRARY_PATH flag.", target)})?
|
||||
}
|
||||
#[cfg(not(feature = "http"))]
|
||||
return Err(anyhow!("This wasmer binary isn't compiled with an HTTP request library (feature flag `http`). To cross-compile, specify the path of the non-native libwasmer or release tarball with the --library-path LIBRARY_PATH or --tarball TARBALL_PATH flag."));
|
||||
};
|
||||
filename.into()
|
||||
}
|
||||
};
|
||||
Some(CrossCompileSetup {
|
||||
target,
|
||||
zig_binary_path,
|
||||
library,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (store, compiler_type) = self.compiler.get_store_for_target(target.clone())?;
|
||||
|
||||
println!("Compiler: {}", compiler_type.to_string());
|
||||
println!("Target: {}", target.triple());
|
||||
println!("Format: {:?}", object_format);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let wasm_object_path = PathBuf::from("wasm.o");
|
||||
#[cfg(windows)]
|
||||
@@ -105,14 +252,25 @@ impl CreateExe {
|
||||
let wasm_module_path = starting_cd.join(&self.path);
|
||||
|
||||
if let Some(header_path) = self.header.as_ref() {
|
||||
/* In this case, since a header file is given, the input file is expected to be an
|
||||
* object created with `create-obj` subcommand */
|
||||
let header_path = starting_cd.join(&header_path);
|
||||
std::fs::copy(&header_path, Path::new("static_defs.h"))
|
||||
.context("Could not access given header file")?;
|
||||
link(
|
||||
if let Some(setup) = cross_compilation.as_ref() {
|
||||
self.compile_zig(
|
||||
output_path,
|
||||
wasm_module_path,
|
||||
std::path::Path::new("static_defs.h").into(),
|
||||
setup,
|
||||
)?;
|
||||
} else {
|
||||
self.link(
|
||||
output_path,
|
||||
wasm_module_path,
|
||||
std::path::Path::new("static_defs.h").into(),
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
match object_format {
|
||||
ObjectFormat::Serialized => {
|
||||
@@ -127,7 +285,8 @@ impl CreateExe {
|
||||
writer.flush()?;
|
||||
drop(writer);
|
||||
|
||||
self.compile_c(wasm_object_path, output_path)?;
|
||||
let cli_given_triple = self.target_triple.clone();
|
||||
self.compile_c(wasm_object_path, cli_given_triple, output_path)?;
|
||||
}
|
||||
#[cfg(not(feature = "static-artifact-create"))]
|
||||
ObjectFormat::Symbols => {
|
||||
@@ -152,19 +311,26 @@ impl CreateExe {
|
||||
&*symbol_registry,
|
||||
metadata_length,
|
||||
);
|
||||
/* Write object file with functions */
|
||||
// Write object file with functions
|
||||
let object_file_path: std::path::PathBuf =
|
||||
std::path::Path::new("functions.o").into();
|
||||
let mut writer = BufWriter::new(File::create(&object_file_path)?);
|
||||
obj.write_stream(&mut writer)
|
||||
.map_err(|err| anyhow::anyhow!(err.to_string()))?;
|
||||
writer.flush()?;
|
||||
/* Write down header file that includes pointer arrays and the deserialize function
|
||||
* */
|
||||
// Write down header file that includes pointer arrays and the deserialize function
|
||||
let mut writer = BufWriter::new(File::create("static_defs.h")?);
|
||||
writer.write_all(header_file_src.as_bytes())?;
|
||||
writer.flush()?;
|
||||
link(
|
||||
if let Some(setup) = cross_compilation.as_ref() {
|
||||
self.compile_zig(
|
||||
output_path,
|
||||
object_file_path,
|
||||
std::path::Path::new("static_defs.h").into(),
|
||||
setup,
|
||||
)?;
|
||||
} else {
|
||||
self.link(
|
||||
output_path,
|
||||
object_file_path,
|
||||
std::path::Path::new("static_defs.h").into(),
|
||||
@@ -172,15 +338,30 @@ impl CreateExe {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cross_compilation.is_some() {
|
||||
eprintln!(
|
||||
"✔ Cross-compiled executable for `{}` target compiled successfully to `{}`.",
|
||||
target.triple(),
|
||||
self.output.display(),
|
||||
);
|
||||
} else {
|
||||
eprintln!(
|
||||
"✔ Native executable compiled successfully to `{}`.",
|
||||
self.output.display(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_c(&self, wasm_object_path: PathBuf, output_path: PathBuf) -> anyhow::Result<()> {
|
||||
fn compile_c(
|
||||
&self,
|
||||
wasm_object_path: PathBuf,
|
||||
target_triple: Option<wasmer::Triple>,
|
||||
output_path: PathBuf,
|
||||
) -> anyhow::Result<()> {
|
||||
// write C src to disk
|
||||
let c_src_path = Path::new("wasmer_main.c");
|
||||
#[cfg(not(windows))]
|
||||
@@ -196,13 +377,13 @@ impl CreateExe {
|
||||
.context("Failed to open C source code file")?;
|
||||
c_src_file.write_all(WASMER_MAIN_C_SOURCE)?;
|
||||
}
|
||||
run_c_compile(c_src_path, &c_src_obj, self.target_triple.clone())
|
||||
run_c_compile(c_src_path, &c_src_obj, target_triple.clone())
|
||||
.context("Failed to compile C source code")?;
|
||||
LinkCode {
|
||||
object_paths: vec![c_src_obj, wasm_object_path],
|
||||
output_path,
|
||||
additional_libraries: self.libraries.clone(),
|
||||
target: self.target_triple.clone(),
|
||||
target: target_triple,
|
||||
..Default::default()
|
||||
}
|
||||
.run()
|
||||
@@ -210,25 +391,29 @@ impl CreateExe {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "static-artifact-create")]
|
||||
fn link(
|
||||
fn compile_zig(
|
||||
&self,
|
||||
output_path: PathBuf,
|
||||
object_path: PathBuf,
|
||||
mut header_code_path: PathBuf,
|
||||
setup: &CrossCompileSetup,
|
||||
) -> anyhow::Result<()> {
|
||||
let linkcode = LinkCode {
|
||||
object_paths: vec![object_path, "main_obj.obj".into()],
|
||||
output_path,
|
||||
..Default::default()
|
||||
};
|
||||
let c_src_path = Path::new("wasmer_main.c");
|
||||
let mut libwasmer_path = get_libwasmer_path()?
|
||||
.canonicalize()
|
||||
.context("Failed to find libwasmer")?;
|
||||
println!("Using libwasmer: {}", libwasmer_path.display());
|
||||
let _lib_filename = libwasmer_path
|
||||
let CrossCompileSetup {
|
||||
ref target,
|
||||
ref zig_binary_path,
|
||||
ref library,
|
||||
} = setup;
|
||||
let mut libwasmer_path = library.to_path_buf();
|
||||
|
||||
println!("Library Path: {}", libwasmer_path.display());
|
||||
/* Cross compilation is only possible with zig */
|
||||
println!("Using zig binary: {}", zig_binary_path.display());
|
||||
let zig_triple = triple_to_zig_triple(target);
|
||||
eprintln!("Using zig target triple: {}", &zig_triple);
|
||||
|
||||
let lib_filename = libwasmer_path
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
@@ -248,8 +433,91 @@ fn link(
|
||||
header_code_path.pop();
|
||||
}
|
||||
|
||||
if header_code_path.display().to_string().is_empty() {
|
||||
header_code_path = std::env::current_dir()?;
|
||||
}
|
||||
|
||||
/* Compile main function */
|
||||
let compilation = Command::new("cc")
|
||||
let compilation = {
|
||||
let mut include_dir = libwasmer_path.clone();
|
||||
include_dir.pop();
|
||||
include_dir.push("include");
|
||||
|
||||
let mut cmd = Command::new(zig_binary_path);
|
||||
let mut cmd_mut: &mut Command = cmd
|
||||
.arg("cc")
|
||||
.arg("-target")
|
||||
.arg(&zig_triple)
|
||||
.arg(&format!("-L{}", libwasmer_path.display()))
|
||||
.arg(&format!("-l:{}", lib_filename))
|
||||
.arg(&format!("-I{}", include_dir.display()))
|
||||
.arg(&format!("-I{}", header_code_path.display()));
|
||||
if !zig_triple.contains("windows") {
|
||||
cmd_mut = cmd_mut.arg("-lunwind");
|
||||
}
|
||||
cmd_mut
|
||||
.arg(&object_path)
|
||||
.arg(&c_src_path)
|
||||
.arg("-o")
|
||||
.arg(&output_path)
|
||||
.output()
|
||||
.context("Could not execute `zig`")?
|
||||
};
|
||||
if !compilation.status.success() {
|
||||
return Err(anyhow::anyhow!(String::from_utf8_lossy(
|
||||
&compilation.stderr
|
||||
)
|
||||
.to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "static-artifact-create")]
|
||||
fn link(
|
||||
&self,
|
||||
output_path: PathBuf,
|
||||
object_path: PathBuf,
|
||||
mut header_code_path: PathBuf,
|
||||
) -> anyhow::Result<()> {
|
||||
let linkcode = LinkCode {
|
||||
object_paths: vec![object_path, "main_obj.obj".into()],
|
||||
output_path,
|
||||
..Default::default()
|
||||
};
|
||||
let c_src_path = Path::new("wasmer_main.c");
|
||||
let mut libwasmer_path = get_libwasmer_path()?
|
||||
.canonicalize()
|
||||
.context("Failed to find libwasmer")?;
|
||||
|
||||
println!("Using libwasmer file: {}", libwasmer_path.display());
|
||||
|
||||
let lib_filename = libwasmer_path
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
libwasmer_path.pop();
|
||||
{
|
||||
let mut c_src_file = fs::OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(&c_src_path)
|
||||
.context("Failed to open C source code file")?;
|
||||
c_src_file.write_all(WASMER_STATIC_MAIN_C_SOURCE)?;
|
||||
}
|
||||
|
||||
if !header_code_path.is_dir() {
|
||||
header_code_path.pop();
|
||||
}
|
||||
|
||||
if header_code_path.display().to_string().is_empty() {
|
||||
header_code_path = std::env::current_dir()?;
|
||||
}
|
||||
|
||||
/* Compile main function */
|
||||
let compilation = {
|
||||
Command::new("cc")
|
||||
.arg("-c")
|
||||
.arg(&c_src_path)
|
||||
.arg(if linkcode.optimization_flag.is_empty() {
|
||||
@@ -259,8 +527,8 @@ fn link(
|
||||
})
|
||||
.arg(&format!("-L{}", libwasmer_path.display()))
|
||||
.arg(&format!("-I{}", get_wasmer_include_directory()?.display()))
|
||||
//.arg(&format!("-l:{}", lib_filename))
|
||||
.arg("-lwasmer")
|
||||
.arg(&format!("-l:{}", lib_filename))
|
||||
//.arg("-lwasmer")
|
||||
// Add libraries required per platform.
|
||||
// We need userenv, sockets (Ws2_32), advapi32 for some system calls and bcrypt for random numbers.
|
||||
//#[cfg(windows)]
|
||||
@@ -277,7 +545,8 @@ fn link(
|
||||
.arg("-v")
|
||||
.arg("-o")
|
||||
.arg("main_obj.obj")
|
||||
.output()?;
|
||||
.output()?
|
||||
};
|
||||
if !compilation.status.success() {
|
||||
return Err(anyhow::anyhow!(String::from_utf8_lossy(
|
||||
&compilation.stderr
|
||||
@@ -287,6 +556,30 @@ fn link(
|
||||
linkcode.run().context("Failed to link objects together")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn triple_to_zig_triple(target_triple: &Triple) -> String {
|
||||
let arch = match target_triple.architecture {
|
||||
wasmer_types::Architecture::X86_64 => "x86_64".into(),
|
||||
wasmer_types::Architecture::Aarch64(wasmer_types::Aarch64Architecture::Aarch64) => {
|
||||
"aarch64".into()
|
||||
}
|
||||
v => v.to_string(),
|
||||
};
|
||||
let os = match target_triple.operating_system {
|
||||
wasmer_types::OperatingSystem::Linux => "linux".into(),
|
||||
wasmer_types::OperatingSystem::Darwin => "macos".into(),
|
||||
wasmer_types::OperatingSystem::Windows => "windows".into(),
|
||||
v => v.to_string(),
|
||||
};
|
||||
let env = match target_triple.environment {
|
||||
wasmer_types::Environment::Musl => "musl",
|
||||
wasmer_types::Environment::Gnu => "gnu",
|
||||
wasmer_types::Environment::Msvc => "msvc",
|
||||
_ => "none",
|
||||
};
|
||||
format!("{}-{}-{}", arch, os, env)
|
||||
}
|
||||
|
||||
fn get_wasmer_dir() -> anyhow::Result<PathBuf> {
|
||||
Ok(PathBuf::from(
|
||||
@@ -452,3 +745,247 @@ impl LinkCode {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
mod http_fetch {
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use http_req::{
|
||||
request::Request,
|
||||
response::{Response, StatusCode},
|
||||
uri::Uri,
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub fn get_latest_release() -> Result<serde_json::Value> {
|
||||
let mut writer = Vec::new();
|
||||
let uri = Uri::try_from("https://api.github.com/repos/wasmerio/wasmer/releases").unwrap();
|
||||
|
||||
let response = Request::new(&uri)
|
||||
.header("User-Agent", "wasmer")
|
||||
.header("Accept", "application/vnd.github.v3+json")
|
||||
.timeout(Some(std::time::Duration::new(30, 0)))
|
||||
.send(&mut writer)
|
||||
.map_err(anyhow::Error::new)
|
||||
.context("Could not lookup wasmer repository on Github.")?;
|
||||
|
||||
if response.status_code() != StatusCode::new(200) {
|
||||
return Err(anyhow!(
|
||||
"Github API replied with non-200 status code: {}",
|
||||
response.status_code()
|
||||
));
|
||||
}
|
||||
|
||||
let v: std::result::Result<serde_json::Value, _> = serde_json::from_reader(&*writer);
|
||||
let mut response = v.map_err(anyhow::Error::new)?;
|
||||
|
||||
if let Some(releases) = response.as_array_mut() {
|
||||
releases.retain(|r| {
|
||||
r["tag_name"].is_string() && !r["tag_name"].as_str().unwrap().is_empty()
|
||||
});
|
||||
releases.sort_by_cached_key(|r| r["tag_name"].as_str().unwrap_or_default().to_string());
|
||||
if let Some(latest) = releases.pop() {
|
||||
return Ok(latest);
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow!(
|
||||
"Could not get expected Github API response.\n\nReason: response format is not recognized:\n{:#?}", ""
|
||||
))
|
||||
}
|
||||
|
||||
pub fn download_release(
|
||||
mut release: serde_json::Value,
|
||||
target_triple: wasmer::Triple,
|
||||
) -> Result<std::path::PathBuf> {
|
||||
if let Some(assets) = release["assets"].as_array_mut() {
|
||||
assets.retain(|a| {
|
||||
if let Some(name) = a["name"].as_str() {
|
||||
match target_triple.architecture {
|
||||
wasmer_types::Architecture::X86_64 => {
|
||||
name.contains("x86_64") || name.contains("amd64")
|
||||
}
|
||||
wasmer_types::Architecture::Aarch64(
|
||||
wasmer_types::Aarch64Architecture::Aarch64,
|
||||
) => name.contains("arm64") || name.contains("aarch64"),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
assets.retain(|a| {
|
||||
if let Some(name) = a["name"].as_str() {
|
||||
match target_triple.vendor {
|
||||
wasmer_types::Vendor::Apple => {
|
||||
name.contains("apple")
|
||||
|| name.contains("macos")
|
||||
|| name.contains("darwin")
|
||||
}
|
||||
wasmer_types::Vendor::Pc => name.contains("windows"),
|
||||
_ => true,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
assets.retain(|a| {
|
||||
if let Some(name) = a["name"].as_str() {
|
||||
match target_triple.operating_system {
|
||||
wasmer_types::OperatingSystem::Darwin => {
|
||||
name.contains("apple")
|
||||
|| name.contains("darwin")
|
||||
|| name.contains("macos")
|
||||
}
|
||||
wasmer_types::OperatingSystem::Windows => name.contains("windows"),
|
||||
wasmer_types::OperatingSystem::Linux => name.contains("linux"),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
assets.retain(|a| {
|
||||
if let Some(name) = a["name"].as_str() {
|
||||
match target_triple.environment {
|
||||
wasmer_types::Environment::Musl => name.contains("musl"),
|
||||
_ => !name.contains("musl"),
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if assets.len() == 1 {
|
||||
let browser_download_url =
|
||||
if let Some(url) = assets[0]["browser_download_url"].as_str() {
|
||||
url.to_string()
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"Could not get download url from Github API response."
|
||||
));
|
||||
};
|
||||
let filename = browser_download_url
|
||||
.split('/')
|
||||
.last()
|
||||
.unwrap_or("output")
|
||||
.to_string();
|
||||
let mut file = std::fs::File::create(&filename)?;
|
||||
println!("Downloading {} to {}", browser_download_url, &filename);
|
||||
let download_thread: std::thread::JoinHandle<Result<Response, anyhow::Error>> =
|
||||
std::thread::spawn(move || {
|
||||
let uri = Uri::try_from(browser_download_url.as_str())?;
|
||||
let mut response = Request::new(&uri)
|
||||
.header("User-Agent", "wasmer")
|
||||
.send(&mut file)
|
||||
.map_err(anyhow::Error::new)
|
||||
.context("Could not lookup wasmer artifact on Github.")?;
|
||||
if response.status_code() == StatusCode::new(302) {
|
||||
let redirect_uri =
|
||||
Uri::try_from(response.headers().get("Location").unwrap().as_str())
|
||||
.unwrap();
|
||||
response = Request::new(&redirect_uri)
|
||||
.header("User-Agent", "wasmer")
|
||||
.send(&mut file)
|
||||
.map_err(anyhow::Error::new)
|
||||
.context("Could not lookup wasmer artifact on Github.")?;
|
||||
}
|
||||
Ok(response)
|
||||
});
|
||||
let _response = download_thread
|
||||
.join()
|
||||
.expect("Could not join downloading thread");
|
||||
return Ok(filename.into());
|
||||
}
|
||||
}
|
||||
Err(anyhow!("Could not get release artifact."))
|
||||
}
|
||||
}
|
||||
|
||||
fn untar(tarball: std::path::PathBuf) -> Result<Vec<String>> {
|
||||
let files = std::process::Command::new("tar")
|
||||
.arg("-tf")
|
||||
.arg(&tarball)
|
||||
.output()
|
||||
.expect("failed to execute process")
|
||||
.stdout;
|
||||
|
||||
let files_s = String::from_utf8(files)?;
|
||||
|
||||
let files = files_s
|
||||
.lines()
|
||||
.filter(|p| !p.ends_with('/'))
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let _output = std::process::Command::new("tar")
|
||||
.arg("-xf")
|
||||
.arg(&tarball)
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
fn find_zig_binary(path: Option<PathBuf>) -> Result<PathBuf> {
|
||||
use std::env::split_paths;
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let path_var = std::env::var("PATH").unwrap_or_default();
|
||||
#[cfg(unix)]
|
||||
let system_path_var = std::process::Command::new("getconf")
|
||||
.args(&["PATH"])
|
||||
.output()
|
||||
.map(|output| output.stdout)
|
||||
.unwrap_or_default();
|
||||
let retval = if let Some(p) = path {
|
||||
if p.exists() {
|
||||
p
|
||||
} else {
|
||||
return Err(anyhow!("Could not find `zig` binary in {}.", p.display()));
|
||||
}
|
||||
} else {
|
||||
let mut retval = None;
|
||||
for mut p in split_paths(&path_var).chain(split_paths(
|
||||
#[cfg(unix)]
|
||||
{
|
||||
&OsStr::from_bytes(&system_path_var[..])
|
||||
},
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
OsStr::new("")
|
||||
},
|
||||
)) {
|
||||
p.push("zig");
|
||||
if p.exists() {
|
||||
retval = Some(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval.ok_or_else(|| anyhow!("Could not find `zig` binary in PATH."))?
|
||||
};
|
||||
|
||||
let version = std::process::Command::new(&retval)
|
||||
.arg("version")
|
||||
.output()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Could not execute `zig` binary at path `{}`",
|
||||
retval.display()
|
||||
)
|
||||
})?
|
||||
.stdout;
|
||||
let version_slice = if let Some(pos) = version
|
||||
.iter()
|
||||
.position(|c| !(c.is_ascii_digit() || (*c == b'.')))
|
||||
{
|
||||
&version[..pos]
|
||||
} else {
|
||||
&version[..]
|
||||
};
|
||||
|
||||
if version_slice < b"0.10.0".as_ref() {
|
||||
Err(anyhow!("`zig` binary in PATH (`{}`) is not a new enough version (`{}`): please use version `0.10.0` or newer.", retval.display(), String::from_utf8_lossy(version_slice)))
|
||||
} else {
|
||||
Ok(retval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,9 @@ impl CreateObj {
|
||||
.fold(CpuFeature::set(), |a, b| a | b);
|
||||
// Cranelift requires SSE2, so we have this "hack" for now to facilitate
|
||||
// usage
|
||||
if target_triple.architecture == Architecture::X86_64 {
|
||||
features |= CpuFeature::SSE2;
|
||||
}
|
||||
Target::new(target_triple.clone(), features)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -12,8 +12,8 @@ use enumset::{EnumSet, EnumSetType};
|
||||
use std::str::FromStr;
|
||||
use std::string::{String, ToString};
|
||||
pub use target_lexicon::{
|
||||
Architecture, BinaryFormat, CallingConvention, Endianness, OperatingSystem, PointerWidth,
|
||||
Triple,
|
||||
Aarch64Architecture, Architecture, BinaryFormat, CallingConvention, Endianness, Environment,
|
||||
OperatingSystem, PointerWidth, Triple, Vendor,
|
||||
};
|
||||
|
||||
/// The nomenclature is inspired by the [`cpuid` crate].
|
||||
|
||||
@@ -71,8 +71,8 @@ mod value;
|
||||
mod vmoffsets;
|
||||
|
||||
pub use crate::compilation::target::{
|
||||
Architecture, BinaryFormat, CallingConvention, CpuFeature, Endianness, OperatingSystem,
|
||||
PointerWidth, Target, Triple,
|
||||
Aarch64Architecture, Architecture, BinaryFormat, CallingConvention, CpuFeature, Endianness,
|
||||
Environment, OperatingSystem, PointerWidth, Target, Triple, Vendor,
|
||||
};
|
||||
pub use crate::serialize::{MetadataHeader, SerializableCompilation, SerializableModule};
|
||||
pub use error::{
|
||||
|
||||
Reference in New Issue
Block a user