1//! Build-and-run steps for `./x.py test` test fixtures2//!3//! `./x.py test` (aka [`Kind::Test`]) is currently allowed to reach build steps in other modules.4//! However, this contains ~all test parts we expect people to be able to build and run locally.56// (This file should be split up, but having tidy block all changes is not helpful.)7// ignore-tidy-filelength89use std::collections::HashSet;10use std::env::split_paths;11use std::ffi::{OsStr, OsString};12use std::path::{Path, PathBuf};13use std::{env, fs, iter};1415use build_helper::exit;1617use crate::core::build_steps::compile::{ArtifactKeepMode, Std, run_cargo};18use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler};19use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags};20use crate::core::build_steps::llvm::get_llvm_version;21use crate::core::build_steps::run::{get_completion_paths, get_help_path};22use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;23use crate::core::build_steps::test::compiletest::CompiletestMode;24use crate::core::build_steps::tool::{25 self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool,26 ToolTargetBuildMode, get_tool_target_compiler,27};28use crate::core::build_steps::toolstate::ToolState;29use crate::core::build_steps::{compile, dist, llvm};30use crate::core::builder::{31 self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, StepMetadata,32 crate_description,33};34use crate::core::config::TargetSelection;35use crate::core::config::flags::{Subcommand, get_completion, top_level_help};36use crate::core::{android, debuggers};37use crate::utils::build_stamp::{self, BuildStamp};38use crate::utils::exec::{BootstrapCommand, command};39use crate::utils::helpers::{40 self, LldThreads, TestFilterCategory, add_dylib_path, add_rustdoc_cargo_linker_args,41 dylib_path, dylib_path_var, linker_args, linker_flags, t, target_supports_cranelift_backend,42 up_to_date,43};44use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};45use crate::{CLang, CodegenBackendKind, GitRepo, Mode, PathSet, TestTarget, envify};4647mod compiletest;4849/// Runs `cargo test` on various internal tools used by bootstrap.50#[derive(Debug, Clone, PartialEq, Eq, Hash)]51pub struct CrateBootstrap {52 path: PathBuf,53 host: TargetSelection,54}5556impl Step for CrateBootstrap {57 type Output = ();58 const IS_HOST: bool = true;5960 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {61 // This step is responsible for several different tool paths.62 //63 // By default, it will test all of them, but requesting specific tools on the command-line64 // (e.g. `./x test src/tools/coverage-dump`) will test only the specified tools.65 run.path("src/tools/jsondoclint")66 .path("src/tools/replace-version-placeholder")67 .path("src/tools/coverage-dump")68 // We want `./x test tidy` to _run_ the tidy tool, not its tests.69 // So we need a separate alias to test the tidy tool itself.70 .alias("tidyselftest")71 }7273 fn is_default_step(_builder: &Builder<'_>) -> bool {74 true75 }7677 fn make_run(run: RunConfig<'_>) {78 // Create and ensure a separate instance of this step for each path79 // that was selected on the command-line (or selected by default).80 for path in run.paths {81 let path = path.assert_single_path().path.clone();82 run.builder.ensure(CrateBootstrap { host: run.target, path });83 }84 }8586 fn run(self, builder: &Builder<'_>) {87 let bootstrap_host = builder.config.host_target;88 let compiler = builder.compiler(0, bootstrap_host);89 let mut path = self.path.to_str().unwrap();9091 // Map alias `tidyselftest` back to the actual crate path of tidy.92 if path == "tidyselftest" {93 path = "src/tools/tidy";94 }9596 let cargo = tool::prepare_tool_cargo(97 builder,98 compiler,99 Mode::ToolBootstrap,100 bootstrap_host,101 Kind::Test,102 path,103 SourceType::InTree,104 &[],105 );106107 let crate_name = path.rsplit_once('/').unwrap().1;108 run_cargo_test(cargo, &[], &[], crate_name, bootstrap_host, builder);109 }110111 fn metadata(&self) -> Option<StepMetadata> {112 Some(113 StepMetadata::test("crate-bootstrap", self.host)114 .with_metadata(self.path.as_path().to_string_lossy().to_string()),115 )116 }117}118119#[derive(Debug, Clone, PartialEq, Eq, Hash)]120pub struct Linkcheck {121 host: TargetSelection,122}123124impl Step for Linkcheck {125 type Output = ();126 const IS_HOST: bool = true;127128 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {129 run.path("src/tools/linkchecker")130 }131132 fn is_default_step(builder: &Builder<'_>) -> bool {133 builder.config.docs134 }135136 fn make_run(run: RunConfig<'_>) {137 run.builder.ensure(Linkcheck { host: run.target });138 }139140 /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.141 ///142 /// This tool in `src/tools` will verify the validity of all our links in the143 /// documentation to ensure we don't have a bunch of dead ones.144 fn run(self, builder: &Builder<'_>) {145 let host = self.host;146 let hosts = &builder.hosts;147 let targets = &builder.targets;148149 // if we have different hosts and targets, some things may be built for150 // the host (e.g. rustc) and others for the target (e.g. std). The151 // documentation built for each will contain broken links to152 // docs built for the other platform (e.g. rustc linking to cargo)153 if (hosts != targets) && !hosts.is_empty() && !targets.is_empty() {154 panic!(155 "Linkcheck currently does not support builds with different hosts and targets.156You can skip linkcheck with --skip src/tools/linkchecker"157 );158 }159160 builder.info(&format!("Linkcheck ({host})"));161162 // Test the linkchecker itself.163 let bootstrap_host = builder.config.host_target;164 let compiler = builder.compiler(0, bootstrap_host);165166 let cargo = tool::prepare_tool_cargo(167 builder,168 compiler,169 Mode::ToolBootstrap,170 bootstrap_host,171 Kind::Test,172 "src/tools/linkchecker",173 SourceType::InTree,174 &[],175 );176 run_cargo_test(cargo, &[], &[], "linkchecker self tests", bootstrap_host, builder);177178 if !builder.test_target.runs_doctests() {179 return;180 }181182 // Build all the default documentation.183 builder.run_default_doc_steps();184185 // Build the linkchecker before calling `msg`, since GHA doesn't support nested groups.186 let linkchecker = builder.tool_cmd(Tool::Linkchecker);187188 // Run the linkchecker.189 let _guard = builder.msg_test("Linkcheck", bootstrap_host, 1);190 let _time = helpers::timeit(builder);191 linkchecker.delay_failure().arg(builder.out.join(host).join("doc")).run(builder);192 }193194 fn metadata(&self) -> Option<StepMetadata> {195 Some(StepMetadata::test("link-check", self.host))196 }197}198199fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool {200 command("tidy")201 .allow_failure()202 .arg("--version")203 // Cache the output to avoid running this command more than once (per builder).204 .cached()205 .run_capture_stdout(builder)206 .is_success()207}208209#[derive(Debug, Clone, PartialEq, Eq, Hash)]210pub struct HtmlCheck {211 target: TargetSelection,212}213214impl Step for HtmlCheck {215 type Output = ();216 const IS_HOST: bool = true;217218 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {219 run.path("src/tools/html-checker")220 }221222 fn is_default_step(builder: &Builder<'_>) -> bool {223 check_if_tidy_is_installed(builder)224 }225226 fn make_run(run: RunConfig<'_>) {227 run.builder.ensure(HtmlCheck { target: run.target });228 }229230 fn run(self, builder: &Builder<'_>) {231 if !check_if_tidy_is_installed(builder) {232 eprintln!("not running HTML-check tool because `tidy` is missing");233 eprintln!(234 "You need the HTML tidy tool https://www.html-tidy.org/, this tool is *not* part of the rust project and needs to be installed separately, for example via your package manager."235 );236 panic!("Cannot run html-check tests");237 }238 // Ensure that a few different kinds of documentation are available.239 builder.run_default_doc_steps();240 builder.ensure(crate::core::build_steps::doc::Rustc::for_stage(241 builder,242 builder.top_stage,243 self.target,244 ));245246 builder247 .tool_cmd(Tool::HtmlChecker)248 .delay_failure()249 .arg(builder.doc_out(self.target))250 .run(builder);251 }252253 fn metadata(&self) -> Option<StepMetadata> {254 Some(StepMetadata::test("html-check", self.target))255 }256}257258/// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out259/// some representative crate repositories and runs `cargo test` on them, in260/// order to test cargo.261#[derive(Debug, Clone, PartialEq, Eq, Hash)]262pub struct Cargotest {263 build_compiler: Compiler,264 host: TargetSelection,265}266267impl Step for Cargotest {268 type Output = ();269 const IS_HOST: bool = true;270271 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {272 run.path("src/tools/cargotest")273 }274275 fn make_run(run: RunConfig<'_>) {276 if run.builder.top_stage == 0 {277 eprintln!(278 "ERROR: running cargotest with stage 0 is currently unsupported. Use at least stage 1."279 );280 exit!(1);281 }282 // We want to build cargo stage N (where N == top_stage), and rustc stage N,283 // and test both of these together.284 // So we need to get a build compiler stage N-1 to build the stage N components.285 run.builder.ensure(Cargotest {286 build_compiler: run.builder.compiler(run.builder.top_stage - 1, run.target),287 host: run.target,288 });289 }290291 /// Runs the `cargotest` tool as compiled in `stage` by the `host` compiler.292 ///293 /// This tool in `src/tools` will check out a few Rust projects and run `cargo294 /// test` to ensure that we don't regress the test suites there.295 fn run(self, builder: &Builder<'_>) {296 // cargotest's staging has several pieces:297 // consider ./x test cargotest --stage=2.298 //299 // The test goal is to exercise a (stage 2 cargo, stage 2 rustc) pair through a stage 2300 // cargotest tool.301 // To produce the stage 2 cargo and cargotest, we need to do so with the stage 1 rustc and std.302 // Importantly, the stage 2 rustc being tested (`tested_compiler`) via stage 2 cargotest is303 // the rustc built by an earlier stage 1 rustc (the build_compiler). These are two different304 // compilers!305 let cargo =306 builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host));307 let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host);308 builder.std(tested_compiler, self.host);309310 // Note that this is a short, cryptic, and not scoped directory name. This311 // is currently to minimize the length of path on Windows where we otherwise312 // quickly run into path name limit constraints.313 let out_dir = builder.out.join("ct");314 t!(fs::create_dir_all(&out_dir));315316 let _time = helpers::timeit(builder);317 let mut cmd = builder.tool_cmd(Tool::CargoTest);318 cmd.arg(&cargo.tool_path)319 .arg(&out_dir)320 .args(builder.config.test_args())321 .env("RUSTC", builder.rustc(tested_compiler))322 .env("RUSTDOC", builder.rustdoc_for_compiler(tested_compiler));323 add_rustdoc_cargo_linker_args(&mut cmd, builder, tested_compiler.host, LldThreads::No);324 cmd.delay_failure().run(builder);325 }326327 fn metadata(&self) -> Option<StepMetadata> {328 Some(StepMetadata::test("cargotest", self.host).stage(self.build_compiler.stage + 1))329 }330}331332/// Runs `cargo test` for cargo itself.333/// We label these tests as "cargo self-tests".334#[derive(Debug, Clone, PartialEq, Eq, Hash)]335pub struct Cargo {336 build_compiler: Compiler,337 host: TargetSelection,338}339340impl Cargo {341 const CRATE_PATH: &str = "src/tools/cargo";342}343344impl Step for Cargo {345 type Output = ();346 const IS_HOST: bool = true;347348 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {349 run.path(Self::CRATE_PATH)350 }351352 fn make_run(run: RunConfig<'_>) {353 run.builder.ensure(Cargo {354 build_compiler: get_tool_target_compiler(355 run.builder,356 ToolTargetBuildMode::Build(run.target),357 ),358 host: run.target,359 });360 }361362 /// Runs `cargo test` for `cargo` packaged with Rust.363 fn run(self, builder: &Builder<'_>) {364 // When we do a "stage 1 cargo self-test", it means that we test the stage 1 rustc365 // using stage 1 cargo. So we actually build cargo using the stage 0 compiler, and then366 // run its tests against the stage 1 compiler (called `tested_compiler` below).367 builder.ensure(tool::Cargo::from_build_compiler(self.build_compiler, self.host));368369 let tested_compiler = builder.compiler(self.build_compiler.stage + 1, self.host);370 builder.std(tested_compiler, self.host);371 // We also need to build rustdoc for cargo tests372 // It will be located in the bindir of `tested_compiler`, so we don't need to explicitly373 // pass its path to Cargo.374 builder.rustdoc_for_compiler(tested_compiler);375376 let cargo = tool::prepare_tool_cargo(377 builder,378 self.build_compiler,379 Mode::ToolTarget,380 self.host,381 Kind::Test,382 Self::CRATE_PATH,383 SourceType::Submodule,384 &[],385 );386387 // NOTE: can't use `run_cargo_test` because we need to overwrite `PATH`388 let mut cargo = prepare_cargo_test(cargo, &[], &[], self.host, builder);389390 // Don't run cross-compile tests, we may not have cross-compiled libstd libs391 // available.392 cargo.env("CFG_DISABLE_CROSS_TESTS", "1");393 // Forcibly disable tests using nightly features since any changes to394 // those features won't be able to land.395 cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1");396397 // Configure PATH to find the right rustc. NB. we have to use PATH398 // and not RUSTC because the Cargo test suite has tests that will399 // fail if rustc is not spelled `rustc`.400 cargo.env("PATH", bin_path_for_cargo(builder, tested_compiler));401402 // The `cargo` command configured above has dylib dir path set to the `build_compiler`'s403 // libdir. That causes issues in cargo test, because the programs that cargo compiles are404 // incorrectly picking that libdir, even though they should be picking the405 // `tested_compiler`'s libdir. We thus have to override the precedence here.406 let mut existing_dylib_paths = cargo407 .get_envs()408 .find(|(k, _)| *k == OsStr::new(dylib_path_var()))409 .and_then(|(_, v)| v)410 .map(|value| split_paths(value).collect::<Vec<PathBuf>>())411 .unwrap_or_default();412 existing_dylib_paths.insert(0, builder.rustc_libdir(tested_compiler));413 add_dylib_path(existing_dylib_paths, &mut cargo);414415 // Cargo's test suite uses `CARGO_RUSTC_CURRENT_DIR` to determine the path that `file!` is416 // relative to. Cargo no longer sets this env var, so we have to do that. This has to be the417 // same value as `-Zroot-dir`.418 cargo.env("CARGO_RUSTC_CURRENT_DIR", builder.src.display().to_string());419420 #[cfg(feature = "build-metrics")]421 builder.metrics.begin_test_suite(422 build_helper::metrics::TestSuiteMetadata::CargoPackage {423 crates: vec!["cargo".into()],424 target: self.host.triple.to_string(),425 host: self.host.triple.to_string(),426 stage: self.build_compiler.stage + 1,427 },428 builder,429 );430431 let _time = helpers::timeit(builder);432 add_flags_and_try_run_tests(builder, &mut cargo);433 }434435 fn metadata(&self) -> Option<StepMetadata> {436 Some(StepMetadata::test("cargo", self.host).built_by(self.build_compiler))437 }438}439440#[derive(Debug, Clone, PartialEq, Eq, Hash)]441pub struct RustAnalyzer {442 compilers: RustcPrivateCompilers,443}444445impl Step for RustAnalyzer {446 type Output = ();447 const IS_HOST: bool = true;448449 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {450 run.path("src/tools/rust-analyzer")451 }452453 fn is_default_step(_builder: &Builder<'_>) -> bool {454 true455 }456457 fn make_run(run: RunConfig<'_>) {458 run.builder.ensure(Self {459 compilers: RustcPrivateCompilers::new(460 run.builder,461 run.builder.top_stage,462 run.builder.host_target,463 ),464 });465 }466467 /// Runs `cargo test` for rust-analyzer468 fn run(self, builder: &Builder<'_>) {469 let build_compiler = self.compilers.build_compiler();470 let target = self.compilers.target();471472 // NOTE: rust-analyzer repo currently (as of 2025-12-11) does not run tests against 32-bit473 // targets, so we also don't run them in rust-lang/rust CI (because that will just mean that474 // subtree syncs will keep getting 32-bit-specific failures that are not observed in475 // rust-analyzer repo CI).476 //477 // Some 32-bit specific failures include e.g. target pointer width specific hashes.478479 // FIXME: eventually, we should probably reduce the amount of target tuple substring480 // matching in bootstrap.481 if target.starts_with("i686") {482 return;483 }484485 let mut cargo = tool::prepare_tool_cargo(486 builder,487 build_compiler,488 Mode::ToolRustcPrivate,489 target,490 Kind::Test,491 "src/tools/rust-analyzer",492 SourceType::InTree,493 &["in-rust-tree".to_owned()],494 );495 cargo.allow_features(tool::RustAnalyzer::ALLOW_FEATURES);496497 // N.B. it turns out _setting_ `CARGO_WORKSPACE_DIR` actually somehow breaks `expect-test`,498 // even though previously we actually needed to set that hack to allow `expect-test` to499 // correctly discover the r-a workspace instead of the outer r-l/r workspace.500501 // FIXME: RA's test suite tries to write to the source directory, that can't work in Rust CI502 // without properly wiring up the writable test dir.503 cargo.env("SKIP_SLOW_TESTS", "1");504505 // NOTE: we need to skip `src/tools/rust-analyzer/xtask` as they seem to exercise rustup /506 // stable rustfmt.507 //508 // NOTE: you can only skip a specific workspace package via `--exclude=...` if you *also*509 // specify `--workspace`.510 cargo.arg("--workspace");511 cargo.arg("--exclude=xtask");512513 if build_compiler.stage == 0 {514 // This builds a proc macro against the bootstrap libproc_macro, which is not ABI515 // compatible with the ABI proc-macro-srv expects to load.516 cargo.arg("--exclude=proc-macro-srv");517 cargo.arg("--exclude=proc-macro-srv-cli");518 }519520 let mut skip_tests = vec![];521522 // NOTE: the following test skips is a bit cheeky in that it assumes there are no523 // identically named tests across different r-a packages, where we want to run the524 // identically named test in one package but not another. If we want to support that use525 // case, we'd have to run the r-a tests in two batches (with one excluding the package that526 // we *don't* want to run the test for, and the other batch including).527528 // Across all platforms.529 skip_tests.extend_from_slice(&[530 // FIXME: this test wants to find a `rustc`. We need to provide it with a path to staged531 // in-tree `rustc`, but setting `RUSTC` env var requires some reworking of bootstrap.532 "tests::smoke_test_real_sysroot_cargo",533 // NOTE: part of `smol-str` test suite; this tries to access a stable rustfmt from the534 // environment, which is not something we want to do.535 "check_code_formatting",536 ]);537538 let skip_tests = skip_tests.iter().map(|name| format!("--skip={name}")).collect::<Vec<_>>();539 let skip_tests = skip_tests.iter().map(|s| s.as_str()).collect::<Vec<_>>();540541 cargo.add_rustc_lib_path(builder);542 run_cargo_test(cargo, skip_tests.as_slice(), &[], "rust-analyzer", target, builder);543 }544545 fn metadata(&self) -> Option<StepMetadata> {546 Some(547 StepMetadata::test("rust-analyzer", self.compilers.target())548 .built_by(self.compilers.build_compiler()),549 )550 }551}552553/// Runs `cargo test` for rustfmt.554#[derive(Debug, Clone, PartialEq, Eq, Hash)]555pub struct Rustfmt {556 compilers: RustcPrivateCompilers,557}558559impl Step for Rustfmt {560 type Output = ();561 const IS_HOST: bool = true;562563 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {564 run.path("src/tools/rustfmt")565 }566567 fn make_run(run: RunConfig<'_>) {568 run.builder.ensure(Rustfmt {569 compilers: RustcPrivateCompilers::new(570 run.builder,571 run.builder.top_stage,572 run.builder.host_target,573 ),574 });575 }576577 /// Runs `cargo test` for rustfmt.578 fn run(self, builder: &Builder<'_>) {579 let build_compiler = self.compilers.build_compiler();580 let target = self.compilers.target();581582 let mut cargo = tool::prepare_tool_cargo(583 builder,584 build_compiler,585 Mode::ToolRustcPrivate,586 target,587 Kind::Test,588 "src/tools/rustfmt",589 SourceType::InTree,590 &[],591 );592593 let dir = testdir(builder, target);594 t!(fs::create_dir_all(&dir));595 cargo.env("RUSTFMT_TEST_DIR", dir);596597 cargo.add_rustc_lib_path(builder);598599 run_cargo_test(cargo, &[], &[], "rustfmt", target, builder);600 }601602 fn metadata(&self) -> Option<StepMetadata> {603 Some(604 StepMetadata::test("rustfmt", self.compilers.target())605 .built_by(self.compilers.build_compiler()),606 )607 }608}609610#[derive(Debug, Clone, PartialEq, Eq, Hash)]611pub struct Miri {612 target: TargetSelection,613}614615impl Miri {616 /// Run `cargo miri setup` for the given target, return where the Miri sysroot was put.617 pub fn build_miri_sysroot(618 builder: &Builder<'_>,619 compiler: Compiler,620 target: TargetSelection,621 ) -> PathBuf {622 let miri_sysroot = builder.out.join(compiler.host).join("miri-sysroot");623 let mut cargo = builder::Cargo::new(624 builder,625 compiler,626 Mode::Std,627 SourceType::Submodule,628 target,629 Kind::MiriSetup,630 );631632 // Tell `cargo miri setup` where to find the sources.633 cargo.env("MIRI_LIB_SRC", builder.src.join("library"));634 // Tell it where to put the sysroot.635 cargo.env("MIRI_SYSROOT", &miri_sysroot);636637 let mut cargo = BootstrapCommand::from(cargo);638 let _guard =639 builder.msg(Kind::Build, "miri sysroot", Mode::ToolRustcPrivate, compiler, target);640 cargo.run(builder);641642 // # Determine where Miri put its sysroot.643 // To this end, we run `cargo miri setup --print-sysroot` and capture the output.644 // (We do this separately from the above so that when the setup actually645 // happens we get some output.)646 // We re-use the `cargo` from above.647 cargo.arg("--print-sysroot");648649 builder.do_if_verbose(|| println!("running: {cargo:?}"));650 let stdout = cargo.run_capture_stdout(builder).stdout();651 // Output is "<sysroot>\n".652 let sysroot = stdout.trim_end();653 builder.do_if_verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));654 PathBuf::from(sysroot)655 }656}657658impl Step for Miri {659 type Output = ();660661 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {662 run.path("src/tools/miri")663 }664665 fn make_run(run: RunConfig<'_>) {666 run.builder.ensure(Miri { target: run.target });667 }668669 /// Runs `cargo test` for miri.670 fn run(self, builder: &Builder<'_>) {671 let host = builder.build.host_target;672 let target = self.target;673 let stage = builder.top_stage;674 if stage == 0 {675 eprintln!("miri cannot be tested at stage 0");676 std::process::exit(1);677 }678679 // This compiler runs on the host, we'll just use it for the target.680 let compilers = RustcPrivateCompilers::new(builder, stage, host);681682 // Build our tools.683 let miri = builder.ensure(tool::Miri::from_compilers(compilers));684 // the ui tests also assume cargo-miri has been built685 builder.ensure(tool::CargoMiri::from_compilers(compilers));686687 let target_compiler = compilers.target_compiler();688689 // We also need sysroots, for Miri and for the host (the latter for build scripts).690 // This is for the tests so everything is done with the target compiler.691 let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target);692 builder.std(target_compiler, host);693 let host_sysroot = builder.sysroot(target_compiler);694695 // Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared when696 // the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors.697 if !builder.config.dry_run() {698 // This has to match `CARGO_TARGET_TMPDIR` in Miri's `ui.rs`.699 // This means we need `host` here as that's the target `ui.rs` is built for.700 let ui_test_dep_dir = builder701 .stage_out(miri.build_compiler, Mode::ToolStd)702 .join(host)703 .join("tmp")704 .join("miri_ui");705 // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see706 // <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>).707 // We can hence use that directly as a signal to clear the ui test dir.708 build_stamp::clear_if_dirty(builder, &ui_test_dep_dir, &miri_sysroot);709 }710711 // Run `cargo test`.712 // This is with the Miri crate, so it uses the host compiler.713 let mut cargo = tool::prepare_tool_cargo(714 builder,715 miri.build_compiler,716 Mode::ToolRustcPrivate,717 host,718 Kind::Test,719 "src/tools/miri",720 SourceType::InTree,721 &[],722 );723724 cargo.add_rustc_lib_path(builder);725726 // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test727 // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`.728 let mut cargo = prepare_cargo_test(cargo, &[], &[], host, builder);729730 // miri tests need to know about the stage sysroot731 cargo.env("MIRI_SYSROOT", &miri_sysroot);732 cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);733734 // Set the target.735 cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());736737 {738 let _guard = builder.msg_test("miri", target, target_compiler.stage);739 let _time = helpers::timeit(builder);740 cargo.run(builder);741 }742743 // Run it again for mir-opt-level 4 to catch some miscompilations.744 if builder.config.test_args().is_empty() {745 cargo.env(746 "MIRIFLAGS",747 format!(748 "{} -O -Zmir-opt-level=4 -Cdebug-assertions=yes",749 env::var("MIRIFLAGS").unwrap_or_default()750 ),751 );752 // Optimizations can change backtraces753 cargo.env("MIRI_SKIP_UI_CHECKS", "1");754 // `MIRI_SKIP_UI_CHECKS` and `RUSTC_BLESS` are incompatible755 cargo.env_remove("RUSTC_BLESS");756 // Optimizations can change error locations and remove UB so don't run `fail` tests.757 cargo.args(["tests/pass", "tests/panic"]);758759 {760 let _guard =761 builder.msg_test("miri (mir-opt-level 4)", target, target_compiler.stage);762 let _time = helpers::timeit(builder);763 cargo.run(builder);764 }765 }766 }767}768769/// Runs `cargo miri test` to demonstrate that `src/tools/miri/cargo-miri`770/// works and that libtest works under miri.771#[derive(Debug, Clone, PartialEq, Eq, Hash)]772pub struct CargoMiri {773 target: TargetSelection,774}775776impl Step for CargoMiri {777 type Output = ();778779 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {780 run.path("src/tools/miri/cargo-miri")781 }782783 fn make_run(run: RunConfig<'_>) {784 run.builder.ensure(CargoMiri { target: run.target });785 }786787 /// Tests `cargo miri test`.788 fn run(self, builder: &Builder<'_>) {789 let host = builder.build.host_target;790 let target = self.target;791 let stage = builder.top_stage;792 if stage == 0 {793 eprintln!("cargo-miri cannot be tested at stage 0");794 std::process::exit(1);795 }796797 // This compiler runs on the host, we'll just use it for the target.798 let build_compiler = builder.compiler(stage, host);799800 // Run `cargo miri test`.801 // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures802 // that we get the desired output), but that is sufficient to make sure that the libtest harness803 // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode.804 let mut cargo = tool::prepare_tool_cargo(805 builder,806 build_compiler,807 Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test!808 target,809 Kind::MiriTest,810 "src/tools/miri/test-cargo-miri",811 SourceType::Submodule,812 &[],813 );814815 // If we are testing stage 2+ cargo miri, make sure that it works with the in-tree cargo.816 // We want to do this *somewhere* to ensure that Miri + nightly cargo actually works.817 if stage >= 2 {818 let built_cargo = builder819 .ensure(tool::Cargo::from_build_compiler(820 // Build stage 1 cargo here, we don't need it to be built in any special way,821 // just that it is built from in-tree sources.822 builder.compiler(0, builder.host_target),823 builder.host_target,824 ))825 .tool_path;826 cargo.env("CARGO", built_cargo);827 }828829 // We're not using `prepare_cargo_test` so we have to do this ourselves.830 // (We're not using that as the test-cargo-miri crate is not known to bootstrap.)831 match builder.test_target {832 TestTarget::AllTargets => {833 cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"])834 }835 TestTarget::Default => &mut cargo,836 TestTarget::DocOnly => cargo.arg("--doc"),837 TestTarget::Tests => cargo.arg("--tests"),838 };839 cargo.arg("--").args(builder.config.test_args());840841 // Finally, run everything.842 let mut cargo = BootstrapCommand::from(cargo);843 {844 let _guard = builder.msg_test("cargo-miri", target, stage);845 let _time = helpers::timeit(builder);846 cargo.run(builder);847 }848 }849}850851#[derive(Debug, Clone, PartialEq, Eq, Hash)]852pub struct CompiletestTest {853 host: TargetSelection,854}855856impl Step for CompiletestTest {857 type Output = ();858859 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {860 run.path("src/tools/compiletest")861 }862863 fn make_run(run: RunConfig<'_>) {864 run.builder.ensure(CompiletestTest { host: run.target });865 }866867 /// Runs `cargo test` for compiletest.868 fn run(self, builder: &Builder<'_>) {869 let host = self.host;870871 // Now that compiletest uses only stable Rust, building it always uses872 // the stage 0 compiler. However, some of its unit tests need to be able873 // to query information from an in-tree compiler, so we treat `--stage`874 // as selecting the stage of that secondary compiler.875876 if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {877 eprintln!("\878ERROR: `--stage 0` causes compiletest to query information from the stage0 (precompiled) compiler, instead of the in-tree compiler, which can cause some tests to fail inappropriately879NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."880 );881 crate::exit!(1);882 }883884 let bootstrap_compiler = builder.compiler(0, host);885 let staged_compiler = builder.compiler(builder.top_stage, host);886887 let mut cargo = tool::prepare_tool_cargo(888 builder,889 bootstrap_compiler,890 Mode::ToolBootstrap,891 host,892 Kind::Test,893 "src/tools/compiletest",894 SourceType::InTree,895 &[],896 );897898 // Used for `compiletest` self-tests to have the path to the *staged* compiler. Getting this899 // right is important, as `compiletest` is intended to only support one target spec JSON900 // format, namely that of the staged compiler.901 cargo.env("TEST_RUSTC", builder.rustc(staged_compiler));902903 run_cargo_test(cargo, &[], &[], "compiletest self test", host, builder);904 }905}906907#[derive(Debug, Clone, PartialEq, Eq, Hash)]908pub struct Clippy {909 compilers: RustcPrivateCompilers,910}911912impl Step for Clippy {913 type Output = ();914 const IS_HOST: bool = true;915916 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {917 run.suite_path("src/tools/clippy/tests").path("src/tools/clippy")918 }919920 fn is_default_step(_builder: &Builder<'_>) -> bool {921 false922 }923924 fn make_run(run: RunConfig<'_>) {925 run.builder.ensure(Clippy {926 compilers: RustcPrivateCompilers::new(927 run.builder,928 run.builder.top_stage,929 run.builder.host_target,930 ),931 });932 }933934 /// Runs `cargo test` for clippy.935 fn run(self, builder: &Builder<'_>) {936 let target = self.compilers.target();937938 // We need to carefully distinguish the compiler that builds clippy, and the compiler939 // that is linked into the clippy being tested. `target_compiler` is the latter,940 // and it must also be used by clippy's test runner to build tests and their dependencies.941 let target_compiler = self.compilers.target_compiler();942 let build_compiler = self.compilers.build_compiler();943944 let mut cargo = tool::prepare_tool_cargo(945 builder,946 build_compiler,947 Mode::ToolRustcPrivate,948 target,949 Kind::Test,950 "src/tools/clippy",951 SourceType::InTree,952 &[],953 );954955 cargo.env("RUSTC_TEST_SUITE", builder.rustc(build_compiler));956 cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(build_compiler));957 let host_libs = builder958 .stage_out(build_compiler, Mode::ToolRustcPrivate)959 .join(builder.cargo_dir(Mode::ToolRustcPrivate));960 cargo.env("HOST_LIBS", host_libs);961962 // Build the standard library that the tests can use.963 builder.std(target_compiler, target);964 cargo.env("TEST_SYSROOT", builder.sysroot(target_compiler));965 cargo.env("TEST_RUSTC", builder.rustc(target_compiler));966 cargo.env("TEST_RUSTC_LIB", builder.rustc_libdir(target_compiler));967968 // Collect paths of tests to run969 'partially_test: {970 let paths = &builder.config.paths[..];971 let mut test_names = Vec::new();972 for path in paths {973 match helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder) {974 TestFilterCategory::Arg(path) => {975 test_names.push(path);976 }977 TestFilterCategory::Fullsuite => {978 // When src/tools/clippy is called directly, all tests should be run.979 break 'partially_test;980 }981 TestFilterCategory::Uninteresting => {}982 }983 }984 cargo.env("TESTNAME", test_names.join(","));985 }986987 cargo.add_rustc_lib_path(builder);988 let cargo = prepare_cargo_test(cargo, &[], &[], target, builder);989990 let _guard = builder.msg_test("clippy", target, target_compiler.stage);991992 // Clippy reports errors if it blessed the outputs993 if cargo.allow_failure().run(builder) {994 // The tests succeeded; nothing to do.995 return;996 }997998 if !builder.config.cmd.bless() {999 crate::exit!(1);1000 }1001 }10021003 fn metadata(&self) -> Option<StepMetadata> {1004 Some(1005 StepMetadata::test("clippy", self.compilers.target())1006 .built_by(self.compilers.build_compiler()),1007 )1008 }1009}10101011fn bin_path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {1012 let path = builder.sysroot(compiler).join("bin");1013 let old_path = env::var_os("PATH").unwrap_or_default();1014 env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("")1015}10161017/// Run the rustdoc-themes tool to test a given compiler.1018#[derive(Debug, Clone, Hash, PartialEq, Eq)]1019pub struct RustdocTheme {1020 /// The compiler (more accurately, its rustdoc) that we test.1021 test_compiler: Compiler,1022}10231024impl Step for RustdocTheme {1025 type Output = ();1026 const IS_HOST: bool = true;10271028 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1029 run.path("src/tools/rustdoc-themes")1030 }10311032 fn is_default_step(_builder: &Builder<'_>) -> bool {1033 true1034 }10351036 fn make_run(run: RunConfig<'_>) {1037 let test_compiler = run.builder.compiler(run.builder.top_stage, run.target);10381039 run.builder.ensure(RustdocTheme { test_compiler });1040 }10411042 fn run(self, builder: &Builder<'_>) {1043 let rustdoc = builder.bootstrap_out.join("rustdoc");1044 let mut cmd = builder.tool_cmd(Tool::RustdocTheme);1045 cmd.arg(rustdoc.to_str().unwrap())1046 .arg(builder.src.join("src/librustdoc/html/static/css/rustdoc.css").to_str().unwrap())1047 .env("RUSTC_STAGE", self.test_compiler.stage.to_string())1048 .env("RUSTC_SYSROOT", builder.sysroot(self.test_compiler))1049 .env(1050 "RUSTDOC_LIBDIR",1051 builder.sysroot_target_libdir(self.test_compiler, self.test_compiler.host),1052 )1053 .env("CFG_RELEASE_CHANNEL", &builder.config.channel)1054 .env("RUSTDOC_REAL", builder.rustdoc_for_compiler(self.test_compiler))1055 .env("RUSTC_BOOTSTRAP", "1");1056 cmd.args(linker_args(builder, self.test_compiler.host, LldThreads::No));10571058 cmd.delay_failure().run(builder);1059 }10601061 fn metadata(&self) -> Option<StepMetadata> {1062 Some(1063 StepMetadata::test("rustdoc-theme", self.test_compiler.host)1064 .stage(self.test_compiler.stage),1065 )1066 }1067}10681069/// Test rustdoc JS for the standard library.1070#[derive(Debug, Clone, Hash, PartialEq, Eq)]1071pub struct RustdocJSStd {1072 /// Compiler that will build the standary library.1073 build_compiler: Compiler,1074 target: TargetSelection,1075}10761077impl Step for RustdocJSStd {1078 type Output = ();1079 const IS_HOST: bool = true;10801081 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1082 run.suite_path("tests/rustdoc-js-std")1083 }10841085 fn is_default_step(builder: &Builder<'_>) -> bool {1086 builder.config.nodejs.is_some()1087 }10881089 fn make_run(run: RunConfig<'_>) {1090 run.builder.ensure(RustdocJSStd {1091 build_compiler: run.builder.compiler(run.builder.top_stage, run.builder.host_target),1092 target: run.target,1093 });1094 }10951096 fn run(self, builder: &Builder<'_>) {1097 let nodejs =1098 builder.config.nodejs.as_ref().expect("need nodejs to run rustdoc-js-std tests");1099 let mut command = command(nodejs);1100 command1101 .arg(builder.src.join("src/tools/rustdoc-js/tester.js"))1102 .arg("--crate-name")1103 .arg("std")1104 .arg("--resource-suffix")1105 .arg(&builder.version)1106 .arg("--doc-folder")1107 .arg(builder.doc_out(self.target))1108 .arg("--test-folder")1109 .arg(builder.src.join("tests/rustdoc-js-std"));11101111 let full_suite = builder.paths.iter().any(|path| {1112 matches!(1113 helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder),1114 TestFilterCategory::Fullsuite1115 )1116 });11171118 // If we have to also run the full suite, don't worry about the individual arguments.1119 // They will be covered by running the entire suite1120 if !full_suite {1121 for path in &builder.paths {1122 if let TestFilterCategory::Arg(p) =1123 helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder)1124 {1125 if !p.ends_with(".js") {1126 eprintln!("A non-js file was given: `{}`", path.display());1127 panic!("Cannot run rustdoc-js-std tests");1128 }1129 command.arg("--test-file").arg(path);1130 }1131 }1132 }11331134 builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler(1135 self.build_compiler,1136 self.target,1137 DocumentationFormat::Html,1138 ));1139 let _guard = builder.msg_test("rustdoc-js-std", self.target, self.build_compiler.stage);1140 command.run(builder);1141 }11421143 fn metadata(&self) -> Option<StepMetadata> {1144 Some(StepMetadata::test("rustdoc-js-std", self.target).stage(self.build_compiler.stage))1145 }1146}11471148#[derive(Debug, Clone, Hash, PartialEq, Eq)]1149pub struct RustdocJSNotStd {1150 pub target: TargetSelection,1151 pub compiler: Compiler,1152}11531154impl Step for RustdocJSNotStd {1155 type Output = ();1156 const IS_HOST: bool = true;11571158 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1159 run.suite_path("tests/rustdoc-js")1160 }11611162 fn is_default_step(builder: &Builder<'_>) -> bool {1163 builder.config.nodejs.is_some()1164 }11651166 fn make_run(run: RunConfig<'_>) {1167 let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());1168 run.builder.ensure(RustdocJSNotStd { target: run.target, compiler });1169 }11701171 fn run(self, builder: &Builder<'_>) {1172 builder.ensure(Compiletest {1173 test_compiler: self.compiler,1174 target: self.target,1175 mode: CompiletestMode::RustdocJs,1176 suite: "rustdoc-js",1177 path: "tests/rustdoc-js",1178 compare_mode: None,1179 });1180 }1181}11821183fn get_browser_ui_test_version_inner(1184 builder: &Builder<'_>,1185 yarn: &Path,1186 global: bool,1187) -> Option<String> {1188 let mut command = command(yarn);1189 command1190 .arg("--cwd")1191 .arg(&builder.build.out)1192 .arg("list")1193 .arg("--parseable")1194 .arg("--long")1195 .arg("--depth=0");1196 if global {1197 command.arg("--global");1198 }1199 // Cache the command output so that `test::RustdocGUI` only performs these1200 // command-line probes once.1201 let lines = command.allow_failure().cached().run_capture(builder).stdout();1202 lines1203 .lines()1204 .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))1205 .map(|v| v.to_owned())1206}12071208fn get_browser_ui_test_version(builder: &Builder<'_>) -> Option<String> {1209 let yarn = builder.config.yarn.as_deref()?;1210 get_browser_ui_test_version_inner(builder, yarn, false)1211 .or_else(|| get_browser_ui_test_version_inner(builder, yarn, true))1212}12131214/// Run GUI tests on a given rustdoc.1215#[derive(Debug, Clone, Hash, PartialEq, Eq)]1216pub struct RustdocGUI {1217 /// The compiler whose rustdoc we are testing.1218 test_compiler: Compiler,1219 target: TargetSelection,1220}12211222impl Step for RustdocGUI {1223 type Output = ();1224 const IS_HOST: bool = true;12251226 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1227 run.suite_path("tests/rustdoc-gui")1228 }12291230 fn is_default_step(builder: &Builder<'_>) -> bool {1231 builder.config.nodejs.is_some()1232 && builder.test_target != TestTarget::DocOnly1233 && get_browser_ui_test_version(builder).is_some()1234 }12351236 fn make_run(run: RunConfig<'_>) {1237 let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());1238 run.builder.ensure(RustdocGUI { test_compiler, target: run.target });1239 }12401241 fn run(self, builder: &Builder<'_>) {1242 builder.std(self.test_compiler, self.target);12431244 let mut cmd = builder.tool_cmd(Tool::RustdocGUITest);12451246 let out_dir = builder.test_out(self.target).join("rustdoc-gui");1247 build_stamp::clear_if_dirty(1248 builder,1249 &out_dir,1250 &builder.rustdoc_for_compiler(self.test_compiler),1251 );12521253 if let Some(src) = builder.config.src.to_str() {1254 cmd.arg("--rust-src").arg(src);1255 }12561257 if let Some(out_dir) = out_dir.to_str() {1258 cmd.arg("--out-dir").arg(out_dir);1259 }12601261 if let Some(initial_cargo) = builder.config.initial_cargo.to_str() {1262 cmd.arg("--initial-cargo").arg(initial_cargo);1263 }12641265 cmd.arg("--jobs").arg(builder.jobs().to_string());12661267 cmd.env("RUSTDOC", builder.rustdoc_for_compiler(self.test_compiler))1268 .env("RUSTC", builder.rustc(self.test_compiler));12691270 add_rustdoc_cargo_linker_args(&mut cmd, builder, self.test_compiler.host, LldThreads::No);12711272 let full_suite = builder.paths.iter().any(|path| {1273 matches!(1274 helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder),1275 TestFilterCategory::Fullsuite1276 )1277 });12781279 // If we have to also run the full suite, don't worry about the individual arguments.1280 // They will be covered by running the entire suite1281 if !full_suite {1282 for path in &builder.paths {1283 if let TestFilterCategory::Arg(p) =1284 helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder)1285 {1286 if !p.ends_with(".goml") {1287 eprintln!("A non-goml file was given: `{}`", path.display());1288 panic!("Cannot run rustdoc-gui tests");1289 }1290 if let Some(name) = path.file_name().and_then(|f| f.to_str()) {1291 cmd.arg("--goml-file").arg(name);1292 }1293 }1294 }1295 }12961297 for test_arg in builder.config.test_args() {1298 cmd.arg("--test-arg").arg(test_arg);1299 }13001301 if let Some(ref nodejs) = builder.config.nodejs {1302 cmd.arg("--nodejs").arg(nodejs);1303 }13041305 if let Some(ref yarn) = builder.config.yarn {1306 cmd.arg("--yarn").arg(yarn);1307 }13081309 let _time = helpers::timeit(builder);1310 let _guard = builder.msg_test("rustdoc-gui", self.target, self.test_compiler.stage);1311 try_run_tests(builder, &mut cmd, true);1312 }13131314 fn metadata(&self) -> Option<StepMetadata> {1315 Some(StepMetadata::test("rustdoc-gui", self.target).stage(self.test_compiler.stage))1316 }1317}13181319/// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style1320/// problems in the repository.1321///1322/// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.)1323#[derive(Debug, Clone, PartialEq, Eq, Hash)]1324pub struct Tidy;13251326impl Step for Tidy {1327 type Output = ();1328 const IS_HOST: bool = true;13291330 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1331 run.path("src/tools/tidy")1332 }13331334 fn is_default_step(builder: &Builder<'_>) -> bool {1335 builder.test_target != TestTarget::DocOnly1336 }13371338 fn make_run(run: RunConfig<'_>) {1339 run.builder.ensure(Tidy);1340 }13411342 /// Runs the `tidy` tool.1343 ///1344 /// This tool in `src/tools` checks up on various bits and pieces of style and1345 /// otherwise just implements a few lint-like checks that are specific to the1346 /// compiler itself.1347 ///1348 /// Once tidy passes, this step also runs `fmt --check` if tests are being run1349 /// for the `dev` or `nightly` channels.1350 fn run(self, builder: &Builder<'_>) {1351 let mut cmd = builder.tool_cmd(Tool::Tidy);1352 cmd.arg(format!("--root-path={}", builder.src.display()));1353 cmd.arg(format!("--cargo-path={}", builder.initial_cargo.display()));1354 cmd.arg(format!("--output-dir={}", builder.out.display()));1355 // Tidy is heavily IO constrained. Still respect `-j`, but use a higher limit if `jobs` hasn't been configured.1356 let jobs = builder.config.jobs.unwrap_or_else(|| {1357 8 * std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u321358 });1359 cmd.arg(format!("--concurrency={jobs}"));1360 // pass the path to the yarn command used for installing js deps.1361 if let Some(yarn) = &builder.config.yarn {1362 cmd.arg(format!("--npm-path={}", yarn.display()));1363 } else {1364 cmd.arg("--npm-path=yarn");1365 }1366 if builder.is_verbose() {1367 cmd.arg("--verbose");1368 }1369 if builder.config.cmd.bless() {1370 cmd.arg("--bless");1371 }1372 if builder.config.is_running_on_ci() {1373 cmd.arg("--ci=true");1374 }1375 if let Some(s) =1376 builder.config.cmd.extra_checks().or(builder.config.tidy_extra_checks.as_deref())1377 {1378 cmd.arg(format!("--extra-checks={s}"));1379 }1380 let mut args = std::env::args_os();1381 if args.any(|arg| arg == OsStr::new("--")) {1382 cmd.arg("--");1383 cmd.args(args);1384 }13851386 if builder.config.channel == "dev" || builder.config.channel == "nightly" {1387 if !builder.config.json_output {1388 builder.info("fmt check");1389 if builder.config.initial_rustfmt.is_none() {1390 let inferred_rustfmt_dir = builder.initial_sysroot.join("bin");1391 eprintln!(1392 "\1393ERROR: no `rustfmt` binary found in {PATH}1394INFO: `rust.channel` is currently set to \"{CHAN}\"1395HELP: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `bootstrap.toml` file1396HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to `x.py test`",1397 PATH = inferred_rustfmt_dir.display(),1398 CHAN = builder.config.channel,1399 );1400 crate::exit!(1);1401 }1402 let all = false;1403 crate::core::build_steps::format::format(1404 builder,1405 !builder.config.cmd.bless(),1406 all,1407 &[],1408 );1409 } else {1410 eprintln!(1411 "WARNING: `--json-output` is not supported on rustfmt, formatting will be skipped"1412 );1413 }1414 }14151416 builder.info("tidy check");1417 cmd.delay_failure().run(builder);14181419 builder.info("x.py completions check");1420 let completion_paths = get_completion_paths(builder);1421 if builder.config.cmd.bless() {1422 builder.ensure(crate::core::build_steps::run::GenerateCompletions);1423 } else if completion_paths1424 .into_iter()1425 .any(|(shell, path)| get_completion(shell, &path).is_some())1426 {1427 eprintln!(1428 "x.py completions were changed; run `x.py run generate-completions` to update them"1429 );1430 crate::exit!(1);1431 }14321433 builder.info("x.py help check");1434 if builder.config.cmd.bless() {1435 builder.ensure(crate::core::build_steps::run::GenerateHelp);1436 } else {1437 let help_path = get_help_path(builder);1438 let cur_help = std::fs::read_to_string(&help_path).unwrap_or_else(|err| {1439 eprintln!("couldn't read {}: {}", help_path.display(), err);1440 crate::exit!(1);1441 });1442 let new_help = top_level_help();14431444 if new_help != cur_help {1445 eprintln!("x.py help was changed; run `x.py run generate-help` to update it");1446 crate::exit!(1);1447 }1448 }1449 }14501451 fn metadata(&self) -> Option<StepMetadata> {1452 Some(StepMetadata::test("tidy", TargetSelection::default()))1453 }1454}14551456/// Runs `cargo test` on the `src/tools/run-make-support` crate.1457/// That crate is used by run-make tests.1458#[derive(Debug, Clone, PartialEq, Eq, Hash)]1459pub struct CrateRunMakeSupport {1460 host: TargetSelection,1461}14621463impl Step for CrateRunMakeSupport {1464 type Output = ();1465 const IS_HOST: bool = true;14661467 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1468 run.path("src/tools/run-make-support")1469 }14701471 fn make_run(run: RunConfig<'_>) {1472 run.builder.ensure(CrateRunMakeSupport { host: run.target });1473 }14741475 /// Runs `cargo test` for run-make-support.1476 fn run(self, builder: &Builder<'_>) {1477 let host = self.host;1478 let compiler = builder.compiler(0, host);14791480 let mut cargo = tool::prepare_tool_cargo(1481 builder,1482 compiler,1483 Mode::ToolBootstrap,1484 host,1485 Kind::Test,1486 "src/tools/run-make-support",1487 SourceType::InTree,1488 &[],1489 );1490 cargo.allow_features("test");1491 run_cargo_test(cargo, &[], &[], "run-make-support self test", host, builder);1492 }1493}14941495#[derive(Debug, Clone, PartialEq, Eq, Hash)]1496pub struct CrateBuildHelper {1497 host: TargetSelection,1498}14991500impl Step for CrateBuildHelper {1501 type Output = ();1502 const IS_HOST: bool = true;15031504 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1505 run.path("src/build_helper")1506 }15071508 fn make_run(run: RunConfig<'_>) {1509 run.builder.ensure(CrateBuildHelper { host: run.target });1510 }15111512 /// Runs `cargo test` for build_helper.1513 fn run(self, builder: &Builder<'_>) {1514 let host = self.host;1515 let compiler = builder.compiler(0, host);15161517 let mut cargo = tool::prepare_tool_cargo(1518 builder,1519 compiler,1520 Mode::ToolBootstrap,1521 host,1522 Kind::Test,1523 "src/build_helper",1524 SourceType::InTree,1525 &[],1526 );1527 cargo.allow_features("test");1528 run_cargo_test(cargo, &[], &[], "build_helper self test", host, builder);1529 }1530}15311532fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf {1533 builder.out.join(host).join("test")1534}15351536/// Declares a test step that invokes compiletest on a particular test suite.1537macro_rules! test {1538 (1539 $( #[$attr:meta] )* // allow docstrings and attributes1540 $name:ident {1541 path: $path:expr,1542 mode: $mode:expr,1543 suite: $suite:expr,1544 default: $default:expr1545 $( , IS_HOST: $IS_HOST:expr )? // default: false1546 $( , compare_mode: $compare_mode:expr )? // default: None1547 $( , )? // optional trailing comma1548 }1549 ) => {1550 $( #[$attr] )*1551 #[derive(Debug, Clone, PartialEq, Eq, Hash)]1552 pub struct $name {1553 test_compiler: Compiler,1554 target: TargetSelection,1555 }15561557 impl Step for $name {1558 type Output = ();1559 const IS_HOST: bool = (const {1560 #[allow(unused_assignments, unused_mut)]1561 let mut value = false;1562 $( value = $IS_HOST; )?1563 value1564 });15651566 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1567 run.suite_path($path)1568 }15691570 fn is_default_step(_builder: &Builder<'_>) -> bool {1571 const { $default }1572 }15731574 fn make_run(run: RunConfig<'_>) {1575 let test_compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());15761577 run.builder.ensure($name { test_compiler, target: run.target });1578 }15791580 fn run(self, builder: &Builder<'_>) {1581 builder.ensure(Compiletest {1582 test_compiler: self.test_compiler,1583 target: self.target,1584 mode: const { $mode },1585 suite: $suite,1586 path: $path,1587 compare_mode: (const {1588 #[allow(unused_assignments, unused_mut)]1589 let mut value = None;1590 $( value = $compare_mode; )?1591 value1592 }),1593 })1594 }1595 }1596 };1597}15981599test!(Ui { path: "tests/ui", mode: CompiletestMode::Ui, suite: "ui", default: true });16001601test!(Crashes {1602 path: "tests/crashes",1603 mode: CompiletestMode::Crashes,1604 suite: "crashes",1605 default: true,1606});16071608test!(CodegenLlvm {1609 path: "tests/codegen-llvm",1610 mode: CompiletestMode::Codegen,1611 suite: "codegen-llvm",1612 default: true1613});16141615test!(CodegenUnits {1616 path: "tests/codegen-units",1617 mode: CompiletestMode::CodegenUnits,1618 suite: "codegen-units",1619 default: true,1620});16211622test!(Incremental {1623 path: "tests/incremental",1624 mode: CompiletestMode::Incremental,1625 suite: "incremental",1626 default: true,1627});16281629test!(Debuginfo {1630 path: "tests/debuginfo",1631 mode: CompiletestMode::Debuginfo,1632 suite: "debuginfo",1633 default: true,1634 compare_mode: Some("split-dwarf"),1635});16361637test!(UiFullDeps {1638 path: "tests/ui-fulldeps",1639 mode: CompiletestMode::Ui,1640 suite: "ui-fulldeps",1641 default: true,1642 IS_HOST: true,1643});16441645test!(RustdocHtml {1646 path: "tests/rustdoc-html",1647 mode: CompiletestMode::RustdocHtml,1648 suite: "rustdoc-html",1649 default: true,1650 IS_HOST: true,1651});1652test!(RustdocUi {1653 path: "tests/rustdoc-ui",1654 mode: CompiletestMode::Ui,1655 suite: "rustdoc-ui",1656 default: true,1657 IS_HOST: true,1658});16591660test!(RustdocJson {1661 path: "tests/rustdoc-json",1662 mode: CompiletestMode::RustdocJson,1663 suite: "rustdoc-json",1664 default: true,1665 IS_HOST: true,1666});16671668test!(Pretty {1669 path: "tests/pretty",1670 mode: CompiletestMode::Pretty,1671 suite: "pretty",1672 default: true,1673 IS_HOST: true,1674});16751676test!(RunMake {1677 path: "tests/run-make",1678 mode: CompiletestMode::RunMake,1679 suite: "run-make",1680 default: true,1681});1682test!(RunMakeCargo {1683 path: "tests/run-make-cargo",1684 mode: CompiletestMode::RunMake,1685 suite: "run-make-cargo",1686 default: true1687});1688test!(BuildStd {1689 path: "tests/build-std",1690 mode: CompiletestMode::RunMake,1691 suite: "build-std",1692 default: false1693});16941695test!(AssemblyLlvm {1696 path: "tests/assembly-llvm",1697 mode: CompiletestMode::Assembly,1698 suite: "assembly-llvm",1699 default: true1700});17011702/// Runs the coverage test suite at `tests/coverage` in some or all of the1703/// coverage test modes.1704#[derive(Debug, Clone, PartialEq, Eq, Hash)]1705pub struct Coverage {1706 pub compiler: Compiler,1707 pub target: TargetSelection,1708 pub(crate) mode: CompiletestMode,1709}17101711impl Coverage {1712 const PATH: &'static str = "tests/coverage";1713 const SUITE: &'static str = "coverage";1714 const ALL_MODES: &[CompiletestMode] =1715 &[CompiletestMode::CoverageMap, CompiletestMode::CoverageRun];1716}17171718impl Step for Coverage {1719 type Output = ();1720 /// Compiletest will automatically skip the "coverage-run" tests if necessary.1721 const IS_HOST: bool = false;17221723 fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> {1724 // Support various invocation styles, including:1725 // - `./x test coverage`1726 // - `./x test tests/coverage/trivial.rs`1727 // - `./x test coverage-map`1728 // - `./x test coverage-run -- tests/coverage/trivial.rs`1729 run = run.suite_path(Self::PATH);1730 for mode in Self::ALL_MODES {1731 run = run.alias(mode.as_str());1732 }1733 run1734 }17351736 fn is_default_step(_builder: &Builder<'_>) -> bool {1737 true1738 }17391740 fn make_run(run: RunConfig<'_>) {1741 let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());1742 let target = run.target;17431744 // List of (coverage) test modes that the coverage test suite will be1745 // run in. It's OK for this to contain duplicates, because the call to1746 // `Builder::ensure` below will take care of deduplication.1747 let mut modes = vec![];17481749 // From the pathsets that were selected on the command-line (or by default),1750 // determine which modes to run in.1751 for path in &run.paths {1752 match path {1753 PathSet::Set(_) => {1754 for &mode in Self::ALL_MODES {1755 if path.assert_single_path().path == Path::new(mode.as_str()) {1756 modes.push(mode);1757 break;1758 }1759 }1760 }1761 PathSet::Suite(_) => {1762 modes.extend_from_slice(Self::ALL_MODES);1763 break;1764 }1765 }1766 }17671768 // Skip any modes that were explicitly skipped/excluded on the command-line.1769 // FIXME(Zalathar): Integrate this into central skip handling somehow?1770 modes.retain(|mode| {1771 !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode.as_str()))1772 });17731774 // FIXME(Zalathar): Make these commands skip all coverage tests, as expected:1775 // - `./x test --skip=tests`1776 // - `./x test --skip=tests/coverage`1777 // - `./x test --skip=coverage`1778 // Skip handling currently doesn't have a way to know that skipping the coverage1779 // suite should also skip the `coverage-map` and `coverage-run` aliases.17801781 for mode in modes {1782 run.builder.ensure(Coverage { compiler, target, mode });1783 }1784 }17851786 fn run(self, builder: &Builder<'_>) {1787 let Self { compiler, target, mode } = self;1788 // Like other compiletest suite test steps, delegate to an internal1789 // compiletest task to actually run the tests.1790 builder.ensure(Compiletest {1791 test_compiler: compiler,1792 target,1793 mode,1794 suite: Self::SUITE,1795 path: Self::PATH,1796 compare_mode: None,1797 });1798 }1799}18001801test!(CoverageRunRustdoc {1802 path: "tests/coverage-run-rustdoc",1803 mode: CompiletestMode::CoverageRun,1804 suite: "coverage-run-rustdoc",1805 default: true,1806 IS_HOST: true,1807});18081809// For the mir-opt suite we do not use macros, as we need custom behavior when blessing.1810#[derive(Debug, Clone, PartialEq, Eq, Hash)]1811pub struct MirOpt {1812 pub compiler: Compiler,1813 pub target: TargetSelection,1814}18151816impl Step for MirOpt {1817 type Output = ();18181819 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1820 run.suite_path("tests/mir-opt")1821 }18221823 fn is_default_step(_builder: &Builder<'_>) -> bool {1824 true1825 }18261827 fn make_run(run: RunConfig<'_>) {1828 let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());1829 run.builder.ensure(MirOpt { compiler, target: run.target });1830 }18311832 fn run(self, builder: &Builder<'_>) {1833 let run = |target| {1834 builder.ensure(Compiletest {1835 test_compiler: self.compiler,1836 target,1837 mode: CompiletestMode::MirOpt,1838 suite: "mir-opt",1839 path: "tests/mir-opt",1840 compare_mode: None,1841 })1842 };18431844 run(self.target);18451846 // Run more targets with `--bless`. But we always run the host target first, since some1847 // tests use very specific `only` clauses that are not covered by the target set below.1848 if builder.config.cmd.bless() {1849 // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort,1850 // but while we're at it we might as well flex our cross-compilation support. This1851 // selection covers all our tier 1 operating systems and architectures using only tier1852 // 1 targets.18531854 for target in ["aarch64-unknown-linux-gnu", "i686-pc-windows-msvc"] {1855 run(TargetSelection::from_user(target));1856 }18571858 for target in ["x86_64-apple-darwin", "i686-unknown-linux-musl"] {1859 let target = TargetSelection::from_user(target);1860 let panic_abort_target = builder.ensure(MirOptPanicAbortSyntheticTarget {1861 compiler: self.compiler,1862 base: target,1863 });1864 run(panic_abort_target);1865 }1866 }1867 }1868}18691870/// Executes the `compiletest` tool to run a suite of tests.1871///1872/// Compiles all tests with `test_compiler` for `target` with the specified1873/// compiletest `mode` and `suite` arguments. For example `mode` can be1874/// "mir-opt" and `suite` can be something like "debuginfo".1875#[derive(Debug, Clone, PartialEq, Eq, Hash)]1876struct Compiletest {1877 /// The compiler that we're testing.1878 test_compiler: Compiler,1879 target: TargetSelection,1880 mode: CompiletestMode,1881 suite: &'static str,1882 path: &'static str,1883 compare_mode: Option<&'static str>,1884}18851886impl Step for Compiletest {1887 type Output = ();18881889 fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {1890 run.never()1891 }18921893 fn run(self, builder: &Builder<'_>) {1894 if builder.test_target == TestTarget::DocOnly {1895 return;1896 }18971898 if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 {1899 eprintln!("\1900ERROR: `--stage 0` runs compiletest on the stage0 (precompiled) compiler, not your local changes, and will almost always cause tests to fail1901HELP: to test the compiler or standard library, omit the stage or explicitly use `--stage 1` instead1902NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`."1903 );1904 crate::exit!(1);1905 }19061907 let mut test_compiler = self.test_compiler;1908 let target = self.target;1909 let mode = self.mode;1910 let suite = self.suite;19111912 // Path for test suite1913 let suite_path = self.path;19141915 // Skip codegen tests if they aren't enabled in configuration.1916 if !builder.config.codegen_tests && mode == CompiletestMode::Codegen {1917 return;1918 }19191920 // Support stage 1 ui-fulldeps. This is somewhat complicated: ui-fulldeps tests for the most1921 // part test the *API* of the compiler, not how it compiles a given file. As a result, we1922 // can run them against the stage 1 sources as long as we build them with the stage 01923 // bootstrap compiler.1924 // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the1925 // running compiler in stage 2 when plugins run.1926 let query_compiler;1927 let (stage, stage_id) = if suite == "ui-fulldeps" && test_compiler.stage == 1 {1928 // Even when using the stage 0 compiler, we also need to provide the stage 1 compiler1929 // so that compiletest can query it for target information.1930 query_compiler = Some(test_compiler);1931 // At stage 0 (stage - 1) we are using the stage0 compiler. Using `self.target` can lead1932 // finding an incorrect compiler path on cross-targets, as the stage 0 is always equal to1933 // `build.build` in the configuration.1934 let build = builder.build.host_target;1935 test_compiler = builder.compiler(test_compiler.stage - 1, build);1936 let test_stage = test_compiler.stage + 1;1937 (test_stage, format!("stage{test_stage}-{build}"))1938 } else {1939 query_compiler = None;1940 let stage = test_compiler.stage;1941 (stage, format!("stage{stage}-{target}"))1942 };19431944 if suite.ends_with("fulldeps") {1945 builder.ensure(compile::Rustc::new(test_compiler, target));1946 }19471948 if suite == "debuginfo" {1949 builder.ensure(dist::DebuggerScripts {1950 sysroot: builder.sysroot(test_compiler).to_path_buf(),1951 target,1952 });1953 }1954 if mode == CompiletestMode::RunMake {1955 builder.tool_exe(Tool::RunMakeSupport);1956 }19571958 // ensure that `libproc_macro` is available on the host.1959 if suite == "mir-opt" {1960 builder.ensure(1961 compile::Std::new(test_compiler, test_compiler.host).is_for_mir_opt_tests(true),1962 );1963 } else {1964 builder.std(test_compiler, test_compiler.host);1965 }19661967 let mut cmd = builder.tool_cmd(Tool::Compiletest);19681969 if suite == "mir-opt" {1970 builder.ensure(compile::Std::new(test_compiler, target).is_for_mir_opt_tests(true));1971 } else {1972 builder.std(test_compiler, target);1973 }19741975 builder.ensure(RemoteCopyLibs { build_compiler: test_compiler, target });19761977 // compiletest currently has... a lot of arguments, so let's just pass all1978 // of them!19791980 cmd.arg("--stage").arg(stage.to_string());1981 cmd.arg("--stage-id").arg(stage_id);19821983 cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(test_compiler));1984 cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(test_compiler, target));1985 cmd.arg("--rustc-path").arg(builder.rustc(test_compiler));1986 if let Some(query_compiler) = query_compiler {1987 cmd.arg("--query-rustc-path").arg(builder.rustc(query_compiler));1988 }19891990 // Minicore auxiliary lib for `no_core` tests that need `core` stubs in cross-compilation1991 // scenarios.1992 cmd.arg("--minicore-path")1993 .arg(builder.src.join("tests").join("auxiliary").join("minicore.rs"));19941995 let is_rustdoc = suite == "rustdoc-ui" || suite == "rustdoc-js";19961997 // There are (potentially) 2 `cargo`s to consider:1998 //1999 // - A "bootstrap" cargo, which is the same cargo used to build bootstrap itself, and is2000 // used to build the `run-make` test recipes and the `run-make-support` test library. All
Findings
✓ No findings reported for this file.