From 6d0d40e34685dc146a12570516a453a77475ff33 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 18 Aug 2022 13:50:40 +0300 Subject: [PATCH 1/5] create-exe: fix serialized create-exe not working Unified generated objects interfaces: both static and serialized objects expose the same function signature to create the module: wasm_module_t* wasmer_object_module_new(wasm_store_t* store, const char* module_name); --- lib/cli/src/c_gen/staticlib_header.rs | 4 +- lib/cli/src/commands/create_exe.rs | 78 ++++--- lib/cli/src/commands/create_obj.rs | 12 +- lib/cli/src/commands/wasmer_create_exe_main.c | 14 +- ...eate_exe.h => wasmer_deserialize_module.h} | 2 +- .../commands/wasmer_static_create_exe_main.c | 192 ------------------ 6 files changed, 54 insertions(+), 248 deletions(-) rename lib/cli/src/commands/{wasmer_create_exe.h => wasmer_deserialize_module.h} (84%) delete mode 100644 lib/cli/src/commands/wasmer_static_create_exe_main.c diff --git a/lib/cli/src/c_gen/staticlib_header.rs b/lib/cli/src/c_gen/staticlib_header.rs index bd203c644b8..6449ef8b490 100644 --- a/lib/cli/src/c_gen/staticlib_header.rs +++ b/lib/cli/src/c_gen/staticlib_header.rs @@ -58,8 +58,8 @@ wasm_byte_vec_t generate_serialized_data() { return module_byte_vec; } -wasm_module_t* wasmer_static_module_new(wasm_store_t* store, const char* wasm_name) { - // wasm_name intentionally unused for now: will be used in the future. +wasm_module_t* wasmer_object_module_new(wasm_store_t* store, const char* module_name) { + // module_name intentionally unused for now: will be used in the future. wasm_byte_vec_t module_byte_vec = generate_serialized_data(); wasm_module_t* module = wasm_module_deserialize(store, &module_byte_vec); free(module_byte_vec.data); diff --git a/lib/cli/src/commands/create_exe.rs b/lib/cli/src/commands/create_exe.rs index 69b9abb4234..470dd96a6ad 100644 --- a/lib/cli/src/commands/create_exe.rs +++ b/lib/cli/src/commands/create_exe.rs @@ -21,8 +21,7 @@ use wasmer_object::{emit_serialized, get_object_for_target}; pub type PrefixerFn = Box String + Send>; 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"); +const WASMER_DESERIALIZE_HEADER: &str = include_str!("wasmer_deserialize_module.h"); #[derive(Debug, Clone)] struct CrossCompile { @@ -307,9 +306,39 @@ impl CreateExe { .map_err(|err| anyhow::anyhow!(err.to_string()))?; writer.flush()?; drop(writer); + // Write down header file that includes deserialize function + { + let mut writer = BufWriter::new(File::create("static_defs.h")?); + writer.write_all(WASMER_DESERIALIZE_HEADER.as_bytes())?; + writer.flush()?; + } + + // write C src to disk + let c_src_path = Path::new("wasmer_main.c"); + #[cfg(not(windows))] + let c_src_obj = PathBuf::from("wasmer_main.o"); + #[cfg(windows)] + let c_src_obj = PathBuf::from("wasmer_main.obj"); - let cli_given_triple = self.target_triple.clone(); - self.compile_c(wasm_object_path, cli_given_triple, output_path)?; + { + 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_MAIN_C_SOURCE)?; + } + run_c_compile(c_src_path, &c_src_obj, self.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(), + ..Default::default() + } + .run() + .context("Failed to link objects together")?; } #[cfg(not(feature = "static-artifact-create"))] ObjectFormat::Symbols => { @@ -379,42 +408,6 @@ impl CreateExe { Ok(()) } - fn compile_c( - &self, - wasm_object_path: PathBuf, - target_triple: Option, - output_path: PathBuf, - ) -> anyhow::Result<()> { - // write C src to disk - let c_src_path = Path::new("wasmer_main.c"); - #[cfg(not(windows))] - let c_src_obj = PathBuf::from("wasmer_main.o"); - #[cfg(windows)] - let c_src_obj = PathBuf::from("wasmer_main.obj"); - - { - 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_MAIN_C_SOURCE)?; - } - 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: target_triple, - ..Default::default() - } - .run() - .context("Failed to link objects together")?; - - Ok(()) - } - fn compile_zig( &self, output_path: PathBuf, @@ -449,7 +442,7 @@ impl CreateExe { .write(true) .open(&c_src_path) .context("Failed to open C source code file")?; - c_src_file.write_all(WASMER_STATIC_MAIN_C_SOURCE)?; + c_src_file.write_all(WASMER_MAIN_C_SOURCE)?; } if !header_code_path.is_dir() { @@ -531,7 +524,7 @@ impl CreateExe { .write(true) .open(&c_src_path) .context("Failed to open C source code file")?; - c_src_file.write_all(WASMER_STATIC_MAIN_C_SOURCE)?; + c_src_file.write_all(WASMER_MAIN_C_SOURCE)?; } if !header_code_path.is_dir() { @@ -666,6 +659,7 @@ fn run_c_compile( .arg("-O2") .arg("-c") .arg(path_to_c_src) + .arg("-I.") .arg("-I") .arg(get_wasmer_include_directory()?); diff --git a/lib/cli/src/commands/create_obj.rs b/lib/cli/src/commands/create_obj.rs index f9113e45fd7..c8e3534ba6a 100644 --- a/lib/cli/src/commands/create_obj.rs +++ b/lib/cli/src/commands/create_obj.rs @@ -15,7 +15,7 @@ use std::process::Command; use wasmer::*; use wasmer_object::{emit_serialized, get_object_for_target}; -const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_create_exe.h"); +const WASMER_SERIALIZED_HEADER: &[u8] = include_bytes!("wasmer_deserialize_module.h"); #[derive(Debug, Parser)] /// The options for the `wasmer create-exe` subcommand @@ -152,6 +152,16 @@ impl CreateObj { self.output.display(), header_output.display(), ); + eprintln!("\n---\n"); + eprintln!( + r#"To use, link the object file to your executable and call the `wasmer_object_module_new` function defined in the header file. For example, in the C language: + + #include "{}" + + wasm_module_t *module = wasmer_object_module_new(store, "my_module_name"); + "#, + header_output.display(), + ); Ok(()) } diff --git a/lib/cli/src/commands/wasmer_create_exe_main.c b/lib/cli/src/commands/wasmer_create_exe_main.c index a58fb5c73c2..3fe98f62c45 100644 --- a/lib/cli/src/commands/wasmer_create_exe_main.c +++ b/lib/cli/src/commands/wasmer_create_exe_main.c @@ -1,7 +1,5 @@ - #include "wasmer.h" -//#include "my_wasm.h" - +#include "static_defs.h" #include #include #include @@ -11,8 +9,8 @@ // TODO: make this define templated so that the Rust code can toggle it on/off #define WASI -extern size_t WASMER_MODULE_LENGTH asm("WASMER_MODULE_LENGTH"); -extern char WASMER_MODULE_DATA asm("WASMER_MODULE_DATA"); +extern wasm_module_t* wasmer_object_module_new(wasm_store_t* store, const char* module_name) asm("wasmer_object_module_new"); + static void print_wasmer_error() { int error_len = wasmer_last_error_length(); @@ -95,11 +93,7 @@ int main(int argc, char *argv[]) { wasm_engine_t *engine = wasm_engine_new_with_config(config); wasm_store_t *store = wasm_store_new(engine); - wasm_byte_vec_t module_byte_vec = { - .size = WASMER_MODULE_LENGTH, - .data = (const char*)&WASMER_MODULE_DATA, - }; - wasm_module_t *module = wasm_module_deserialize(store, &module_byte_vec); + wasm_module_t *module = wasmer_object_module_new(store, "module"); if (!module) { fprintf(stderr, "Failed to create module\n"); diff --git a/lib/cli/src/commands/wasmer_create_exe.h b/lib/cli/src/commands/wasmer_deserialize_module.h similarity index 84% rename from lib/cli/src/commands/wasmer_create_exe.h rename to lib/cli/src/commands/wasmer_deserialize_module.h index 98705d4fc6f..f0ef229aa43 100644 --- a/lib/cli/src/commands/wasmer_create_exe.h +++ b/lib/cli/src/commands/wasmer_deserialize_module.h @@ -10,7 +10,7 @@ extern "C" { extern size_t WASMER_MODULE_LENGTH asm("WASMER_MODULE_LENGTH"); extern char WASMER_MODULE_DATA asm("WASMER_MODULE_DATA"); -wasm_module_t* wasmer_module_new(wasm_store_t* store, const char* wasm_name) { +wasm_module_t* wasmer_object_module_new(wasm_store_t* store, const char* module_name) { wasm_byte_vec_t module_byte_vec = { .size = WASMER_MODULE_LENGTH, .data = (const char*)&WASMER_MODULE_DATA, diff --git a/lib/cli/src/commands/wasmer_static_create_exe_main.c b/lib/cli/src/commands/wasmer_static_create_exe_main.c deleted file mode 100644 index 2276ed3bc75..00000000000 --- a/lib/cli/src/commands/wasmer_static_create_exe_main.c +++ /dev/null @@ -1,192 +0,0 @@ -#include "wasmer.h" -#include "static_defs.h" -#include -#include -#include - -#define own - -// TODO: make this define templated so that the Rust code can toggle it on/off -#define WASI - -extern wasm_module_t* wasmer_module_new(wasm_store_t* store) asm("wasmer_module_new"); -extern wasm_module_t* wasmer_static_module_new(wasm_store_t* store,const char* wasm_name) asm("wasmer_static_module_new"); - - -static void print_wasmer_error() { - int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char *error_str = (char *)malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("%s\n", error_str); - free(error_str); -} - -#ifdef WASI -static void pass_mapdir_arg(wasi_config_t *wasi_config, char *mapdir) { - int colon_location = strchr(mapdir, ':') - mapdir; - if (colon_location == 0) { - // error malformed argument - fprintf(stderr, "Expected mapdir argument of the form alias:directory\n"); - exit(-1); - } - - char *alias = (char *)malloc(colon_location + 1); - memcpy(alias, mapdir, colon_location); - alias[colon_location] = '\0'; - - int dir_len = strlen(mapdir) - colon_location; - char *dir = (char *)malloc(dir_len + 1); - memcpy(dir, &mapdir[colon_location + 1], dir_len); - dir[dir_len] = '\0'; - - wasi_config_mapdir(wasi_config, alias, dir); - free(alias); - free(dir); -} - -// We try to parse out `--dir` and `--mapdir` ahead of time and process those -// specially. All other arguments are passed to the guest program. -static void handle_arguments(wasi_config_t *wasi_config, int argc, - char *argv[]) { - for (int i = 1; i < argc; ++i) { - // We probably want special args like `--dir` and `--mapdir` to not be - // passed directly - if (strcmp(argv[i], "--dir") == 0) { - // next arg is a preopen directory - if ((i + 1) < argc) { - i++; - wasi_config_preopen_dir(wasi_config, argv[i]); - } else { - fprintf(stderr, "--dir expects a following argument specifying which " - "directory to preopen\n"); - exit(-1); - } - } else if (strcmp(argv[i], "--mapdir") == 0) { - // next arg is a mapdir - if ((i + 1) < argc) { - i++; - pass_mapdir_arg(wasi_config, argv[i]); - } else { - fprintf(stderr, - "--mapdir expects a following argument specifying which " - "directory to preopen in the form alias:directory\n"); - exit(-1); - } - } else if (strncmp(argv[i], "--dir=", strlen("--dir=")) == 0) { - // this arg is a preopen dir - char *dir = argv[i] + strlen("--dir="); - wasi_config_preopen_dir(wasi_config, dir); - } else if (strncmp(argv[i], "--mapdir=", strlen("--mapdir=")) == 0) { - // this arg is a mapdir - char *mapdir = argv[i] + strlen("--mapdir="); - pass_mapdir_arg(wasi_config, mapdir); - } else { - // guest argument - wasi_config_arg(wasi_config, argv[i]); - } - } -} -#endif - -int main(int argc, char *argv[]) { - wasm_config_t *config = wasm_config_new(); - wasm_engine_t *engine = wasm_engine_new_with_config(config); - wasm_store_t *store = wasm_store_new(engine); - - wasm_module_t *module = wasmer_static_module_new(store, "module"); - - if (!module) { - fprintf(stderr, "Failed to create module\n"); - print_wasmer_error(); - return -1; - } - - // We have now finished the memory buffer book keeping and we have a valid - // Module. - -#ifdef WASI - wasi_config_t *wasi_config = wasi_config_new(argv[0]); - handle_arguments(wasi_config, argc, argv); - - wasi_env_t *wasi_env = wasi_env_new(store, wasi_config); - if (!wasi_env) { - fprintf(stderr, "Error building WASI env!\n"); - print_wasmer_error(); - return 1; - } -#endif - - wasm_importtype_vec_t import_types; - wasm_module_imports(module, &import_types); - - wasm_extern_vec_t imports; - wasm_extern_vec_new_uninitialized(&imports, import_types.size); - wasm_importtype_vec_delete(&import_types); - -#ifdef WASI - bool get_imports_result = wasi_get_imports(store, wasi_env, module, &imports); - - if (!get_imports_result) { - fprintf(stderr, "Error getting WASI imports!\n"); - print_wasmer_error(); - - return 1; - } -#endif - - wasm_instance_t *instance = wasm_instance_new(store, module, &imports, NULL); - - if (!instance) { - fprintf(stderr, "Failed to create instance\n"); - print_wasmer_error(); - return -1; - } - -#ifdef WASI - // Read the exports. - wasm_extern_vec_t exports; - wasm_instance_exports(instance, &exports); - wasm_memory_t* mem = NULL; - for (size_t i = 0; i < exports.size; i++) { - mem = wasm_extern_as_memory(exports.data[i]); - if (mem) { - break; - } - } - - if (!mem) { - fprintf(stderr, "Failed to create instance: Could not find memory in exports\n"); - print_wasmer_error(); - return -1; - } - wasi_env_set_memory(wasi_env, mem); - - own wasm_func_t *start_function = wasi_get_start_function(instance); - if (!start_function) { - fprintf(stderr, "`_start` function not found\n"); - print_wasmer_error(); - return -1; - } - - wasm_val_vec_t args = WASM_EMPTY_VEC; - wasm_val_vec_t results = WASM_EMPTY_VEC; - own wasm_trap_t *trap = wasm_func_call(start_function, &args, &results); - if (trap) { - fprintf(stderr, "Trap is not NULL: TODO:\n"); - return -1; - } -#endif - - // TODO: handle non-WASI start (maybe with invoke?) - -#ifdef WASI - wasi_env_delete(wasi_env); - wasm_extern_vec_delete(&exports); -#endif - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); - wasm_engine_delete(engine); - return 0; -} From 51ad557fb442a3190b4e2a1826d360d9b10879bd Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 18 Aug 2022 14:28:27 +0300 Subject: [PATCH 2/5] tests/integration/cli: fix tests failing --- Makefile | 2 +- tests/integration/cli/build.rs | 2 +- tests/integration/cli/src/assets.rs | 68 +++++++++----------------- tests/integration/cli/tests/run.rs | 12 ++--- tests/integration/cli/tests/version.rs | 12 +++-- 5 files changed, 37 insertions(+), 59 deletions(-) diff --git a/Makefile b/Makefile index a6be323b4d9..67bff7ec69f 100644 --- a/Makefile +++ b/Makefile @@ -535,7 +535,7 @@ test-examples: $(CARGO_BINARY) test $(CARGO_TARGET) --release $(compiler_features) --features wasi --examples test-integration: - $(CARGO_BINARY) test $(CARGO_TARGET) --no-fail-fast -p wasmer-integration-tests-cli + $(CARGO_BINARY) test $(CARGO_TARGET) --no-fail-fast -p wasmer-integration-tests-cli -- --nocapture test-integration-ios: $(CARGO_BINARY) test $(CARGO_TARGET) -p wasmer-integration-tests-ios diff --git a/tests/integration/cli/build.rs b/tests/integration/cli/build.rs index 81caa36d691..9057ee24f22 100644 --- a/tests/integration/cli/build.rs +++ b/tests/integration/cli/build.rs @@ -1,6 +1,6 @@ fn main() { println!( - "cargo:rustc-env=TARGET={}", + "cargo:rustc-env=CARGO_BUILD_TARGET={}", std::env::var("TARGET").unwrap() ); } diff --git a/tests/integration/cli/src/assets.rs b/tests/integration/cli/src/assets.rs index c3700707d1f..6f7b9a24b14 100644 --- a/tests/integration/cli/src/assets.rs +++ b/tests/integration/cli/src/assets.rs @@ -3,74 +3,49 @@ use std::path::PathBuf; pub const C_ASSET_PATH: &str = concat!( env!("CARGO_MANIFEST_DIR"), - "/../../../lib/c-api/examples/assets" + "/../../../lib/c-api/examples/assets/" ); -pub const ASSET_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../tests/examples"); +pub const ASSET_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../tests/examples/"); -pub const WASMER_INCLUDE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../lib/c-api"); +pub const WASMER_INCLUDE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../lib/c-api/"); #[cfg(feature = "debug")] -pub const WASMER_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../target/debug/wasmer"); - -#[cfg(not(feature = "debug"))] -pub const WASMER_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target/release/wasmer" -); - +pub const WASMER_TARGET_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../target/debug/"); #[cfg(feature = "debug")] -pub const WASMER_TARGET_PATH: &str = concat!( +pub const WASMER_TARGET_PATH_2: &str = concat!( env!("CARGO_MANIFEST_DIR"), - "/../../../", - env!("CARGO_CFG_TARGET"), - "/debug/wasmer" + "/../../../target/", + env!("CARGO_BUILD_TARGET"), + "/debug/" ); /* env var TARGET is set by tests/integration/cli/build.rs on compile-time */ #[cfg(not(feature = "debug"))] -pub const WASMER_TARGET_PATH: &str = concat!( +pub const WASMER_TARGET_PATH: &str = + concat!(env!("CARGO_MANIFEST_DIR"), "/../../../target/release/"); +#[cfg(not(feature = "debug"))] +pub const WASMER_TARGET_PATH2: &str = concat!( env!("CARGO_MANIFEST_DIR"), "/../../../target/", - env!("TARGET"), - "/release/wasmer" + env!("CARGO_BUILD_TARGET"), + "/release/" ); #[cfg(not(windows))] -pub const LIBWASMER_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target/release/libwasmer.a" -); +pub const LIBWASMER_FILENAME: &str = "libwasmer.a"; #[cfg(windows)] -pub const LIBWASMER_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target/release/wasmer.lib" -); - -#[cfg(not(windows))] -pub const LIBWASMER_TARGET_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../target", - env!("TARGET"), - "/release/libwasmer.a" -); - -#[cfg(windows)] -pub const LIBWASMER_TARGET_PATH: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../../", - env!("TARGET"), - "/release/wasmer.lib" -); +pub const LIBWASMER_PATH: &str = "wasmer.lib"; /// Get the path to the `libwasmer.a` static library. pub fn get_libwasmer_path() -> PathBuf { let mut ret = PathBuf::from( - env::var("WASMER_TEST_LIBWASMER_PATH").unwrap_or_else(|_| LIBWASMER_PATH.to_string()), + env::var("WASMER_TEST_LIBWASMER_PATH") + .unwrap_or_else(|_| format!("{}{}", WASMER_TARGET_PATH, LIBWASMER_FILENAME)), ); if !ret.exists() { - ret = PathBuf::from(LIBWASMER_TARGET_PATH.to_string()); + ret = PathBuf::from(format!("{}{}", WASMER_TARGET_PATH2, LIBWASMER_FILENAME)); } if !ret.exists() { panic!("Could not find libwasmer path! {:?}", ret); @@ -81,10 +56,11 @@ pub fn get_libwasmer_path() -> PathBuf { /// Get the path to the `wasmer` executable to be used in this test. pub fn get_wasmer_path() -> PathBuf { let mut ret = PathBuf::from( - env::var("WASMER_TEST_WASMER_PATH").unwrap_or_else(|_| WASMER_PATH.to_string()), + env::var("WASMER_TEST_WASMER_PATH") + .unwrap_or_else(|_| format!("{}wasmer", WASMER_TARGET_PATH)), ); if !ret.exists() { - ret = PathBuf::from(WASMER_TARGET_PATH.to_string()); + ret = PathBuf::from(format!("{}wasmer", WASMER_TARGET_PATH2)); } if !ret.exists() { panic!("Could not find wasmer executable path! {:?}", ret); diff --git a/tests/integration/cli/tests/run.rs b/tests/integration/cli/tests/run.rs index 45e1901f06b..bb97b67ca04 100644 --- a/tests/integration/cli/tests/run.rs +++ b/tests/integration/cli/tests/run.rs @@ -2,7 +2,7 @@ use anyhow::bail; use std::process::Command; -use wasmer_integration_tests_cli::{ASSET_PATH, C_ASSET_PATH, WASMER_PATH}; +use wasmer_integration_tests_cli::{get_wasmer_path, ASSET_PATH, C_ASSET_PATH}; fn wasi_test_wasm_path() -> String { format!("{}/{}", C_ASSET_PATH, "qjs.wasm") @@ -18,7 +18,7 @@ fn test_no_start_wat_path() -> String { #[test] fn run_wasi_works() -> anyhow::Result<()> { - let output = Command::new(WASMER_PATH) + let output = Command::new(get_wasmer_path()) .arg("run") .arg(wasi_test_wasm_path()) .arg("--") @@ -44,7 +44,7 @@ fn run_wasi_works() -> anyhow::Result<()> { #[test] fn run_no_imports_wasm_works() -> anyhow::Result<()> { - let output = Command::new(WASMER_PATH) + let output = Command::new(get_wasmer_path()) .arg("run") .arg(test_no_imports_wat_path()) .output()?; @@ -82,7 +82,7 @@ fn run_invoke_works_with_nomain_wasi() -> anyhow::Result<()> { let random = rand::random::(); let module_file = std::env::temp_dir().join(&format!("{random}.wat")); std::fs::write(&module_file, wasi_wat.as_bytes()).unwrap(); - let output = Command::new(WASMER_PATH) + let output = Command::new(get_wasmer_path()) .arg("run") .arg(&module_file) .output()?; @@ -94,7 +94,7 @@ fn run_invoke_works_with_nomain_wasi() -> anyhow::Result<()> { panic!(); } - let output = Command::new(WASMER_PATH) + let output = Command::new(get_wasmer_path()) .arg("run") .arg("--invoke") .arg("_start") @@ -114,7 +114,7 @@ fn run_invoke_works_with_nomain_wasi() -> anyhow::Result<()> { #[test] fn run_no_start_wasm_report_error() -> anyhow::Result<()> { - let output = Command::new(WASMER_PATH) + let output = Command::new(get_wasmer_path()) .arg("run") .arg(test_no_start_wat_path()) .output()?; diff --git a/tests/integration/cli/tests/version.rs b/tests/integration/cli/tests/version.rs index 543237c23f1..f304749d732 100644 --- a/tests/integration/cli/tests/version.rs +++ b/tests/integration/cli/tests/version.rs @@ -1,16 +1,17 @@ use anyhow::bail; use std::process::Command; -use wasmer_integration_tests_cli::WASMER_PATH; +use wasmer_integration_tests_cli::get_wasmer_path; const WASMER_VERSION: &str = env!("CARGO_PKG_VERSION"); #[test] fn version_string_is_correct() -> anyhow::Result<()> { let expected_version_output = format!("wasmer {}\n", WASMER_VERSION); + let wasmer_path = get_wasmer_path(); let outputs = [ - Command::new(WASMER_PATH).arg("--version").output()?, - Command::new(WASMER_PATH).arg("-V").output()?, + Command::new(&wasmer_path).arg("--version").output()?, + Command::new(&wasmer_path).arg("-V").output()?, ]; for output in &outputs { @@ -34,10 +35,11 @@ fn version_string_is_correct() -> anyhow::Result<()> { #[test] fn help_text_contains_version() -> anyhow::Result<()> { let expected_version_output = format!("wasmer {}", WASMER_VERSION); + let wasmer_path = get_wasmer_path(); let outputs = [ - Command::new(WASMER_PATH).arg("--help").output()?, - Command::new(WASMER_PATH).arg("-h").output()?, + Command::new(&wasmer_path).arg("--help").output()?, + Command::new(&wasmer_path).arg("-h").output()?, ]; for output in &outputs { From e859559c2c7d154b47569ea3342f62b3c054f66c Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 18 Aug 2022 14:29:28 +0300 Subject: [PATCH 3/5] Add CLI integration tests in CI test workflow --- .github/workflows/test-sys.yaml | 18 ++++++++++++++++++ Makefile | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-sys.yaml b/.github/workflows/test-sys.yaml index 1a1267f4a57..ae708933570 100644 --- a/.github/workflows/test-sys.yaml +++ b/.github/workflows/test-sys.yaml @@ -199,6 +199,14 @@ jobs: '${{ runner.tool_cache }}/cargo-sccache/bin/sccache' -s echo 'RUSTC_WRAPPER=${{ runner.tool_cache }}/cargo-sccache/bin/sccache' >> $GITHUB_ENV shell: bash + - name: Test + if: matrix.run_test && matrix.os != 'windows-2019' + run: | + make + env: + TARGET: ${{ matrix.target }} + TARGET_DIR: target/${{ matrix.target }}/release + CARGO_TARGET: --target ${{ matrix.target }} - name: Test if: matrix.run_test && matrix.os != 'windows-2019' run: | @@ -215,6 +223,16 @@ jobs: TARGET: ${{ matrix.target }} TARGET_DIR: target/${{ matrix.target }}/release CARGO_TARGET: --target ${{ matrix.target }} + - name: Test integration CLI + if: matrix.run_test && matrix.os != 'windows-2019' + run: | + make && make build-capi && make package-capi && make package + export WASMER_DIR=`pwd`/package + make test-integration-cli + env: + TARGET: ${{ matrix.target }} + TARGET_DIR: target/${{ matrix.target }}/release + CARGO_TARGET: --target ${{ matrix.target }} - name: Test if: matrix.run_test && matrix.os == 'windows-2019' run: | diff --git a/Makefile b/Makefile index 67bff7ec69f..810677016ac 100644 --- a/Makefile +++ b/Makefile @@ -534,7 +534,7 @@ test-examples: $(CARGO_BINARY) test $(CARGO_TARGET) $(compiler_features) --features wasi --examples $(CARGO_BINARY) test $(CARGO_TARGET) --release $(compiler_features) --features wasi --examples -test-integration: +test-integration-cli: $(CARGO_BINARY) test $(CARGO_TARGET) --no-fail-fast -p wasmer-integration-tests-cli -- --nocapture test-integration-ios: From 44c6c98295e76a8b377b1da1a72dcf9e1adf3f62 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 18 Aug 2022 14:54:42 +0300 Subject: [PATCH 4/5] tests/integration/cli: add tests for create-{exe,obj} & --object-format flag Test that serialized and symbol formats work both in create-exe and create-obj. --- tests/integration/cli/tests/create_exe.rs | 173 +++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index abb213c58b6..67a75cb117a 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -26,6 +26,8 @@ struct WasmerCreateExe { native_executable_path: PathBuf, /// Compiler with which to compile the Wasm. compiler: Compiler, + /// Extra CLI flags + extra_cli_flags: Vec<&'static str>, } impl Default for WasmerCreateExe { @@ -40,17 +42,19 @@ impl Default for WasmerCreateExe { wasm_path: PathBuf::from(create_exe_test_wasm_path()), native_executable_path, compiler: Compiler::Cranelift, + extra_cli_flags: vec![], } } } impl WasmerCreateExe { - fn run(&self) -> anyhow::Result<()> { + fn run(&self) -> anyhow::Result> { let output = Command::new(&self.wasmer_path) .current_dir(&self.current_dir) .arg("create-exe") .arg(&self.wasm_path.canonicalize()?) .arg(&self.compiler.to_flag()) + .args(self.extra_cli_flags.iter()) .arg("-o") .arg(&self.native_executable_path) .output()?; @@ -64,7 +68,66 @@ impl WasmerCreateExe { .expect("stderr is not utf8! need to handle arbitrary bytes") ); } - Ok(()) + Ok(output.stdout) + } +} + +/// Data used to run the `wasmer compile` command. +#[derive(Debug)] +struct WasmerCreateObj { + /// The directory to operate in. + current_dir: PathBuf, + /// Path to wasmer executable used to run the command. + wasmer_path: PathBuf, + /// Path to the Wasm file to compile. + wasm_path: PathBuf, + /// Path to the object file produced by compiling the Wasm. + output_object_path: PathBuf, + /// Compiler with which to compile the Wasm. + compiler: Compiler, + /// Extra CLI flags + extra_cli_flags: Vec<&'static str>, +} + +impl Default for WasmerCreateObj { + fn default() -> Self { + #[cfg(not(windows))] + let output_object_path = PathBuf::from("wasm.o"); + #[cfg(windows)] + let output_object_path = PathBuf::from("wasm.obj"); + Self { + current_dir: std::env::current_dir().unwrap(), + wasmer_path: get_wasmer_path(), + wasm_path: PathBuf::from(create_exe_test_wasm_path()), + output_object_path, + compiler: Compiler::Cranelift, + extra_cli_flags: vec![], + } + } +} + +impl WasmerCreateObj { + fn run(&self) -> anyhow::Result> { + let output = Command::new(&self.wasmer_path) + .current_dir(&self.current_dir) + .arg("create-obj") + .arg(&self.wasm_path.canonicalize()?) + .arg(&self.compiler.to_flag()) + .args(self.extra_cli_flags.iter()) + .arg("-o") + .arg(&self.output_object_path) + .output()?; + + if !output.status.success() { + bail!( + "wasmer create-obj failed with: stdout: {}\n\nstderr: {}", + std::str::from_utf8(&output.stdout) + .expect("stdout is not utf8! need to handle arbitrary bytes"), + std::str::from_utf8(&output.stderr) + .expect("stderr is not utf8! need to handle arbitrary bytes") + ); + } + Ok(output.stdout) } } @@ -160,3 +223,109 @@ fn create_exe_works_with_file() -> anyhow::Result<()> { Ok(()) } + +#[test] +fn create_exe_serialized_works() -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let operating_dir: PathBuf = temp_dir.path().to_owned(); + + let wasm_path = operating_dir.join(create_exe_test_wasm_path()); + #[cfg(not(windows))] + let executable_path = operating_dir.join("wasm.out"); + #[cfg(windows)] + let executable_path = operating_dir.join("wasm.exe"); + + let output: Vec = WasmerCreateExe { + current_dir: operating_dir.clone(), + wasm_path, + native_executable_path: executable_path.clone(), + compiler: Compiler::Cranelift, + extra_cli_flags: vec!["--object-format", "serialized"], + ..Default::default() + } + .run() + .context("Failed to create-exe wasm with Wasmer")?; + + let result = run_code( + &operating_dir, + &executable_path, + &["--eval".to_string(), "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));".to_string()], + ) + .context("Failed to run generated executable")?; + let result_lines = result.lines().collect::>(); + assert_eq!(result_lines, vec!["\"Hello, World\""],); + + let output_str = String::from_utf8_lossy(&output); + assert!( + output_str.contains("erialized"), + "create-exe output doesn't mention `serialized` format keyword:\n{}", + output_str + ); + + Ok(()) +} + +fn create_obj(args: Vec<&'static str>, keyword_needle: &str, keyword: &str) -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let operating_dir: PathBuf = temp_dir.path().to_owned(); + + let wasm_path = operating_dir.join(create_exe_test_wasm_path()); + + #[cfg(not(windows))] + let object_path = operating_dir.join("wasm.o"); + #[cfg(windows)] + let object_path = operating_dir.join("wasm.obj"); + + let output: Vec = WasmerCreateObj { + current_dir: operating_dir.clone(), + wasm_path, + output_object_path: object_path.clone(), + compiler: Compiler::Cranelift, + extra_cli_flags: args, + ..Default::default() + } + .run() + .context("Failed to create-obj wasm with Wasmer")?; + + assert!( + object_path.exists(), + "create-obj successfully completed but object output file `{}` missing", + object_path.display() + ); + let mut object_header_path = object_path.clone(); + object_header_path.set_extension("h"); + assert!( + object_header_path.exists(), + "create-obj successfully completed but object output header file `{}` missing", + object_header_path.display() + ); + + let output_str = String::from_utf8_lossy(&output); + assert!( + output_str.contains(keyword_needle), + "create-obj output doesn't mention `{}` format keyword:\n{}", + keyword, + output_str + ); + + Ok(()) +} + +#[test] +fn create_obj_default() -> anyhow::Result<()> { + create_obj(vec![], "ymbols", "symbols") +} + +#[test] +fn create_obj_symbols() -> anyhow::Result<()> { + create_obj(vec!["--object-format", "symbols"], "ymbols", "symbols") +} + +#[test] +fn create_obj_serialized() -> anyhow::Result<()> { + create_obj( + vec!["--object-format", "serialized"], + "erialized", + "serialized", + ) +} From 2f2b895aecba046113059a8f0f1ca339767cb066 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 18 Aug 2022 14:57:51 +0300 Subject: [PATCH 5/5] tests/integration/cli: test that giving object as input to create-exe works --- tests/integration/cli/tests/create_exe.rs | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/tests/integration/cli/tests/create_exe.rs b/tests/integration/cli/tests/create_exe.rs index 67a75cb117a..65b4433d4ef 100644 --- a/tests/integration/cli/tests/create_exe.rs +++ b/tests/integration/cli/tests/create_exe.rs @@ -329,3 +329,81 @@ fn create_obj_serialized() -> anyhow::Result<()> { "serialized", ) } + +fn create_exe_with_object_input(args: Vec<&'static str>) -> anyhow::Result<()> { + let temp_dir = tempfile::tempdir()?; + let operating_dir: PathBuf = temp_dir.path().to_owned(); + + let wasm_path = operating_dir.join(create_exe_test_wasm_path()); + + #[cfg(not(windows))] + let object_path = operating_dir.join("wasm.o"); + #[cfg(windows)] + let object_path = operating_dir.join("wasm.obj"); + + WasmerCreateObj { + current_dir: operating_dir.clone(), + wasm_path, + output_object_path: object_path.clone(), + compiler: Compiler::Cranelift, + extra_cli_flags: args, + ..Default::default() + } + .run() + .context("Failed to create-obj wasm with Wasmer")?; + + assert!( + object_path.exists(), + "create-obj successfully completed but object output file `{}` missing", + object_path.display() + ); + let mut object_header_path = object_path.clone(); + object_header_path.set_extension("h"); + assert!( + object_header_path.exists(), + "create-obj successfully completed but object output header file `{}` missing", + object_header_path.display() + ); + + #[cfg(not(windows))] + let executable_path = operating_dir.join("wasm.out"); + #[cfg(windows)] + let executable_path = operating_dir.join("wasm.exe"); + + WasmerCreateExe { + current_dir: operating_dir.clone(), + wasm_path: object_path, + native_executable_path: executable_path.clone(), + compiler: Compiler::Cranelift, + extra_cli_flags: vec!["--header", "wasm.h"], + ..Default::default() + } + .run() + .context("Failed to create-exe wasm with Wasmer")?; + + let result = run_code( + &operating_dir, + &executable_path, + &["--eval".to_string(), "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));".to_string()], + ) + .context("Failed to run generated executable")?; + let result_lines = result.lines().collect::>(); + assert_eq!(result_lines, vec!["\"Hello, World\""],); + + Ok(()) +} + +#[test] +fn create_exe_with_object_input_default() -> anyhow::Result<()> { + create_exe_with_object_input(vec![]) +} + +#[test] +fn create_exe_with_object_input_symbols() -> anyhow::Result<()> { + create_exe_with_object_input(vec!["--object-format", "symbols"]) +} + +#[test] +fn create_exe_with_object_input_serialized() -> anyhow::Result<()> { + create_exe_with_object_input(vec!["--object-format", "serialized"]) +}