wasmer_capi_examples_runner/
lib.rs1#[cfg(test)]
2use std::error::Error;
3
4#[cfg(test)]
5static INCLUDE_REGEX: &str = "#include \"(.*)\"";
6
7#[derive(Default)]
8pub struct RemoveTestsOnDrop {}
9
10impl Drop for RemoveTestsOnDrop {
11 fn drop(&mut self) {
12 let manifest_dir = env!("CARGO_MANIFEST_DIR");
13 for entry in std::fs::read_dir(manifest_dir).unwrap() {
14 let entry = entry.unwrap();
15 let path = entry.path();
16 let extension = path.extension().and_then(|s| s.to_str());
17 if extension == Some("obj") || extension == Some("exe") || extension == Some("o") {
18 println!("removing {}", path.display());
19 let _ = std::fs::remove_file(&path);
20 }
21 }
22 if let Some(parent) = std::path::Path::new(&manifest_dir).parent() {
23 for entry in std::fs::read_dir(parent).unwrap() {
24 let entry = entry.unwrap();
25 let path = entry.path();
26 let extension = path.extension().and_then(|s| s.to_str());
27 if extension == Some("obj") || extension == Some("exe") || extension == Some("o") {
28 println!("removing {}", path.display());
29 let _ = std::fs::remove_file(path);
30 }
31 }
32 }
33 }
34}
35
36fn make_package() {
37 let wasmer_root_dir = find_wasmer_base_dir();
38 let _ = std::fs::create_dir_all(format!("{wasmer_root_dir}/package/lib"));
39 let _ = std::fs::create_dir_all(format!("{wasmer_root_dir}/package/include"));
40 let _ = std::fs::copy(
41 format!("{wasmer_root_dir}/lib/c-api/tests/wasm.h"),
42 format!("{wasmer_root_dir}/package/include/wasm.h"),
43 );
44 let _ = std::fs::copy(
45 format!("{wasmer_root_dir}/lib/c-api/tests/wasmer.h"),
46 format!("{wasmer_root_dir}/package/include/wasmer.h"),
47 );
48 #[cfg(target_os = "windows")]
49 let _ = std::fs::copy(
50 &format!("{wasmer_root_dir}/target/release/wasmer.dll"),
51 &format!("{wasmer_root_dir}/package/lib"),
52 );
53 #[cfg(target_os = "windows")]
54 let _ = std::fs::copy(
55 &format!("{wasmer_root_dir}/target/release/wasmer.dll.lib"),
56 &format!("{wasmer_root_dir}/package/lib"),
57 );
58 #[cfg(not(target_os = "windows"))]
59 let _ = std::fs::copy(
60 format!("{wasmer_root_dir}/target/release/libwasmer.so"),
61 format!("{wasmer_root_dir}/package/lib"),
62 );
63 #[cfg(not(target_os = "windows"))]
64 let _ = std::fs::copy(
65 format!("{wasmer_root_dir}/target/release/libwasmer.lib"),
66 format!("{wasmer_root_dir}/package/lib"),
67 );
68 println!("copying done (make package)");
69}
70
71#[derive(Debug)]
72pub struct Config {
73 pub wasmer_dir: String,
74 pub root_dir: String,
75}
76
77impl Config {
78 pub fn get() -> Config {
79 let mut config = Config {
80 wasmer_dir: std::env::var("WASMER_DIR").unwrap_or_default(),
81 root_dir: std::env::var("ROOT_DIR").unwrap_or_default(),
82 };
83
84 let manifest_dir = env!("CARGO_MANIFEST_DIR");
86 let wasmer_base_dir = find_wasmer_base_dir();
87
88 if config.wasmer_dir.is_empty() {
89 println!("manifest dir = {manifest_dir}, wasmer root dir = {wasmer_base_dir}");
90 config.wasmer_dir = wasmer_base_dir.clone() + "/package";
91 if !std::path::Path::new(&config.wasmer_dir).exists() {
92 println!("running make build-capi...");
93 let mut cmd = std::process::Command::new("make");
95 cmd.arg("build-capi");
96 cmd.current_dir(&wasmer_base_dir);
97 let result = cmd.output();
98 println!("make build-capi: {result:#?}");
99
100 println!("running make package-capi...");
101 let mut cmd = std::process::Command::new("make");
103 cmd.arg("package-capi");
104 cmd.current_dir(&wasmer_base_dir);
105 let result = cmd.output();
106 make_package();
107 println!("make package: {result:#?}");
108
109 println!("list {}", config.wasmer_dir);
110 match std::fs::read_dir(&config.wasmer_dir) {
111 Ok(o) => {
112 for entry in o {
113 let entry = entry.unwrap();
114 let path = entry.path();
115 println!(" {:?}", path.file_name());
116 }
117 }
118 Err(e) => {
119 println!("error in reading config.wasmer_dir: {e}");
120 }
121 };
122 }
123 }
124 if config.root_dir.is_empty() {
125 config.root_dir = wasmer_base_dir + "/lib/c-api/examples";
126 }
127
128 config
129 }
130}
131
132fn find_wasmer_base_dir() -> String {
133 let wasmer_base_dir = env!("CARGO_MANIFEST_DIR");
134 let mut path2 = wasmer_base_dir.split("wasmer").collect::<Vec<_>>();
135 path2.pop();
136 let mut wasmer_base_dir = path2.join("wasmer");
137
138 if wasmer_base_dir.contains("wasmer/lib/c-api") {
139 wasmer_base_dir = wasmer_base_dir
140 .split("wasmer/lib/c-api")
141 .next()
142 .unwrap()
143 .to_string()
144 + "wasmer";
145 } else if wasmer_base_dir.contains("wasmer\\lib\\c-api") {
146 wasmer_base_dir = wasmer_base_dir
147 .split("wasmer\\lib\\c-api")
148 .next()
149 .unwrap()
150 .to_string()
151 + "wasmer";
152 }
153
154 wasmer_base_dir
155}
156
157#[cfg(test)]
158pub const TESTS: &[&str] = &[
159 "deprecated-header",
160 "early-exit",
161 "instance",
162 "imports-exports",
163 "exports-function",
164 "exports-global",
165 "memory",
166 "memory2",
167 "features",
168 "wasi",
169];
170
171#[test]
172fn test_run() {
173 let _drop = RemoveTestsOnDrop::default();
174 let config = Config::get();
175 println!("config: {config:#?}");
176
177 let manifest_dir = env!("CARGO_MANIFEST_DIR");
178 let host = target_lexicon::HOST.to_string();
179 let target = &host;
180
181 let wasmer_dll_dir = format!("{}/lib", config.wasmer_dir);
182 let libwasmer_so_path = format!("{}/lib/libwasmer.so", config.wasmer_dir);
183 let path = std::env::var("PATH").unwrap_or_default();
184 let newpath = format!("{wasmer_dll_dir};{path}");
185 let exe_dir = match std::path::Path::new(&manifest_dir).parent() {
186 Some(parent) => format!("{}", parent.display()),
187 None => manifest_dir.to_string(),
188 };
189
190 for test in TESTS.iter() {
191 let manifest_dir_parent = std::path::Path::new(&manifest_dir);
192 let manifest_dir_parent = manifest_dir_parent.parent().unwrap();
193 let c_file_path = manifest_dir_parent.join(format!("{test}.c"));
194
195 if target.contains("msvc") {
196 let mut build = cc::Build::new();
197 let build = build
198 .cargo_metadata(false)
199 .warnings(true)
200 .static_crt(true)
201 .extra_warnings(true)
202 .warnings_into_errors(false)
203 .debug(true)
204 .host(&host)
205 .target(target)
206 .opt_level(1);
207
208 let compiler = build.try_get_compiler().unwrap();
209 let mut command = compiler.to_command();
210
211 command.arg(format!("{}", c_file_path.display()));
212 if !config.wasmer_dir.is_empty() {
213 command.arg("/I");
214 command.arg(format!("{}/include/", config.wasmer_dir));
215 let mut log = String::new();
216 fixup_symlinks(
217 &[
218 format!("{}/include", config.wasmer_dir),
219 config.root_dir.to_string(),
220 ],
221 &mut log,
222 &config.root_dir,
223 )
224 .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}"));
225 println!("{log}");
226 }
227
228 let exe_outpath = manifest_dir_parent.join(format!("{test}.exe"));
229 let exe_outpath = format!("{}", exe_outpath.display());
230 println!("compiling exe to {exe_outpath}");
231
232 command.arg(format!("/Fo:{}/", manifest_dir_parent.display()));
233 command.arg("/link");
234 if !config.wasmer_dir.is_empty() {
235 command.arg(format!("/LIBPATH:{}/lib", config.wasmer_dir));
236 command.arg(format!("{}/lib/wasmer.dll.lib", config.wasmer_dir));
237 }
238 command.arg(format!("/OUT:{exe_outpath}"));
239
240 println!("compiling WINDOWS {test}: {command:?}");
243
244 let vcvars_bat_path = find_vcvarsall(&compiler).expect("no vcvarsall.bat");
245 let vcvars_bat_path_parent = std::path::Path::new(&vcvars_bat_path).parent().unwrap();
246 let _vcvars_modified_output = vcvars_bat_path_parent.join("compile-windows.bat");
247 let vcvars_bat_file = std::fs::read_to_string(&vcvars_bat_path).unwrap();
248 let batch_formatted = format!("{}\\", vcvars_bat_path_parent.display());
249 let vcvars_bat_file = vcvars_bat_file
250 .replace("%~dp0", &batch_formatted.replace('\\', "\\\\"))
251 .replace("\"%1\"", "\"x64\"");
252 let vcvars_modified = format!("{vcvars_bat_file}\r\n{command:?}");
253 let path = std::path::Path::new(&manifest_dir).join("compile-windows.bat");
254 println!("outputting batch to {}", path.display());
255 std::fs::write(&path, vcvars_modified).unwrap();
256
257 let mut vcvars = std::process::Command::new("cmd");
260 vcvars.arg("/C");
261 vcvars.arg(&path);
262 vcvars.arg("x64");
263 vcvars.current_dir(vcvars_bat_path_parent);
264
265 let output = vcvars
267 .output()
268 .map_err(|e| format!("failed to compile {command:#?}: {e}"))
269 .unwrap();
270 if !output.status.success() {
271 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
272 println!("stdout: {}", String::from_utf8_lossy(&output.stderr));
273 panic!("failed to compile {test}");
275 }
276
277 if !std::path::Path::new(&exe_outpath).exists() {
278 panic!("error: {exe_outpath} does not exist");
279 }
280 if !std::path::Path::new(&wasmer_dll_dir)
281 .join("wasmer.dll")
282 .exists()
283 {
284 panic!("error: {wasmer_dll_dir} has no wasmer.dll");
285 }
286 let mut command = std::process::Command::new(&exe_outpath);
288 command.env("PATH", &newpath);
289 command.current_dir(&exe_dir);
290 println!("executing {test}: {command:?}");
291 println!("setting current dir = {exe_dir}");
292 let output = command
293 .output()
294 .unwrap_or_else(|_| panic!("failed to run {command:#?}"));
295 if !output.status.success() {
296 println!("{output:#?}");
297 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
298 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
299 panic!("failed to execute {test}");
301 }
302 } else {
303 let compiler_cmd = match std::process::Command::new("cc").output() {
304 Ok(_) => "cc",
305 Err(_) => "gcc",
306 };
307
308 let mut command = std::process::Command::new(compiler_cmd);
309
310 if !config.wasmer_dir.is_empty() {
311 command.arg("-I");
312 command.arg(format!("{}/include", config.wasmer_dir));
313 let mut log = String::new();
314 fixup_symlinks(
315 &[
316 format!("{}/include", config.wasmer_dir),
317 config.root_dir.to_string(),
318 ],
319 &mut log,
320 &config.root_dir,
321 )
322 .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}"));
323 }
324 command.arg(&c_file_path);
325 if !config.wasmer_dir.is_empty() {
326 command.arg("-L");
327 command.arg(format!("{}/lib/", config.wasmer_dir));
328 command.arg("-lwasmer");
329 command.arg(format!("-Wl,-rpath,{}/lib/", config.wasmer_dir));
330 }
331 command.arg("-o");
332 command.arg(format!("{manifest_dir}/../{test}"));
333
334 println!("compiling LINUX {command:#?}");
348 let output = command
350 .output()
351 .map_err(|e| format!("failed to compile {command:#?}: {e}"))
352 .unwrap();
353 if !output.status.success() {
354 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
355 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
356 panic!("failed to compile {test}: {command:#?}");
358 }
359
360 let mut command = std::process::Command::new(format!("{manifest_dir}/../{test}"));
362 command.env("LD_PRELOAD", &libwasmer_so_path);
363 command.current_dir(&exe_dir);
364 println!("execute: {command:#?}");
365 let output = command
366 .output()
367 .unwrap_or_else(|_| panic!("failed to run {command:#?}"));
368 if !output.status.success() {
369 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
370 println!("stdout: {}", String::from_utf8_lossy(&output.stderr));
371 panic!("failed to execute {test} executable");
373 }
374 }
375 }
376}
377
378#[cfg(test)]
404fn fixup_symlinks(
405 include_paths: &[String],
406 log: &mut String,
407 root_dir: &str,
408) -> Result<(), Box<dyn Error>> {
409 let source = std::path::Path::new(root_dir)
410 .join("lib")
411 .join("c-api")
412 .join("tests")
413 .join("wasm-c-api")
414 .join("include")
415 .join("wasm.h");
416 let target = std::path::Path::new(root_dir)
417 .join("lib")
418 .join("c-api")
419 .join("tests")
420 .join("wasm.h");
421 println!("copying {} -> {}", source.display(), target.display());
422 let _ = std::fs::copy(source, target);
423
424 log.push_str(&format!("include paths: {include_paths:?}"));
425 for i in include_paths {
426 let i = i.replacen("-I", "", 1);
427 let mut paths_headers = Vec::new();
428 let readdir = match std::fs::read_dir(&i) {
429 Ok(o) => o,
430 Err(_) => continue,
431 };
432 for entry in readdir {
433 let entry = entry?;
434 let path = entry.path();
435 let path_display = format!("{}", path.display());
436 if path_display.ends_with('h') {
437 paths_headers.push(path_display);
438 }
439 }
440 fixup_symlinks_inner(&paths_headers, log)?;
441 }
442
443 Ok(())
444}
445
446#[cfg(test)]
447fn fixup_symlinks_inner(include_paths: &[String], log: &mut String) -> Result<(), Box<dyn Error>> {
448 log.push_str(&format!("fixup symlinks: {include_paths:#?}"));
449 let regex = regex::Regex::new(INCLUDE_REGEX).unwrap();
450 for path in include_paths.iter() {
451 let file = match std::fs::read_to_string(path) {
452 Ok(o) => o,
453 _ => continue,
454 };
455 let lines_3 = file.lines().take(3).collect::<Vec<_>>();
456 log.push_str(&format!("first 3 lines of {path:?}: {lines_3:#?}\n"));
457
458 let parent = std::path::Path::new(&path).parent().unwrap();
459 if let Ok(symlink) = std::fs::read_to_string(parent.join(&file)) {
460 log.push_str(&format!("symlinking {path:?}\n"));
461 std::fs::write(path, symlink)?;
462 }
463
464 let filepaths = regex
466 .captures_iter(&file)
467 .map(|c| c[1].to_string())
468 .collect::<Vec<_>>();
469 log.push_str(&format!("regex captures: ({path:?}): {filepaths:#?}\n"));
470 let joined_filepaths = filepaths
471 .iter()
472 .map(|s| {
473 let path = parent.join(s);
474 format!("{}", path.display())
475 })
476 .collect::<Vec<_>>();
477 fixup_symlinks_inner(&joined_filepaths, log)?;
478 }
479 Ok(())
480}
481
482#[cfg(test)]
483fn find_vcvarsall(compiler: &cc::Tool) -> Option<String> {
484 if !compiler.is_like_msvc() {
485 return None;
486 }
487
488 let path = compiler.path();
489 let path = format!("{}", path.display());
490 let split = path.split("VC").next()?;
491
492 Some(format!("{split}VC\\Auxiliary\\Build\\vcvarsall.bat"))
493}