use crate::build; use crate::config::{Channel, ConfigInfo}; use crate::utils::{ get_toolchain, git_clone, git_clone_root_dir, remove_file, run_command, run_command_with_env, run_command_with_output_and_env, rustc_version_info, split_args, walk_dir, }; use std::collections::{BTreeSet, HashMap}; use std::ffi::OsStr; use std::fs::{create_dir_all, remove_dir_all, File}; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::str::FromStr; type Env = HashMap; type Runner = fn(&Env, &TestArg) -> Result<(), String>; type Runners = HashMap<&'static str, (&'static str, Runner)>; fn get_runners() -> Runners { let mut runners = HashMap::new(); runners.insert( "--test-rustc", ("Run all rustc tests", test_rustc as Runner), ); runners.insert( "--test-successful-rustc", ("Run successful rustc tests", test_successful_rustc), ); runners.insert( "--test-failing-rustc", ("Run failing rustc tests", test_failing_rustc), ); runners.insert( "--projects", ("Run the tests of popular crates", test_projects), ); runners.insert("--test-libcore", ("Run libcore tests", test_libcore)); runners.insert("--clean", ("Empty cargo target directory", clean)); runners.insert("--build-sysroot", ("Build sysroot", build_sysroot)); runners.insert("--std-tests", ("Run std tests", std_tests)); runners.insert("--asm-tests", ("Run asm tests", asm_tests)); runners.insert( "--extended-tests", ("Run extended sysroot tests", extended_sysroot_tests), ); runners.insert( "--extended-rand-tests", ("Run extended rand tests", extended_rand_tests), ); runners.insert( "--extended-regex-example-tests", ( "Run extended regex example tests", extended_regex_example_tests, ), ); runners.insert( "--extended-regex-tests", ("Run extended regex tests", extended_regex_tests), ); runners.insert("--mini-tests", ("Run mini tests", mini_tests)); runners } fn get_number_after_arg( args: &mut impl Iterator, option: &str, ) -> Result { match args.next() { Some(nb) if !nb.is_empty() => match usize::from_str(&nb) { Ok(nb) => Ok(nb), Err(_) => Err(format!( "Expected a number after `{}`, found `{}`", option, nb )), }, _ => Err(format!( "Expected a number after `{}`, found nothing", option )), } } fn show_usage() { println!( r#" `test` command help: --release : Build codegen in release mode --sysroot-panic-abort : Build the sysroot without unwinding support. --features [arg] : Add a new feature [arg] --use-system-gcc : Use system installed libgccjit --build-only : Only build rustc_codegen_gcc then exits --nb-parts : Used to split rustc_tests (for CI needs) --current-part : Used with `--nb-parts`, allows you to specify which parts to test"# ); ConfigInfo::show_usage(); for (option, (doc, _)) in get_runners() { // FIXME: Instead of using the hard-coded `23` value, better to compute it instead. let needed_spaces = 23_usize.saturating_sub(option.len()); let spaces: String = std::iter::repeat(' ').take(needed_spaces).collect(); println!(" {}{}: {}", option, spaces, doc); } println!(" --help : Show this help"); } #[derive(Default, Debug)] struct TestArg { build_only: bool, use_system_gcc: bool, runners: BTreeSet, flags: Vec, nb_parts: Option, current_part: Option, sysroot_panic_abort: bool, config_info: ConfigInfo, } impl TestArg { fn new() -> Result, String> { let mut test_arg = Self::default(); // We skip binary name and the `test` command. let mut args = std::env::args().skip(2); let runners = get_runners(); while let Some(arg) = args.next() { match arg.as_str() { "--features" => match args.next() { Some(feature) if !feature.is_empty() => { test_arg .flags .extend_from_slice(&["--features".into(), feature]); } _ => { return Err("Expected an argument after `--features`, found nothing".into()) } }, "--use-system-gcc" => { println!("Using system GCC"); test_arg.use_system_gcc = true; } "--build-only" => test_arg.build_only = true, "--nb-parts" => { test_arg.nb_parts = Some(get_number_after_arg(&mut args, "--nb-parts")?); } "--current-part" => { test_arg.current_part = Some(get_number_after_arg(&mut args, "--current-part")?); } "--sysroot-panic-abort" => { test_arg.sysroot_panic_abort = true; } "--help" => { show_usage(); return Ok(None); } x if runners.contains_key(x) => { test_arg.runners.insert(x.into()); } arg => { if !test_arg.config_info.parse_argument(arg, &mut args)? { return Err(format!("Unknown option {}", arg)); } } } } match (test_arg.current_part, test_arg.nb_parts) { (Some(_), Some(_)) | (None, None) => {} _ => { return Err( "If either `--current-part` or `--nb-parts` is specified, the other one \ needs to be specified as well!" .to_string(), ); } } if test_arg.config_info.no_default_features { test_arg.flags.push("--no-default-features".into()); } Ok(Some(test_arg)) } pub fn is_using_gcc_master_branch(&self) -> bool { !self.config_info.no_default_features } } fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> { if args.config_info.backend.is_some() { return Ok(()); } let mut command: Vec<&dyn AsRef> = vec![&"cargo", &"rustc"]; let mut tmp_env; let env = if args.config_info.channel == Channel::Release { tmp_env = env.clone(); tmp_env.insert("CARGO_INCREMENTAL".to_string(), "1".to_string()); command.push(&"--release"); &tmp_env } else { &env }; for flag in args.flags.iter() { command.push(flag); } run_command_with_output_and_env(&command, None, Some(env)) } fn clean(_env: &Env, args: &TestArg) -> Result<(), String> { let _ = std::fs::remove_dir_all(&args.config_info.cargo_target_dir); let path = Path::new(&args.config_info.cargo_target_dir).join("gccjit"); std::fs::create_dir_all(&path) .map_err(|error| format!("failed to create folder `{}`: {:?}", path.display(), error)) } fn mini_tests(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[BUILD] mini_core"); let crate_types = if args.config_info.host_triple != args.config_info.target_triple { "lib" } else { "lib,dylib" } .to_string(); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/mini_core.rs", &"--crate-name", &"mini_core", &"--crate-type", &crate_types, &"--target", &args.config_info.target_triple, ]); run_command_with_output_and_env(&command, None, Some(&env))?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[BUILD] example"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/example.rs", &"--crate-type", &"lib", &"--target", &args.config_info.target_triple, ]); run_command_with_output_and_env(&command, None, Some(&env))?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] mini_core_hello_world"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/mini_core_hello_world.rs", &"--crate-name", &"mini_core_hello_world", &"--crate-type", &"bin", &"-g", &"--target", &args.config_info.target_triple, ]); run_command_with_output_and_env(&command, None, Some(&env))?; let command: &[&dyn AsRef] = &[ &Path::new(&args.config_info.cargo_target_dir).join("mini_core_hello_world"), &"abc", &"bcd", ]; maybe_run_command_in_vm(&command, env, args)?; Ok(()) } fn build_sysroot(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[BUILD] sysroot"); build::build_sysroot(env, &args.config_info)?; Ok(()) } // TODO(GuillaumeGomez): when rewriting in Rust, refactor with the code in tests/lang_tests_common.rs if possible. fn maybe_run_command_in_vm( command: &[&dyn AsRef], env: &Env, args: &TestArg, ) -> Result<(), String> { if !args.config_info.run_in_vm { run_command_with_output_and_env(command, None, Some(env))?; return Ok(()); } let vm_parent_dir = match env.get("CG_GCC_VM_DIR") { Some(dir) if !dir.is_empty() => PathBuf::from(dir.clone()), _ => std::env::current_dir().unwrap(), }; let vm_dir = "vm"; let exe_to_run = command.first().unwrap(); let exe = Path::new(&exe_to_run); 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); let inside_vm_exe_path = Path::new("/home").join(exe_filename); let sudo_command: &[&dyn AsRef] = &[&"sudo", &"cp", &exe, &vm_exe_path]; run_command_with_env(sudo_command, None, Some(env))?; let mut vm_command: Vec<&dyn AsRef> = vec![ &"sudo", &"chroot", &vm_dir, &"qemu-m68k-static", &inside_vm_exe_path, ]; vm_command.extend_from_slice(command); run_command_with_output_and_env(&vm_command, Some(&vm_parent_dir), Some(env))?; Ok(()) } fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> { let cargo_target_dir = Path::new(&args.config_info.cargo_target_dir); // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] arbitrary_self_types_pointers_and_wrappers"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/arbitrary_self_types_pointers_and_wrappers.rs", &"--crate-name", &"arbitrary_self_types_pointers_and_wrappers", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm( &[&cargo_target_dir.join("arbitrary_self_types_pointers_and_wrappers")], env, args, )?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] alloc_system"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/alloc_system.rs", &"--crate-type", &"lib", &"--target", &args.config_info.target_triple, ]); if args.is_using_gcc_master_branch() { command.extend_from_slice(&[&"--cfg", &"feature=\"master\""]); } run_command_with_env(&command, None, Some(env))?; // FIXME: doesn't work on m68k. if args.config_info.host_triple == args.config_info.target_triple { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] alloc_example"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/alloc_example.rs", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm(&[&cargo_target_dir.join("alloc_example")], env, args)?; } // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] dst_field_align"); // FIXME(antoyo): Re-add -Zmir-opt-level=2 once rust-lang/rust#67529 is fixed. let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/dst-field-align.rs", &"--crate-name", &"dst_field_align", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm(&[&cargo_target_dir.join("dst_field_align")], env, args)?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] std_example"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/std_example.rs", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); if args.is_using_gcc_master_branch() { command.extend_from_slice(&[&"--cfg", &"feature=\"master\""]); } run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm( &[ &cargo_target_dir.join("std_example"), &"--target", &args.config_info.target_triple, ], env, args, )?; let test_flags = if let Some(test_flags) = env.get("TEST_FLAGS") { split_args(test_flags)? } else { Vec::new() }; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] subslice-patterns-const-eval"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/subslice-patterns-const-eval.rs", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); for test_flag in &test_flags { command.push(test_flag); } run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm( &[&cargo_target_dir.join("subslice-patterns-const-eval")], env, args, )?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] track-caller-attribute"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/track-caller-attribute.rs", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); for test_flag in &test_flags { command.push(test_flag); } run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm( &[&cargo_target_dir.join("track-caller-attribute")], env, args, )?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[AOT] mod_bench"); let mut command = args.config_info.rustc_command_vec(); command.extend_from_slice(&[ &"example/mod_bench.rs", &"--crate-type", &"bin", &"--target", &args.config_info.target_triple, ]); run_command_with_env(&command, None, Some(env))?; // FIXME: the compiled binary is not run. Ok(()) } fn setup_rustc(env: &mut Env, args: &TestArg) -> Result { let toolchain = format!( "+{channel}-{host}", channel = get_toolchain()?, // May also include date host = args.config_info.host_triple ); let rust_dir_path = Path::new(crate::BUILD_DIR).join("rust"); // If the repository was already cloned, command will fail, so doesn't matter. let _ = git_clone( "https://github.com/rust-lang/rust.git", Some(&rust_dir_path), false, ); let rust_dir: Option<&Path> = Some(&rust_dir_path); run_command(&[&"git", &"checkout", &"--", &"tests/"], rust_dir)?; run_command_with_output_and_env(&[&"git", &"fetch"], rust_dir, Some(env))?; let rustc_commit = match rustc_version_info(env.get("RUSTC").map(|s| s.as_str()))?.commit_hash { Some(commit_hash) => commit_hash, None => return Err("Couldn't retrieve rustc commit hash".to_string()), }; if rustc_commit != "unknown" { run_command_with_output_and_env( &[&"git", &"checkout", &rustc_commit], rust_dir, Some(env), )?; } else { run_command_with_output_and_env(&[&"git", &"checkout"], rust_dir, Some(env))?; } let cargo = String::from_utf8( run_command_with_env(&[&"rustup", &"which", &"cargo"], rust_dir, Some(env))?.stdout, ) .map_err(|error| format!("Failed to retrieve cargo path: {:?}", error)) .and_then(|cargo| { let cargo = cargo.trim().to_owned(); if cargo.is_empty() { Err(format!("`cargo` path is empty")) } else { Ok(cargo) } })?; let rustc = String::from_utf8( run_command_with_env( &[&"rustup", &toolchain, &"which", &"rustc"], rust_dir, Some(env), )? .stdout, ) .map_err(|error| format!("Failed to retrieve rustc path: {:?}", error)) .and_then(|rustc| { let rustc = rustc.trim().to_owned(); if rustc.is_empty() { Err(format!("`rustc` path is empty")) } else { Ok(rustc) } })?; let llvm_filecheck = match run_command_with_env( &[ &"bash", &"-c", &"which FileCheck-10 || \ which FileCheck-11 || \ which FileCheck-12 || \ which FileCheck-13 || \ which FileCheck-14", ], rust_dir, Some(env), ) { Ok(cmd) => String::from_utf8_lossy(&cmd.stdout).to_string(), Err(_) => { eprintln!("Failed to retrieve LLVM FileCheck, ignoring..."); String::new() } }; let file_path = rust_dir_path.join("config.toml"); std::fs::write( &file_path, &format!( r#"change-id = 115898 [rust] codegen-backends = [] deny-warnings = false verbose-tests = true [build] cargo = "{cargo}" local-rebuild = true rustc = "{rustc}" [target.x86_64-unknown-linux-gnu] llvm-filecheck = "{llvm_filecheck}" [llvm] download-ci-llvm = false "#, cargo = cargo, rustc = rustc, llvm_filecheck = llvm_filecheck.trim(), ), ) .map_err(|error| { format!( "Failed to write into `{}`: {:?}", file_path.display(), error ) })?; Ok(rust_dir_path) } fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { let mut env = env.clone(); let rust_dir = setup_rustc(&mut env, args)?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rustc asm test suite"); env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); let extra = if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; let rustc_args = &format!( r#"-Zpanic-abort-tests \ -Zcodegen-backend="{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}" \ --sysroot "{pwd}/build_sysroot/sysroot" -Cpanic=abort{extra}"#, pwd = std::env::current_dir() .map_err(|error| format!("`current_dir` failed: {:?}", error))? .display(), channel = args.config_info.channel.as_str(), dylib_ext = args.config_info.dylib_ext, extra = extra, ); run_command_with_env( &[ &"./x.py", &"test", &"--run", &"always", &"--stage", &"0", &"tests/assembly/asm", &"--rustc-args", &rustc_args, ], Some(&rust_dir), Some(&env), )?; Ok(()) } fn run_cargo_command( command: &[&dyn AsRef], cwd: Option<&Path>, env: &Env, args: &TestArg, ) -> Result<(), String> { run_cargo_command_with_callback(command, cwd, env, args, |cargo_command, cwd, env| { run_command_with_output_and_env(cargo_command, cwd, Some(env))?; Ok(()) }) } fn run_cargo_command_with_callback( command: &[&dyn AsRef], cwd: Option<&Path>, env: &Env, args: &TestArg, callback: F, ) -> Result<(), String> where F: Fn(&[&dyn AsRef], Option<&Path>, &Env) -> Result<(), String>, { let toolchain = get_toolchain()?; let toolchain_arg = format!("+{}", toolchain); let rustc_version = String::from_utf8( run_command_with_env(&[&args.config_info.rustc_command[0], &"-V"], cwd, Some(env))?.stdout, ) .map_err(|error| format!("Failed to retrieve rustc version: {:?}", error))?; let rustc_toolchain_version = String::from_utf8( run_command_with_env( &[&args.config_info.rustc_command[0], &toolchain_arg, &"-V"], cwd, Some(env), )? .stdout, ) .map_err(|error| format!("Failed to retrieve rustc +toolchain version: {:?}", error))?; if rustc_version != rustc_toolchain_version { eprintln!( "rustc_codegen_gcc is built for `{}` but the default rustc version is `{}`.", rustc_toolchain_version, rustc_version, ); eprintln!("Using `{}`.", rustc_toolchain_version); } let mut env = env.clone(); let rustflags = env.get("RUSTFLAGS").cloned().unwrap_or_default(); env.insert("RUSTDOCFLAGS".to_string(), rustflags); let mut cargo_command: Vec<&dyn AsRef> = vec![&"cargo", &toolchain_arg]; cargo_command.extend_from_slice(&command); callback(&cargo_command, cwd, &env) } // FIXME(antoyo): linker gives multiple definitions error on Linux // echo "[BUILD] sysroot in release mode" // ./build_sysroot/build_sysroot.sh --release fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { let projects = [ //"https://gitlab.gnome.org/GNOME/librsvg", // FIXME: doesn't compile in the CI since the // version of cairo and other libraries is too old. "https://github.com/rust-random/getrandom", "https://github.com/BurntSushi/memchr", "https://github.com/dtolnay/itoa", "https://github.com/rust-lang/cfg-if", "https://github.com/rust-lang-nursery/lazy-static.rs", //"https://github.com/marshallpierce/rust-base64", // FIXME: one test is OOM-killed. // TODO: ignore the base64 test that is OOM-killed. "https://github.com/time-rs/time", "https://github.com/rust-lang/log", "https://github.com/bitflags/bitflags", //"https://github.com/serde-rs/serde", // FIXME: one test fails. //"https://github.com/rayon-rs/rayon", // TODO: very slow, only run on master? //"https://github.com/rust-lang/cargo", // TODO: very slow, only run on master? ]; let run_tests = |projects_path, iter: &mut dyn Iterator| -> Result<(), String> { for project in iter { let clone_result = git_clone_root_dir(project, projects_path, true)?; let repo_path = Path::new(&clone_result.repo_dir); run_cargo_command(&[&"build", &"--release"], Some(repo_path), env, args)?; run_cargo_command(&[&"test"], Some(repo_path), env, args)?; } Ok(()) }; let projects_path = Path::new("projects"); create_dir_all(projects_path) .map_err(|err| format!("Failed to create directory `projects`: {}", err))?; let nb_parts = args.nb_parts.unwrap_or(0); if nb_parts > 0 { // We increment the number of tests by one because if this is an odd number, we would skip // one test. let count = projects.len() / nb_parts + 1; let current_part = args.current_part.unwrap(); let start = current_part * count; // We remove the projects we don't want to test. run_tests(projects_path, &mut projects.iter().skip(start).take(count))?; } else { run_tests(projects_path, &mut projects.iter())?; } Ok(()) } fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] libcore"); let path = Path::new("build_sysroot/sysroot_src/library/core/tests"); let _ = remove_dir_all(path.join("target")); run_cargo_command(&[&"test"], Some(path), env, args)?; Ok(()) } // echo "[BENCH COMPILE] mod_bench" // // COMPILE_MOD_BENCH_INLINE="$RUSTC example/mod_bench.rs --crate-type bin -Zmir-opt-level=3 -O --crate-name mod_bench_inline" // COMPILE_MOD_BENCH_LLVM_0="rustc example/mod_bench.rs --crate-type bin -Copt-level=0 -o $cargo_target_dir/mod_bench_llvm_0 -Cpanic=abort" // COMPILE_MOD_BENCH_LLVM_1="rustc example/mod_bench.rs --crate-type bin -Copt-level=1 -o $cargo_target_dir/mod_bench_llvm_1 -Cpanic=abort" // COMPILE_MOD_BENCH_LLVM_2="rustc example/mod_bench.rs --crate-type bin -Copt-level=2 -o $cargo_target_dir/mod_bench_llvm_2 -Cpanic=abort" // COMPILE_MOD_BENCH_LLVM_3="rustc example/mod_bench.rs --crate-type bin -Copt-level=3 -o $cargo_target_dir/mod_bench_llvm_3 -Cpanic=abort" // // Use 100 runs, because a single compilations doesn't take more than ~150ms, so it isn't very slow // hyperfine --runs ${COMPILE_RUNS:-100} "$COMPILE_MOD_BENCH_INLINE" "$COMPILE_MOD_BENCH_LLVM_0" "$COMPILE_MOD_BENCH_LLVM_1" "$COMPILE_MOD_BENCH_LLVM_2" "$COMPILE_MOD_BENCH_LLVM_3" // echo "[BENCH RUN] mod_bench" // hyperfine --runs ${RUN_RUNS:-10} $cargo_target_dir/mod_bench{,_inline} $cargo_target_dir/mod_bench_llvm_* fn extended_rand_tests(env: &Env, args: &TestArg) -> Result<(), String> { if !args.is_using_gcc_master_branch() { println!("Not using GCC master branch. Skipping `extended_rand_tests`."); return Ok(()); } let mut env = env.clone(); // newer aho_corasick versions throw a deprecation warning let rustflags = format!( "{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default() ); env.insert("RUSTFLAGS".to_string(), rustflags); let path = Path::new(crate::BUILD_DIR).join("rand"); run_cargo_command(&[&"clean"], Some(&path), &env, args)?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rust-random/rand"); run_cargo_command(&[&"test", &"--workspace"], Some(&path), &env, args)?; Ok(()) } fn extended_regex_example_tests(env: &Env, args: &TestArg) -> Result<(), String> { if !args.is_using_gcc_master_branch() { println!("Not using GCC master branch. Skipping `extended_regex_example_tests`."); return Ok(()); } let path = Path::new(crate::BUILD_DIR).join("regex"); run_cargo_command(&[&"clean"], Some(&path), env, args)?; // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rust-lang/regex example shootout-regex-dna"); let mut env = env.clone(); // newer aho_corasick versions throw a deprecation warning let rustflags = format!( "{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default() ); env.insert("RUSTFLAGS".to_string(), rustflags); // Make sure `[codegen mono items] start` doesn't poison the diff run_cargo_command( &[&"build", &"--example", &"shootout-regex-dna"], Some(&path), &env, args, )?; run_cargo_command_with_callback( &[&"run", &"--example", &"shootout-regex-dna"], Some(&path), &env, args, |cargo_command, cwd, env| { // FIXME: rewrite this with `child.stdin.write_all()` because // `examples/regexdna-input.txt` is very small. let mut command: Vec<&dyn AsRef> = vec![&"bash", &"-c"]; let cargo_args = cargo_command .iter() .map(|s| s.as_ref().to_str().unwrap()) .collect::>(); let bash_command = format!( "cat examples/regexdna-input.txt | {} | grep -v 'Spawned thread' > res.txt", cargo_args.join(" "), ); command.push(&bash_command); run_command_with_output_and_env(&command, cwd, Some(env))?; run_command_with_output_and_env( &[&"diff", &"-u", &"res.txt", &"examples/regexdna-output.txt"], cwd, Some(env), )?; Ok(()) }, )?; Ok(()) } fn extended_regex_tests(env: &Env, args: &TestArg) -> Result<(), String> { if !args.is_using_gcc_master_branch() { println!("Not using GCC master branch. Skipping `extended_regex_tests`."); return Ok(()); } // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rust-lang/regex tests"); let mut env = env.clone(); // newer aho_corasick versions throw a deprecation warning let rustflags = format!( "{} --cap-lints warn", env.get("RUSTFLAGS").cloned().unwrap_or_default() ); env.insert("RUSTFLAGS".to_string(), rustflags); let path = Path::new(crate::BUILD_DIR).join("regex"); run_cargo_command( &[ &"test", &"--tests", &"--", // FIXME: try removing `--exclude-should-panic` argument &"--exclude-should-panic", &"--test-threads", &"1", &"-Zunstable-options", &"-q", ], Some(&path), &env, args, )?; Ok(()) } fn extended_sysroot_tests(env: &Env, args: &TestArg) -> Result<(), String> { // pushd simple-raytracer // echo "[BENCH COMPILE] ebobby/simple-raytracer" // hyperfine --runs "${RUN_RUNS:-10}" --warmup 1 --prepare "cargo clean" \ // "RUSTC=rustc RUSTFLAGS='' cargo build" \ // "../y.sh cargo build" // echo "[BENCH RUN] ebobby/simple-raytracer" // cp ./target/debug/main ./raytracer_cg_gcc // hyperfine --runs "${RUN_RUNS:-10}" ./raytracer_cg_llvm ./raytracer_cg_gcc // popd extended_rand_tests(env, args)?; extended_regex_example_tests(env, args)?; extended_regex_tests(env, args)?; Ok(()) } fn should_not_remove_test(file: &str) -> bool { // contains //~ERROR, but shouldn't be removed [ "issues/auxiliary/issue-3136-a.rs", "type-alias-impl-trait/auxiliary/cross_crate_ice.rs", "type-alias-impl-trait/auxiliary/cross_crate_ice2.rs", "macros/rfc-2011-nicer-assert-messages/auxiliary/common.rs", "imports/ambiguous-1.rs", "imports/ambiguous-4-extern.rs", "entry-point/auxiliary/bad_main_functions.rs", ] .iter() .any(|to_ignore| file.ends_with(to_ignore)) } fn should_remove_test(file_path: &Path) -> Result { // Tests generating errors. let file = File::open(file_path) .map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?; for line in BufReader::new(file).lines().filter_map(|line| line.ok()) { let line = line.trim(); if line.is_empty() { continue; } if [ "//@ error-pattern:", "//@ build-fail", "//@ run-fail", "-Cllvm-args", "//~", "thread", ] .iter() .any(|check| line.contains(check)) { return Ok(true); } if line.contains("//[") && line.contains("]~") { return Ok(true); } } if file_path .display() .to_string() .contains("ambiguous-4-extern.rs") { eprintln!("nothing found for {file_path:?}"); } Ok(false) } fn test_rustc_inner(env: &Env, args: &TestArg, prepare_files_callback: F) -> Result<(), String> where F: Fn(&Path) -> Result, { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rust-lang/rust"); let mut env = env.clone(); let rust_path = setup_rustc(&mut env, args)?; walk_dir( rust_path.join("tests/ui"), |dir| { let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or(""); if [ "abi", "extern", "unsized-locals", "proc-macro", "threads-sendsync", "borrowck", "test-attrs", ] .iter() .any(|name| *name == dir_name) { std::fs::remove_dir_all(dir).map_err(|error| { format!("Failed to remove folder `{}`: {:?}", dir.display(), error) })?; } Ok(()) }, |_| Ok(()), )?; // These two functions are used to remove files that are known to not be working currently // with the GCC backend to reduce noise. fn dir_handling(dir: &Path) -> Result<(), String> { if dir .file_name() .map(|name| name == "auxiliary") .unwrap_or(true) { return Ok(()); } walk_dir(dir, dir_handling, file_handling) } fn file_handling(file_path: &Path) -> Result<(), String> { if !file_path .extension() .map(|extension| extension == "rs") .unwrap_or(false) { return Ok(()); } let path_str = file_path.display().to_string().replace("\\", "/"); if should_not_remove_test(&path_str) { return Ok(()); } else if should_remove_test(file_path)? { return remove_file(&file_path); } Ok(()) } remove_file(&rust_path.join("tests/ui/consts/const_cmp_type_id.rs"))?; remove_file(&rust_path.join("tests/ui/consts/issue-73976-monomorphic.rs"))?; // this test is oom-killed in the CI. remove_file(&rust_path.join("tests/ui/consts/issue-miri-1910.rs"))?; // Tests generating errors. remove_file(&rust_path.join("tests/ui/consts/issue-94675.rs"))?; remove_file(&rust_path.join("tests/ui/mir/mir_heavy_promoted.rs"))?; remove_file(&rust_path.join("tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs"))?; remove_file(&rust_path.join("tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs"))?; walk_dir(rust_path.join("tests/ui"), dir_handling, file_handling)?; if !prepare_files_callback(&rust_path)? { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("Keeping all UI tests"); } let nb_parts = args.nb_parts.unwrap_or(0); if nb_parts > 0 { let current_part = args.current_part.unwrap(); // FIXME: create a function "display_if_not_quiet" or something along the line. println!( "Splitting ui_test into {} parts (and running part {})", nb_parts, current_part ); let out = String::from_utf8( run_command( &[ &"find", &"tests/ui", &"-type", &"f", &"-name", &"*.rs", &"-not", &"-path", &"*/auxiliary/*", ], Some(&rust_path), )? .stdout, ) .map_err(|error| format!("Failed to retrieve output of find command: {:?}", error))?; let mut files = out .split('\n') .map(|line| line.trim()) .filter(|line| !line.is_empty()) .collect::>(); // To ensure it'll be always the same sub files, we sort the content. files.sort(); // We increment the number of tests by one because if this is an odd number, we would skip // one test. let count = files.len() / nb_parts + 1; let start = current_part * count; // We remove the files we don't want to test. for path in files.iter().skip(start).take(count) { remove_file(&rust_path.join(path))?; } } // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rustc test suite"); env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); let extra = if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; let rustc_args = format!( "{} -Zcodegen-backend={} --sysroot {}{}", env.get("TEST_FLAGS").unwrap_or(&String::new()), args.config_info.cg_backend_path, args.config_info.sysroot_path, extra, ); env.get_mut("RUSTFLAGS").unwrap().clear(); run_command_with_output_and_env( &[ &"./x.py", &"test", &"--run", &"always", &"--stage", &"0", &"tests/ui", &"--rustc-args", &rustc_args, ], Some(&rust_path), Some(&env), )?; Ok(()) } fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> { test_rustc_inner(env, args, |_| Ok(false)) } fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { test_rustc_inner(env, args, |rust_path| { // Removing all tests. run_command( &[ &"find", &"tests/ui", &"-type", &"f", &"-name", &"*.rs", &"-not", &"-path", &"*/auxiliary/*", &"-delete", ], Some(rust_path), )?; // Putting back only the failing ones. let path = "tests/failing-ui-tests.txt"; if let Ok(files) = std::fs::read_to_string(path) { for file in files .split('\n') .map(|line| line.trim()) .filter(|line| !line.is_empty()) { run_command(&[&"git", &"checkout", &"--", &file], Some(&rust_path))?; } } else { println!( "Failed to read `{}`, not putting back failing ui tests", path ); } Ok(true) }) } fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { test_rustc_inner(env, args, |rust_path| { // Removing the failing tests. let path = "tests/failing-ui-tests.txt"; if let Ok(files) = std::fs::read_to_string(path) { for file in files .split('\n') .map(|line| line.trim()) .filter(|line| !line.is_empty()) { let path = rust_path.join(file); remove_file(&path)?; } } else { println!( "Failed to read `{}`, not putting back failing ui tests", path ); } Ok(true) }) } fn run_all(env: &Env, args: &TestArg) -> Result<(), String> { clean(env, args)?; mini_tests(env, args)?; build_sysroot(env, args)?; std_tests(env, args)?; // asm_tests(env, args)?; test_libcore(env, args)?; extended_sysroot_tests(env, args)?; test_rustc(env, args)?; Ok(()) } pub fn run() -> Result<(), String> { let mut args = match TestArg::new()? { Some(args) => args, None => return Ok(()), }; let mut env: HashMap = std::env::vars().collect(); if !args.use_system_gcc { args.config_info.setup_gcc_path()?; env.insert( "LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone(), ); env.insert( "LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone(), ); } build_if_no_backend(&env, &args)?; if args.build_only { println!("Since it's build only, exiting..."); return Ok(()); } args.config_info.setup(&mut env, args.use_system_gcc)?; if args.runners.is_empty() { run_all(&env, &args)?; } else { let runners = get_runners(); for runner in args.runners.iter() { runners.get(runner.as_str()).unwrap().1(&env, &args)?; } } Ok(()) }