mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Split wasm test code into smaller well-named functions
This commit is contained in:
parent
d66d432716
commit
a2e58f8530
1 changed files with 113 additions and 84 deletions
|
@ -2,6 +2,7 @@ use core::cell::Cell;
|
|||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tempfile::{tempdir, TempDir};
|
||||
use wasmer::{Memory, WasmPtr};
|
||||
|
||||
|
@ -49,8 +50,33 @@ pub fn compile_and_load<'a, T: Wasm32TestResult>(
|
|||
_test_wrapper_type_info: PhantomData<T>,
|
||||
build_type: BuildType,
|
||||
) -> wasmer::Instance {
|
||||
use std::path::{Path, PathBuf};
|
||||
let app_module_bytes = compile_roc_to_wasm_bytes(arena, src, stdlib, _test_wrapper_type_info);
|
||||
|
||||
let maybe_src_hash = if DEBUG_LOG_SETTINGS.keep_test_binary {
|
||||
// Keep the output files for debugging, in a directory with a hash in the name
|
||||
Some(src_hash(src))
|
||||
} else {
|
||||
// Use a temporary for linking, then delete it
|
||||
None
|
||||
};
|
||||
|
||||
let final_bytes = run_linker(app_module_bytes, maybe_src_hash, build_type);
|
||||
|
||||
load_bytes_into_runtime(final_bytes)
|
||||
}
|
||||
|
||||
fn src_hash(src: &str) -> u64 {
|
||||
let mut hash_state = DefaultHasher::new();
|
||||
src.hash(&mut hash_state);
|
||||
hash_state.finish()
|
||||
}
|
||||
|
||||
fn compile_roc_to_wasm_bytes<'a, T: Wasm32TestResult>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
_test_wrapper_type_info: PhantomData<T>,
|
||||
) -> Vec<u8> {
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let src_dir = Path::new("fake/test/path");
|
||||
|
||||
|
@ -127,94 +153,97 @@ pub fn compile_and_load<'a, T: Wasm32TestResult>(
|
|||
|
||||
T::insert_test_wrapper(arena, &mut wasm_module, TEST_WRAPPER_NAME, main_fn_index);
|
||||
|
||||
let mut module_bytes = std::vec::Vec::with_capacity(4096);
|
||||
wasm_module.serialize_mut(&mut module_bytes);
|
||||
let mut app_module_bytes = std::vec::Vec::with_capacity(4096);
|
||||
wasm_module.serialize_mut(&mut app_module_bytes);
|
||||
|
||||
// now, do wasmer stuff
|
||||
app_module_bytes
|
||||
}
|
||||
|
||||
use wasmer::{Instance, Module, Store};
|
||||
fn run_linker(
|
||||
app_module_bytes: Vec<u8>,
|
||||
maybe_src_hash: Option<u64>,
|
||||
build_type: BuildType,
|
||||
) -> Vec<u8> {
|
||||
let tmp_dir: TempDir; // directory for normal test runs, deleted when dropped
|
||||
let debug_dir: String; // persistent directory for debugging
|
||||
|
||||
let store = Store::default();
|
||||
|
||||
let wasmer_module = {
|
||||
let tmp_dir: TempDir; // directory for normal test runs, deleted when dropped
|
||||
let debug_dir: String; // persistent directory for debugging
|
||||
|
||||
let wasm_build_dir: &Path = if DEBUG_LOG_SETTINGS.keep_test_binary {
|
||||
// Directory name based on a hash of the Roc source
|
||||
let mut hash_state = DefaultHasher::new();
|
||||
src.hash(&mut hash_state);
|
||||
let src_hash = hash_state.finish();
|
||||
debug_dir = format!("/tmp/roc/gen_wasm/{:016x}", src_hash);
|
||||
std::fs::create_dir_all(&debug_dir).unwrap();
|
||||
println!(
|
||||
"Debug commands:\n\twasm-objdump -dx {}/app.o\n\twasm-objdump -dx {}/final.wasm",
|
||||
&debug_dir, &debug_dir,
|
||||
);
|
||||
Path::new(&debug_dir)
|
||||
} else {
|
||||
tmp_dir = tempdir().unwrap();
|
||||
tmp_dir.path()
|
||||
};
|
||||
|
||||
let final_wasm_file = wasm_build_dir.join("final.wasm");
|
||||
let app_o_file = wasm_build_dir.join("app.o");
|
||||
let test_out_dir = std::env::var(OUT_DIR_VAR).unwrap();
|
||||
let test_platform_o = format!("{}/{}.o", test_out_dir, PLATFORM_FILENAME);
|
||||
let libc_a_file = std::env::var(LIBC_PATH_VAR).unwrap();
|
||||
let compiler_rt_o_file = std::env::var(COMPILER_RT_PATH_VAR).unwrap();
|
||||
|
||||
// write the module to a file so the linker can access it
|
||||
std::fs::write(&app_o_file, &module_bytes).unwrap();
|
||||
|
||||
let mut args = vec![
|
||||
"wasm-ld",
|
||||
// input files
|
||||
app_o_file.to_str().unwrap(),
|
||||
bitcode::BUILTINS_WASM32_OBJ_PATH,
|
||||
&test_platform_o,
|
||||
&libc_a_file,
|
||||
&compiler_rt_o_file,
|
||||
// output
|
||||
"-o",
|
||||
final_wasm_file.to_str().unwrap(),
|
||||
// we don't define `_start`
|
||||
"--no-entry",
|
||||
// If you only specify test_wrapper, it will stop at the call to UserApp_main_1
|
||||
// But if you specify both exports, you get all the dependencies.
|
||||
//
|
||||
// It seems that it will not write out an export you didn't explicitly specify,
|
||||
// even if it's a dependency of another export!
|
||||
"--export",
|
||||
"test_wrapper",
|
||||
"--export",
|
||||
"#UserApp_main_1",
|
||||
];
|
||||
|
||||
// For some reason, this makes linking ~3x slower
|
||||
if matches!(build_type, BuildType::Refcount) {
|
||||
args.extend_from_slice(&["--export", "init_refcount_test"]);
|
||||
}
|
||||
|
||||
let linker_output = std::process::Command::new(&crate::helpers::zig_executable())
|
||||
.args(&args)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if !linker_output.status.success() {
|
||||
print!("\nLINKER FAILED\n");
|
||||
for arg in args {
|
||||
print!("{} ", arg);
|
||||
}
|
||||
println!("\n{}", std::str::from_utf8(&linker_output.stdout).unwrap());
|
||||
println!("{}", std::str::from_utf8(&linker_output.stderr).unwrap());
|
||||
}
|
||||
|
||||
Module::from_file(&store, &final_wasm_file).unwrap()
|
||||
let wasm_build_dir: &Path = if let Some(src_hash) = maybe_src_hash {
|
||||
debug_dir = format!("/tmp/roc/gen_wasm/{:016x}", src_hash);
|
||||
std::fs::create_dir_all(&debug_dir).unwrap();
|
||||
println!(
|
||||
"Debug commands:\n\twasm-objdump -dx {}/app.o\n\twasm-objdump -dx {}/final.wasm",
|
||||
&debug_dir, &debug_dir,
|
||||
);
|
||||
Path::new(&debug_dir)
|
||||
} else {
|
||||
tmp_dir = tempdir().unwrap();
|
||||
tmp_dir.path()
|
||||
};
|
||||
|
||||
// First, we create the `WasiEnv`
|
||||
let final_wasm_file = wasm_build_dir.join("final.wasm");
|
||||
let app_o_file = wasm_build_dir.join("app.o");
|
||||
let test_out_dir = std::env::var(OUT_DIR_VAR).unwrap();
|
||||
let test_platform_o = format!("{}/{}.o", test_out_dir, PLATFORM_FILENAME);
|
||||
let libc_a_file = std::env::var(LIBC_PATH_VAR).unwrap();
|
||||
let compiler_rt_o_file = std::env::var(COMPILER_RT_PATH_VAR).unwrap();
|
||||
|
||||
// write the module to a file so the linker can access it
|
||||
std::fs::write(&app_o_file, &app_module_bytes).unwrap();
|
||||
|
||||
let mut args = vec![
|
||||
"wasm-ld",
|
||||
// input files
|
||||
app_o_file.to_str().unwrap(),
|
||||
bitcode::BUILTINS_WASM32_OBJ_PATH,
|
||||
&test_platform_o,
|
||||
&libc_a_file,
|
||||
&compiler_rt_o_file,
|
||||
// output
|
||||
"-o",
|
||||
final_wasm_file.to_str().unwrap(),
|
||||
// we don't define `_start`
|
||||
"--no-entry",
|
||||
// If you only specify test_wrapper, it will stop at the call to UserApp_main_1
|
||||
// But if you specify both exports, you get all the dependencies.
|
||||
//
|
||||
// It seems that it will not write out an export you didn't explicitly specify,
|
||||
// even if it's a dependency of another export!
|
||||
"--export",
|
||||
"test_wrapper",
|
||||
"--export",
|
||||
"#UserApp_main_1",
|
||||
];
|
||||
|
||||
// For some reason, this makes linking ~3x slower
|
||||
if matches!(build_type, BuildType::Refcount) {
|
||||
args.extend_from_slice(&["--export", "init_refcount_test"]);
|
||||
}
|
||||
|
||||
let linker_output = std::process::Command::new(&crate::helpers::zig_executable())
|
||||
.args(&args)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if !linker_output.status.success() {
|
||||
print!("\nLINKER FAILED\n");
|
||||
for arg in args {
|
||||
print!("{} ", arg);
|
||||
}
|
||||
println!("\n{}", std::str::from_utf8(&linker_output.stdout).unwrap());
|
||||
println!("{}", std::str::from_utf8(&linker_output.stderr).unwrap());
|
||||
}
|
||||
|
||||
std::fs::read(final_wasm_file).unwrap()
|
||||
}
|
||||
|
||||
fn load_bytes_into_runtime(bytes: Vec<u8>) -> wasmer::Instance {
|
||||
use wasmer::{Module, Store};
|
||||
use wasmer_wasi::WasiState;
|
||||
|
||||
let store = Store::default();
|
||||
let wasmer_module = Module::new(&store, &bytes).unwrap();
|
||||
|
||||
// First, we create the `WasiEnv`
|
||||
let mut wasi_env = WasiState::new("hello").finalize().unwrap();
|
||||
|
||||
// Then, we get the import object related to our WASI
|
||||
|
@ -223,7 +252,7 @@ pub fn compile_and_load<'a, T: Wasm32TestResult>(
|
|||
.import_object(&wasmer_module)
|
||||
.unwrap_or_else(|_| wasmer::imports!());
|
||||
|
||||
Instance::new(&wasmer_module, &import_object).unwrap()
|
||||
wasmer::Instance::new(&wasmer_module, &import_object).unwrap()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue