| #![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<String>, | |
| test_target: &Option<String>, | |
| 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::<Vec<_>>() | |
| .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); | |
| } |