mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 06:08:29 +00:00
Add --dir and --mapdir support to native executable
This commit is contained in:
@@ -357,6 +357,8 @@ fn exclude_items_from_wasm_c_api(builder: Builder) -> Builder {
|
|||||||
builder
|
builder
|
||||||
.exclude_item("wasi_config_arg")
|
.exclude_item("wasi_config_arg")
|
||||||
.exclude_item("wasi_config_env")
|
.exclude_item("wasi_config_env")
|
||||||
|
.exclude_item("wasi_config_mapdir")
|
||||||
|
.exclude_item("wasi_config_preopen_dir")
|
||||||
.exclude_item("wasi_config_inherit_stderr")
|
.exclude_item("wasi_config_inherit_stderr")
|
||||||
.exclude_item("wasi_config_inherit_stdin")
|
.exclude_item("wasi_config_inherit_stdin")
|
||||||
.exclude_item("wasi_config_inherit_stdout")
|
.exclude_item("wasi_config_inherit_stdout")
|
||||||
|
|||||||
@@ -75,6 +75,63 @@ pub unsafe extern "C" fn wasi_config_arg(config: &mut wasi_config_t, arg: *const
|
|||||||
config.state_builder.arg(arg_bytes);
|
config.state_builder.arg(arg_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasi_config_preopen_dir(
|
||||||
|
config: &mut wasi_config_t,
|
||||||
|
dir: *const c_char,
|
||||||
|
) -> bool {
|
||||||
|
let dir_cstr = CStr::from_ptr(dir);
|
||||||
|
let dir_bytes = dir_cstr.to_bytes();
|
||||||
|
let dir_str = match std::str::from_utf8(dir_bytes) {
|
||||||
|
Ok(dir_str) => dir_str,
|
||||||
|
Err(e) => {
|
||||||
|
update_last_error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = config.state_builder.preopen_dir(dir_str) {
|
||||||
|
update_last_error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasi_config_mapdir(
|
||||||
|
config: &mut wasi_config_t,
|
||||||
|
alias: *const c_char,
|
||||||
|
dir: *const c_char,
|
||||||
|
) -> bool {
|
||||||
|
let alias_cstr = CStr::from_ptr(alias);
|
||||||
|
let alias_bytes = alias_cstr.to_bytes();
|
||||||
|
let alias_str = match std::str::from_utf8(alias_bytes) {
|
||||||
|
Ok(alias_str) => alias_str,
|
||||||
|
Err(e) => {
|
||||||
|
update_last_error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let dir_cstr = CStr::from_ptr(dir);
|
||||||
|
let dir_bytes = dir_cstr.to_bytes();
|
||||||
|
let dir_str = match std::str::from_utf8(dir_bytes) {
|
||||||
|
Ok(dir_str) => dir_str,
|
||||||
|
Err(e) => {
|
||||||
|
update_last_error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = config.state_builder.map_dir(alias_str, dir_str) {
|
||||||
|
update_last_error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) {
|
pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) {
|
||||||
config.inherit_stdout = true;
|
config.inherit_stdout = true;
|
||||||
|
|||||||
@@ -101,10 +101,18 @@ void wasi_config_inherit_stdin(wasi_config_t *config);
|
|||||||
void wasi_config_inherit_stdout(wasi_config_t *config);
|
void wasi_config_inherit_stdout(wasi_config_t *config);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(WASMER_WASI_ENABLED)
|
||||||
|
bool wasi_config_mapdir(wasi_config_t *config, const char *alias, const char *dir);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(WASMER_WASI_ENABLED)
|
#if defined(WASMER_WASI_ENABLED)
|
||||||
wasi_config_t *wasi_config_new(const char *program_name);
|
wasi_config_t *wasi_config_new(const char *program_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(WASMER_WASI_ENABLED)
|
||||||
|
bool wasi_config_preopen_dir(wasi_config_t *config, const char *dir);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(WASMER_WASI_ENABLED)
|
#if defined(WASMER_WASI_ENABLED)
|
||||||
void wasi_env_delete(wasi_env_t *_state);
|
void wasi_env_delete(wasi_env_t *_state);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,81 +9,166 @@ extern "C" {
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// TODO: make this define templated so that the Rust code can toggle it on/off
|
||||||
|
#define WASI
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void print_wasmer_error()
|
void print_wasmer_error()
|
||||||
{
|
{
|
||||||
int error_len = wasmer_last_error_length();
|
int error_len = wasmer_last_error_length();
|
||||||
printf("Error len: `%d`\n", error_len);
|
printf("Error len: `%d`\n", error_len);
|
||||||
char* error_str = (char*) malloc(error_len);
|
char* error_str = (char*) malloc(error_len);
|
||||||
wasmer_last_error_message(error_str, error_len);
|
wasmer_last_error_message(error_str, error_len);
|
||||||
printf("Error str: `%s`\n", error_str);
|
printf("%s\n", error_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
#ifdef WASI
|
||||||
printf("Initializing...\n");
|
int find_colon(char* string) {
|
||||||
wasm_config_t* config = wasm_config_new();
|
int colon_location = 0;
|
||||||
wasm_config_set_engine(config, OBJECT_FILE);
|
for (int j = 0; j < strlen(string); ++j) {
|
||||||
wasm_engine_t* engine = wasm_engine_new_with_config(config);
|
if (string[j] == ':') {
|
||||||
wasm_store_t* store = wasm_store_new(engine);
|
colon_location = j;
|
||||||
|
break;
|
||||||
wasm_module_t* module = wasmer_object_file_engine_new(store, "qjs.wasm");
|
}
|
||||||
if (! module) {
|
}
|
||||||
printf("Failed to create module\n");
|
return colon_location;
|
||||||
print_wasmer_error();
|
}
|
||||||
return -1;
|
|
||||||
}
|
void pass_mapdir_arg(wasi_config_t* wasi_config, char* mapdir) {
|
||||||
|
int colon_location = find_colon(mapdir);
|
||||||
// We have now finished the memory buffer book keeping and we have a valid Module.
|
if (colon_location == 0) {
|
||||||
|
// error malformed argument
|
||||||
// In this example we're passing some JavaScript source code as a command line argument
|
fprintf(stderr, "Expected mapdir argument of the form alias:directory\n");
|
||||||
// to a WASI module that can evaluate JavaScript.
|
exit(-1);
|
||||||
wasi_config_t* wasi_config = wasi_config_new("constant_value_here");
|
}
|
||||||
const char* js_string = "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));";
|
// TODO: triple check for off by one errors
|
||||||
wasi_config_arg(wasi_config, "--eval");
|
int dir_len = strlen(mapdir) - colon_location;
|
||||||
wasi_config_arg(wasi_config, js_string);
|
char* alias = (char*)malloc(colon_location);
|
||||||
wasi_env_t* wasi_env = wasi_env_new(wasi_config);
|
char* dir = (char*)malloc(dir_len);
|
||||||
if (!wasi_env) {
|
int j = 0;
|
||||||
printf("> Error building WASI env!\n");
|
for (j = 0; j < colon_location; ++j) {
|
||||||
print_wasmer_error();
|
alias[j] = mapdir[j];
|
||||||
return 1;
|
}
|
||||||
}
|
alias[j] = 0;
|
||||||
|
for (j = 0; j < dir_len; ++j) {
|
||||||
wasm_importtype_vec_t import_types;
|
dir[j] = mapdir[j + colon_location];
|
||||||
wasm_module_imports(module, &import_types);
|
}
|
||||||
int num_imports = import_types.size;
|
dir[j] = 0;
|
||||||
wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*));
|
|
||||||
wasm_importtype_vec_delete(&import_types);
|
wasi_config_mapdir(wasi_config, alias, dir);
|
||||||
|
free(alias);
|
||||||
bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports);
|
free(dir);
|
||||||
if (!get_imports_result) {
|
}
|
||||||
printf("> Error getting WASI imports!\n");
|
|
||||||
print_wasmer_error();
|
// We try to parse out `--dir` and `--mapdir` ahead of time and process those
|
||||||
return 1;
|
// specially. All other arguments are passed to the guest program.
|
||||||
}
|
void handle_arguments(wasi_config_t* wasi_config, int argc, char* argv[]) {
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
wasm_instance_t* instance = wasm_instance_new(store, module, (const wasm_extern_t* const*) imports, NULL);
|
// We probably want special args like `--dir` and `--mapdir` to not be passed directly
|
||||||
if (! instance) {
|
if (strcmp(argv[i], "--dir") == 0) {
|
||||||
printf("Failed to create instance\n");
|
// next arg is a preopen directory
|
||||||
print_wasmer_error();
|
if ((i + 1) < argc ) {
|
||||||
return -1;
|
i++;
|
||||||
}
|
wasi_config_preopen_dir(wasi_config, argv[i]);
|
||||||
wasi_env_set_instance(wasi_env, instance);
|
} else {
|
||||||
|
fprintf(stderr, "--dir expects a following argument specifying which directory to preopen\n");
|
||||||
// WASI is now set up.
|
exit(-1);
|
||||||
|
}
|
||||||
void* vmctx = wasm_instance_get_vmctx_ptr(instance);
|
}
|
||||||
wasm_val_t* inout[2] = { NULL, NULL };
|
else if (strcmp(argv[i], "--mapdir") == 0) {
|
||||||
|
// next arg is a mapdir
|
||||||
fflush(stdout);
|
if ((i + 1) < argc ) {
|
||||||
// We're able to call our compiled function directly through a trampoline.
|
i++;
|
||||||
wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout);
|
pass_mapdir_arg(wasi_config, argv[i]);
|
||||||
|
} else {
|
||||||
wasm_instance_delete(instance);
|
fprintf(stderr, "--mapdir expects a following argument specifying which directory to preopen in the form alias:directory\n");
|
||||||
wasm_module_delete(module);
|
exit(-1);
|
||||||
wasm_store_delete(store);
|
}
|
||||||
wasm_engine_delete(engine);
|
}
|
||||||
return 0;
|
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[]) {
|
||||||
|
printf("Initializing...\n");
|
||||||
|
wasm_config_t* config = wasm_config_new();
|
||||||
|
wasm_config_set_engine(config, OBJECT_FILE);
|
||||||
|
wasm_engine_t* engine = wasm_engine_new_with_config(config);
|
||||||
|
wasm_store_t* store = wasm_store_new(engine);
|
||||||
|
|
||||||
|
wasm_module_t* module = wasmer_object_file_engine_new(store, "qjs.wasm");
|
||||||
|
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(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);
|
||||||
|
int num_imports = import_types.size;
|
||||||
|
wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*));
|
||||||
|
wasm_importtype_vec_delete(&import_types);
|
||||||
|
|
||||||
|
#ifdef WASI
|
||||||
|
bool get_imports_result = wasi_get_imports(store, module, wasi_env, 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, (const wasm_extern_t* const*) imports, NULL);
|
||||||
|
if (! instance) {
|
||||||
|
fprintf(stderr, "Failed to create instance\n");
|
||||||
|
print_wasmer_error();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WASI
|
||||||
|
wasi_env_set_instance(wasi_env, instance);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* vmctx = wasm_instance_get_vmctx_ptr(instance);
|
||||||
|
wasm_val_t* inout[2] = { NULL, NULL };
|
||||||
|
|
||||||
|
// We're able to call our compiled function directly through a trampoline.
|
||||||
|
wasmer_trampoline_function_call__1(vmctx, wasmer_function__1, &inout);
|
||||||
|
|
||||||
|
wasm_instance_delete(instance);
|
||||||
|
wasm_module_delete(module);
|
||||||
|
wasm_store_delete(store);
|
||||||
|
wasm_engine_delete(engine);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_code(executable_path: &Path) -> anyhow::Result<String> {
|
pub fn run_code(executable_path: &Path, args: &[String]) -> anyhow::Result<String> {
|
||||||
let output = Command::new(executable_path.canonicalize()?).output()?;
|
let output = Command::new(executable_path.canonicalize()?)
|
||||||
|
.args(args)
|
||||||
|
.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
bail!(
|
bail!(
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ fn object_file_engine_works() -> anyhow::Result<()> {
|
|||||||
.run()
|
.run()
|
||||||
.context("Failed to link objects together")?;
|
.context("Failed to link objects together")?;
|
||||||
|
|
||||||
let result = run_code(&executable_path).context("Failed to run generated executable")?;
|
let result = run_code(&executable_path, &[]).context("Failed to run generated executable")?;
|
||||||
let result_lines = result.lines().collect::<Vec<&str>>();
|
let result_lines = result.lines().collect::<Vec<&str>>();
|
||||||
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
|
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,11 @@ fn create_exe_works() -> anyhow::Result<()> {
|
|||||||
.run()
|
.run()
|
||||||
.context("Failed to create-exe wasm with Wasmer")?;
|
.context("Failed to create-exe wasm with Wasmer")?;
|
||||||
|
|
||||||
let result = run_code(&executable_path).context("Failed to run generated executable")?;
|
let result = run_code(
|
||||||
|
&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::<Vec<&str>>();
|
let result_lines = result.lines().collect::<Vec<&str>>();
|
||||||
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
|
assert_eq!(result_lines, vec!["Initializing...", "\"Hello, World\""],);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user