#![allow(clippy::uninlined_format_args)] use std::env::current_dir; use std::path::{Path, PathBuf}; use std::process::Command; use lang_tester::LangTester; use tempfile::TempDir; fn compile_and_run_cmds( compiler_args: Vec, test_target: &Option, exe: &Path, test_mode: TestMode, ) -> Vec<(&'static str, Command)> { let mut compiler = Command::new("rustc"); compiler.args(compiler_args); // Test command 2: run `tempdir/x`. if test_target.is_some() { let mut env_path = std::env::var("PATH").unwrap_or_default(); // FIXME(antoyo): find a better way to add the PATH necessary locally. env_path = format!("/opt/m68k-unknown-linux-gnu/bin:{}", env_path); compiler.env("PATH", env_path); let mut commands = vec![("Compiler", compiler)]; if test_mode.should_run() { let vm_parent_dir = std::env::var("CG_GCC_VM_DIR") .map(PathBuf::from) .unwrap_or_else(|_| std::env::current_dir().unwrap()); let vm_dir = "vm"; let exe_filename = exe.file_name().unwrap(); let vm_home_dir = vm_parent_dir.join(vm_dir).join("home"); let vm_exe_path = vm_home_dir.join(exe_filename); // FIXME(antoyo): panicking here makes the test pass. let inside_vm_exe_path = PathBuf::from("/home").join(exe_filename); let mut copy = Command::new("sudo"); copy.arg("cp"); copy.args([exe, &vm_exe_path]); let mut runtime = Command::new("sudo"); runtime.args(["chroot", vm_dir, "qemu-m68k-static"]); runtime.arg(inside_vm_exe_path); runtime.current_dir(vm_parent_dir); commands.push(("Copy", copy)); commands.push(("Run-time", runtime)); } commands } else { let mut commands = vec![("Compiler", compiler)]; if test_mode.should_run() { let runtime = Command::new(exe); commands.push(("Run-time", runtime)); } commands } } #[derive(Clone, Copy)] enum BuildMode { Debug, Release, } impl BuildMode { fn is_debug(self) -> bool { matches!(self, Self::Debug) } } #[derive(Clone, Copy)] enum TestMode { Compile, CompileAndRun, } impl TestMode { fn should_run(self) -> bool { matches!(self, Self::CompileAndRun) } } fn build_test_runner( tempdir: PathBuf, current_dir: String, build_mode: BuildMode, test_kind: &str, test_dir: &str, test_mode: TestMode, files_to_ignore_on_m68k: &'static [&'static str], ) { fn rust_filter(path: &Path) -> bool { path.is_file() && path.extension().expect("extension").to_str().expect("to_str") == "rs" } #[cfg(feature = "master")] fn filter(filename: &Path) -> bool { rust_filter(filename) } #[cfg(not(feature = "master"))] fn filter(filename: &Path) -> bool { if let Some(filename) = filename.to_str() && filename.ends_with("gep.rs") { return false; } rust_filter(filename) } println!("=== {test_kind} tests ==="); // FIXME(antoyo): find a way to send this via a cli argument. let test_target = std::env::var("CG_GCC_TEST_TARGET").ok(); let test_target_filter = test_target.clone(); LangTester::new() .test_dir(test_dir) .test_path_filter(move |filename| { if !filter(filename) { return false; } if test_target_filter.is_some() && let Some(filename) = filename.file_name() && let Some(filename) = filename.to_str() && files_to_ignore_on_m68k.contains(&filename) { return false; } true }) .test_extract(|path| { std::fs::read_to_string(path) .expect("read file") .lines() .skip_while(|l| !l.starts_with("//")) .take_while(|l| l.starts_with("//")) .map(|l| &l[2..]) .collect::>() .join("\n") }) .test_cmds(move |path| { // Test command 1: Compile `x.rs` into `tempdir/x`. let mut exe = PathBuf::new(); exe.push(&tempdir); exe.push(path.file_stem().expect("file_stem")); let mut compiler_args = vec![ format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir), "--sysroot".into(), format!("{}/build/build_sysroot/sysroot/", current_dir), "-C".into(), "link-arg=-lc".into(), "--extern".into(), "mini_core=target/out/libmini_core.rlib".into(), "-o".into(), exe.to_str().expect("to_str").into(), path.to_str().expect("to_str").into(), ]; if let Some(ref target) = test_target { compiler_args.extend_from_slice(&["--target".into(), target.into()]); let linker = format!("{}-gcc", target); compiler_args.push(format!("-Clinker={}", linker)); } if let Some(flags) = option_env!("TEST_FLAGS") { for flag in flags.split_whitespace() { compiler_args.push(flag.into()); } } if build_mode.is_debug() { compiler_args .extend_from_slice(&["-C".to_string(), "llvm-args=sanitize-undefined".into()]); if test_target.is_none() { // m68k doesn't have lubsan for now compiler_args.extend_from_slice(&["-C".into(), "link-args=-lubsan".into()]); } } else { compiler_args.extend_from_slice(&[ "-C".into(), "opt-level=3".into(), "-C".into(), "lto=no".into(), ]); } compile_and_run_cmds(compiler_args, &test_target, &exe, test_mode) }) .run(); } fn compile_tests(tempdir: PathBuf, current_dir: String) { build_test_runner( tempdir, current_dir, BuildMode::Debug, "lang compile", "tests/compile", TestMode::Compile, &["simd-ffi.rs", "asm_nul_byte.rs", "global_asm_nul_byte.rs", "naked_asm_nul_byte.rs"], ); } fn run_tests(tempdir: PathBuf, current_dir: String) { build_test_runner( tempdir.clone(), current_dir.clone(), BuildMode::Debug, "[DEBUG] lang run", "tests/run", TestMode::CompileAndRun, &[], ); build_test_runner( tempdir, current_dir.to_string(), BuildMode::Release, "[RELEASE] lang run", "tests/run", TestMode::CompileAndRun, &[], ); } fn main() { let tempdir = TempDir::new().expect("temp dir"); let current_dir = current_dir().expect("current dir"); let current_dir = current_dir.to_str().expect("current dir").to_string(); let tempdir_path: PathBuf = tempdir.as_ref().into(); compile_tests(tempdir_path.clone(), current_dir.clone()); run_tests(tempdir_path, current_dir); }