wasmer_c_api_test_runner/
lib.rs1#[cfg(test)]
2use std::error::Error;
3#[cfg(test)]
4use std::process::Stdio;
5
6#[cfg(test)]
7static INCLUDE_REGEX: &str = "#include \"(.*)\"";
8
9#[derive(Debug)]
10pub struct Config {
11 pub wasmer_dir: String,
12 pub root_dir: String,
13}
14
15impl Config {
16 pub fn get() -> Config {
17 let mut config = Config {
18 wasmer_dir: std::env::var("WASMER_DIR").unwrap_or_default(),
19 root_dir: std::env::var("ROOT_DIR").unwrap_or_default(),
20 };
21
22 let wasmer_base_dir = find_wasmer_base_dir();
23 let manifest_dir = env!("CARGO_MANIFEST_DIR");
24
25 if config.wasmer_dir.is_empty() {
26 println!("manifest dir = {manifest_dir}, wasmer root dir = {wasmer_base_dir}");
27 config.wasmer_dir = wasmer_base_dir.clone() + "/package";
28 assert!(std::path::Path::new(&config.wasmer_dir).exists());
29 }
30 if config.root_dir.is_empty() {
31 config.root_dir = wasmer_base_dir + "/lib/c-api/tests";
32 }
33
34 config
35 }
36}
37
38fn find_wasmer_base_dir() -> String {
39 let wasmer_base_dir = env!("CARGO_MANIFEST_DIR");
40 let mut path2 = wasmer_base_dir.split("wasmer").collect::<Vec<_>>();
41 path2.pop();
42 let mut wasmer_base_dir = path2.join("wasmer");
43
44 if wasmer_base_dir.contains("wasmer/lib/c-api") {
45 wasmer_base_dir = wasmer_base_dir
46 .split("wasmer/lib/c-api")
47 .next()
48 .unwrap()
49 .to_string()
50 + "wasmer";
51 } else if wasmer_base_dir.contains("wasmer\\lib\\c-api") {
52 wasmer_base_dir = wasmer_base_dir
53 .split("wasmer\\lib\\c-api")
54 .next()
55 .unwrap()
56 .to_string()
57 + "wasmer";
58 }
59
60 wasmer_base_dir
61}
62
63#[derive(Default)]
64pub struct RemoveTestsOnDrop {}
65
66impl Drop for RemoveTestsOnDrop {
67 fn drop(&mut self) {
68 let manifest_dir = env!("CARGO_MANIFEST_DIR");
69 for entry in std::fs::read_dir(manifest_dir).unwrap() {
70 let entry = entry.unwrap();
71 let path = entry.path();
72 let extension = path.extension().and_then(|s| s.to_str());
73 if extension == Some("obj") || extension == Some("exe") || extension == Some("o") {
74 println!("removing {}", path.display());
75 let _ = std::fs::remove_file(&path);
76 }
77 }
78 if let Some(parent) = std::path::Path::new(&manifest_dir).parent() {
79 for entry in std::fs::read_dir(parent).unwrap() {
80 let entry = entry.unwrap();
81 let path = entry.path();
82 let extension = path.extension().and_then(|s| s.to_str());
83 if extension == Some("obj") || extension == Some("exe") || extension == Some("o") {
84 println!("removing {}", path.display());
85 let _ = std::fs::remove_file(&path);
86 }
87 }
88 }
89 }
90}
91
92#[cfg(test)]
93pub const CAPI_BASE_TESTS: &[&str] = &[
94 "wasm-c-api/example/callback",
95 "wasm-c-api/example/memory",
96 "wasm-c-api/example/start",
97 "wasm-c-api/example/global",
98 "wasm-c-api/example/reflect",
99 "wasm-c-api/example/trap",
100 "wasm-c-api/example/hello",
101 "wasm-c-api/example/serialize",
102 "wasm-c-api/example/multi",
103];
104
105#[allow(unused_variables, dead_code)]
106pub const CAPI_BASE_TESTS_NOT_WORKING: &[&str] = &[
107 "wasm-c-api/example/finalize",
108 "wasm-c-api/example/hostref",
109 "wasm-c-api/example/threads",
110 "wasm-c-api/example/table",
111];
112
113#[test]
115fn test_ok() {
116 let _drop = RemoveTestsOnDrop::default();
117 let config = Config::get();
118 println!("config: {config:#?}");
119
120 let manifest_dir = env!("CARGO_MANIFEST_DIR");
121 let host = target_lexicon::HOST.to_string();
122 let target = &host;
123
124 let wasmer_dll_dir = format!("{}/lib", config.wasmer_dir);
125 let libwasmer_so_path = format!("{}/lib/libwasmer.so", config.wasmer_dir);
126 let exe_dir = format!("{manifest_dir}/../wasm-c-api/example");
127 let path = std::env::var("PATH").unwrap_or_default();
128 let newpath = format!("{};{path}", wasmer_dll_dir.replace('/', "\\"));
129
130 if target.contains("msvc") {
131 for test in CAPI_BASE_TESTS.iter() {
132 let mut build = cc::Build::new();
133 let build = build
134 .cargo_metadata(false)
135 .warnings(true)
136 .static_crt(true)
137 .extra_warnings(true)
138 .warnings_into_errors(false)
139 .debug(true)
140 .host(&host)
141 .target(target)
142 .opt_level(1);
143
144 let compiler = build.try_get_compiler().unwrap();
145
146 println!("compiler {compiler:#?}");
147
148 let vcvars_bat_path = find_vcvars64(&compiler).expect("no vcvars64.bat");
150 let mut vcvars = std::process::Command::new("cmd");
151 vcvars.arg("/C");
152 vcvars.arg(vcvars_bat_path);
153 println!("running {vcvars:?}");
154
155 let output = vcvars
157 .output()
158 .expect("could not invoke vcvars64.bat at {vcvars_bat_path}");
159
160 if !output.status.success() {
161 println!();
162 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
163 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
164 panic!("failed to invoke vcvars64.bat {test}");
166 }
167
168 let mut command = compiler.to_command();
169
170 command.arg(format!("{manifest_dir}/../{test}.c"));
171 if !config.wasmer_dir.is_empty() {
172 command.arg("/I");
173 command.arg(format!("{}/wasm-c-api/include/", config.root_dir));
174 command.arg("/I");
175 command.arg(format!("{}/include/", config.wasmer_dir));
176 let mut log = String::new();
177 fixup_symlinks(
178 &[
179 format!("{}/include/", config.wasmer_dir),
180 format!("{}/wasm-c-api/include/", config.root_dir),
181 config.root_dir.to_string(),
182 ],
183 &mut log,
184 &config.root_dir,
185 )
186 .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}"));
187 println!("{log}");
188 }
189 command.arg("/link");
190 if !config.wasmer_dir.is_empty() {
191 command.arg(format!("/LIBPATH:{}/lib", config.wasmer_dir));
192 command.arg(format!("{}/lib/wasmer.dll.lib", config.wasmer_dir));
193 }
194 command.arg(format!("/OUT:{manifest_dir}/../{test}.exe"));
195
196 println!("compiling {test}: {command:?}");
197
198 let output = command
200 .output()
201 .unwrap_or_else(|_| panic!("failed to compile {command:#?}"));
202 if !output.status.success() {
203 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
204 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
205 println!("output: {output:#?}");
206 panic!("failed to compile {test}");
208 }
209
210 if std::path::Path::new(&format!("{manifest_dir}/../{test}.exe")).exists() {
211 println!("exe does not exist");
212 }
213
214 let mut command = std::process::Command::new(format!("{manifest_dir}/../{test}.exe"));
216 println!("newpath: {}", newpath.clone());
217 command.env("PATH", newpath.clone());
218 command.current_dir(exe_dir.clone());
219 println!("executing {test}: {command:?}");
220 println!("setting current dir = {exe_dir}");
221 let output = command
222 .output()
223 .unwrap_or_else(|_| panic!("failed to run {command:#?}"));
224 if !output.status.success() {
225 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
226 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
227 println!("output: {output:#?}");
228 panic!("failed to execute {test}");
230 }
231
232 }
243 } else {
244 for test in CAPI_BASE_TESTS.iter() {
245 let compiler_cmd = match std::process::Command::new("cc").output() {
246 Ok(_) => "cc",
247 Err(_) => "gcc",
248 };
249 let mut command = std::process::Command::new(compiler_cmd);
250
251 if !config.wasmer_dir.is_empty() {
252 command.arg("-I");
253 command.arg(format!("{}/wasm-c-api/include/", config.root_dir));
254 command.arg("-I");
255 command.arg(format!("{}/include/", config.wasmer_dir));
256 let mut log = String::new();
257 fixup_symlinks(
258 &[
259 format!("{}/include/", config.wasmer_dir),
260 format!("{}/wasm-c-api/include/", config.root_dir),
261 config.root_dir.to_string(),
262 ],
263 &mut log,
264 &config.root_dir,
265 )
266 .unwrap_or_else(|_| panic!("failed to fix symlinks: {log}"));
267 }
268 command.arg(format!("{manifest_dir}/../{test}.c"));
269 if !config.wasmer_dir.is_empty() {
270 command.arg("-L");
271 command.arg(format!("{}/lib/", config.wasmer_dir));
272 command.arg("-lwasmer");
273 command.arg(format!("-Wl,-rpath,{}/lib/", config.wasmer_dir));
274 }
275 command.arg("-o");
276 command.arg(format!("{manifest_dir}/../{test}"));
277
278 println!("compile: {command:#?}");
281 let output = command
283 .stdout(Stdio::inherit())
284 .stderr(Stdio::inherit())
285 .current_dir(find_wasmer_base_dir())
286 .output()
287 .unwrap_or_else(|_| panic!("failed to compile {command:#?}"));
288 if !output.status.success() {
289 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
290 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
291 panic!("failed to compile {test}: {command:#?}");
293 }
294
295 let mut command = std::process::Command::new(format!("{manifest_dir}/../{test}"));
297 command.env("LD_PRELOAD", libwasmer_so_path.clone());
298 command.current_dir(exe_dir.clone());
299 println!("execute: {command:#?}");
300 let output = command
301 .output()
302 .unwrap_or_else(|_| panic!("failed to run {command:#?}"));
303 if !output.status.success() {
304 println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
305 println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
306 panic!("failed to execute {test}: {command:#?}");
308 }
309 }
310 }
311
312 for test in CAPI_BASE_TESTS.iter() {
313 let _ = std::fs::remove_file(format!("{manifest_dir}/{test}.obj"));
314 let _ = std::fs::remove_file(format!("{manifest_dir}/../{test}.exe"));
315 let _ = std::fs::remove_file(format!("{manifest_dir}/../{test}"));
316 }
317}
318
319#[cfg(test)]
361fn fixup_symlinks(
362 include_paths: &[String],
363 log: &mut String,
364 root_dir: &str,
365) -> Result<(), Box<dyn Error>> {
366 let source = std::path::Path::new(root_dir)
367 .join("lib")
368 .join("c-api")
369 .join("tests")
370 .join("wasm-c-api")
371 .join("include")
372 .join("wasm.h");
373 let target = std::path::Path::new(root_dir)
374 .join("lib")
375 .join("c-api")
376 .join("tests")
377 .join("wasm.h");
378 println!("copying {} -> {}", source.display(), target.display());
379 let _ = std::fs::copy(source, target);
380
381 log.push_str(&format!("include paths: {include_paths:?}"));
382 for i in include_paths {
383 let i = i.replacen("-I", "", 1);
384 let i = i.replacen("/I", "", 1);
385 let mut paths_headers = Vec::new();
386 let readdir = match std::fs::read_dir(&i) {
387 Ok(o) => o,
388 Err(_) => continue,
389 };
390 for entry in readdir {
391 let entry = entry?;
392 let path = entry.path();
393 let path_display = format!("{}", path.display());
394 if path_display.ends_with('h') {
395 paths_headers.push(path_display);
396 }
397 }
398 fixup_symlinks_inner(&paths_headers, log)?;
399 }
400
401 Ok(())
402}
403
404#[cfg(test)]
405fn fixup_symlinks_inner(include_paths: &[String], log: &mut String) -> Result<(), Box<dyn Error>> {
406 log.push_str(&format!("fixup symlinks: {include_paths:#?}"));
407 let regex = regex::Regex::new(INCLUDE_REGEX).unwrap();
408 for path in include_paths.iter() {
409 let file = match std::fs::read_to_string(path) {
410 Ok(o) => o,
411 _ => continue,
412 };
413 let lines_3 = file.lines().take(3).collect::<Vec<_>>();
414 log.push_str(&format!("first 3 lines of {path:?}: {lines_3:#?}\n"));
415
416 let parent = std::path::Path::new(&path).parent().unwrap();
417 if let Ok(symlink) = std::fs::read_to_string(parent.join(&file)) {
418 log.push_str(&format!("symlinking {path:?}\n"));
419 std::fs::write(path, symlink)?;
420 }
421
422 let filepaths = regex
424 .captures_iter(&file)
425 .map(|c| c[1].to_string())
426 .collect::<Vec<_>>();
427 log.push_str(&format!("regex captures: ({path:?}): {filepaths:#?}\n"));
428 let joined_filepaths = filepaths
429 .iter()
430 .map(|s| {
431 let path = parent.join(s);
432 format!("{}", path.display())
433 })
434 .collect::<Vec<_>>();
435 fixup_symlinks_inner(&joined_filepaths, log)?;
436 }
437 Ok(())
438}
439
440#[cfg(test)]
441fn find_vcvars64(compiler: &cc::Tool) -> Option<String> {
442 if !compiler.is_like_msvc() {
443 return None;
444 }
445
446 let path = compiler.path();
447 let path = format!("{}", path.display());
448 let split = path.split("VC").next()?;
449
450 Some(format!("{split}VC\\Auxiliary\\Build\\vcvars64.bat"))
451}