use std::collections::HashSet;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::{env, fs, iter};
use clap_complete::shells;
use crate::core::build_steps::compile::run_cargo;
use crate::core::build_steps::doc::DocumentationFormat;
use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
use crate::core::build_steps::tool::{self, SourceType, Tool};
use crate::core::build_steps::toolstate::ToolState;
use crate::core::build_steps::{compile, dist, llvm};
use crate::core::builder::{
self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, crate_description,
};
use crate::core::config::TargetSelection;
use crate::core::config::flags::{Subcommand, get_completion};
use crate::utils::exec::{BootstrapCommand, command};
use crate::utils::helpers::{
self, LldThreads, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date,
};
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
use crate::{CLang, DocTests, GitRepo, Mode, envify};
const ADB_TEST_DIR: &str = "/data/local/tmp/work";
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateBootstrap {
path: PathBuf,
host: TargetSelection,
}
impl Step for CrateBootstrap {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/jsondoclint")
.path("src/tools/suggest-tests")
.path("src/tools/replace-version-placeholder")
.alias("tidyselftest")
}
fn make_run(run: RunConfig<'_>) {
for path in run.paths {
let path = path.assert_single_path().path.clone();
run.builder.ensure(CrateBootstrap { host: run.target, path });
}
}
fn run(self, builder: &Builder<'_>) {
let bootstrap_host = builder.config.build;
let compiler = builder.compiler(0, bootstrap_host);
let mut path = self.path.to_str().unwrap();
if path == "tidyselftest" {
path = "src/tools/tidy";
}
let cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolBootstrap,
bootstrap_host,
Kind::Test,
path,
SourceType::InTree,
&[],
);
let crate_name = path.rsplit_once('/').unwrap().1;
run_cargo_test(cargo, &[], &[], crate_name, crate_name, compiler, bootstrap_host, builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Linkcheck {
host: TargetSelection,
}
impl Step for Linkcheck {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = true;
fn run(self, builder: &Builder<'_>) {
let host = self.host;
let hosts = &builder.hosts;
let targets = &builder.targets;
if (hosts != targets) && !hosts.is_empty() && !targets.is_empty() {
panic!(
"Linkcheck currently does not support builds with different hosts and targets.
You can skip linkcheck with --skip src/tools/linkchecker"
);
}
builder.info(&format!("Linkcheck ({host})"));
let bootstrap_host = builder.config.build;
let compiler = builder.compiler(0, bootstrap_host);
let cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolBootstrap,
bootstrap_host,
Kind::Test,
"src/tools/linkchecker",
SourceType::InTree,
&[],
);
run_cargo_test(
cargo,
&[],
&[],
"linkchecker",
"linkchecker self tests",
compiler,
bootstrap_host,
builder,
);
if builder.doc_tests == DocTests::No {
return;
}
builder.default_doc(&[]);
let linkchecker = builder.tool_cmd(Tool::Linkchecker);
let _guard =
builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host);
let _time = helpers::timeit(builder);
linkchecker.delay_failure().arg(builder.out.join(host).join("doc")).run(builder);
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
let run = run.path("src/tools/linkchecker");
run.default_condition(builder.config.docs)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Linkcheck { host: run.target });
}
}
fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool {
command("tidy").allow_failure().arg("--version").run_capture_stdout(builder).is_success()
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct HtmlCheck {
target: TargetSelection,
}
impl Step for HtmlCheck {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
let run = run.path("src/tools/html-checker");
run.lazy_default_condition(Box::new(|| check_if_tidy_is_installed(builder)))
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(HtmlCheck { target: run.target });
}
fn run(self, builder: &Builder<'_>) {
if !check_if_tidy_is_installed(builder) {
eprintln!("not running HTML-check tool because `tidy` is missing");
eprintln!(
"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."
);
panic!("Cannot run html-check tests");
}
builder.default_doc(&[]);
builder.ensure(crate::core::build_steps::doc::Rustc::new(
builder.top_stage,
self.target,
builder,
));
builder
.tool_cmd(Tool::HtmlChecker)
.delay_failure()
.arg(builder.doc_out(self.target))
.run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Cargotest {
stage: u32,
host: TargetSelection,
}
impl Step for Cargotest {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/cargotest")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Cargotest { stage: run.builder.top_stage, host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let compiler = builder.compiler(self.stage, self.host);
builder.ensure(compile::Rustc::new(compiler, compiler.host));
let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host });
let out_dir = builder.out.join("ct");
t!(fs::create_dir_all(&out_dir));
let _time = helpers::timeit(builder);
let mut cmd = builder.tool_cmd(Tool::CargoTest);
cmd.arg(&cargo)
.arg(&out_dir)
.args(builder.config.test_args())
.env("RUSTC", builder.rustc(compiler))
.env("RUSTDOC", builder.rustdoc(compiler));
add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No);
cmd.delay_failure().run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Cargo {
stage: u32,
host: TargetSelection,
}
impl Cargo {
const CRATE_PATH: &str = "src/tools/cargo";
}
impl Step for Cargo {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path(Self::CRATE_PATH)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Cargo { stage: run.builder.top_stage, host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let compiler = builder.compiler(self.stage, self.host);
builder.ensure(tool::Cargo { compiler, target: self.host });
let cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
self.host,
Kind::Test,
Self::CRATE_PATH,
SourceType::Submodule,
&[],
);
let mut cargo = prepare_cargo_test(cargo, &[], &[], "cargo", compiler, self.host, builder);
cargo.env("CFG_DISABLE_CROSS_TESTS", "1");
cargo.env("CARGO_TEST_DISABLE_NIGHTLY", "1");
cargo.env("PATH", path_for_cargo(builder, compiler));
cargo.env("CARGO_RUSTC_CURRENT_DIR", builder.src.display().to_string());
#[cfg(feature = "build-metrics")]
builder.metrics.begin_test_suite(
build_helper::metrics::TestSuiteMetadata::CargoPackage {
crates: vec!["cargo".into()],
target: self.host.triple.to_string(),
host: self.host.triple.to_string(),
stage: self.stage,
},
builder,
);
let _time = helpers::timeit(builder);
add_flags_and_try_run_tests(builder, &mut cargo);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RustAnalyzer {
stage: u32,
host: TargetSelection,
}
impl Step for RustAnalyzer {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/rust-analyzer")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Self { stage: run.builder.top_stage, host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let stage = self.stage;
let host = self.host;
let compiler = builder.compiler(stage, host);
builder.ensure(compile::Rustc::new(compiler, host));
let workspace_path = "src/tools/rust-analyzer";
let crate_path = "src/tools/rust-analyzer/crates/proc-macro-srv";
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
host,
Kind::Test,
crate_path,
SourceType::InTree,
&["in-rust-tree".to_owned()],
);
cargo.allow_features(tool::RustAnalyzer::ALLOW_FEATURES);
let dir = builder.src.join(workspace_path);
cargo.env("CARGO_WORKSPACE_DIR", &dir);
cargo.env("SKIP_SLOW_TESTS", "1");
cargo.add_rustc_lib_path(builder);
run_cargo_test(cargo, &[], &[], "rust-analyzer", "rust-analyzer", compiler, host, builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Rustfmt {
stage: u32,
host: TargetSelection,
}
impl Step for Rustfmt {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/rustfmt")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustfmt { stage: run.builder.top_stage, host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let stage = self.stage;
let host = self.host;
let compiler = builder.compiler(stage, host);
builder.ensure(tool::Rustfmt { compiler, target: self.host, extra_features: Vec::new() });
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
host,
Kind::Test,
"src/tools/rustfmt",
SourceType::InTree,
&[],
);
let dir = testdir(builder, compiler.host);
t!(fs::create_dir_all(&dir));
cargo.env("RUSTFMT_TEST_DIR", dir);
cargo.add_rustc_lib_path(builder);
run_cargo_test(cargo, &[], &[], "rustfmt", "rustfmt", compiler, host, builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Miri {
target: TargetSelection,
}
impl Miri {
pub fn build_miri_sysroot(
builder: &Builder<'_>,
compiler: Compiler,
target: TargetSelection,
) -> PathBuf {
let miri_sysroot = builder.out.join(compiler.host).join("miri-sysroot");
let mut cargo = builder::Cargo::new(
builder,
compiler,
Mode::Std,
SourceType::Submodule,
target,
Kind::MiriSetup,
);
cargo.env("MIRI_LIB_SRC", builder.src.join("library"));
cargo.env("MIRI_SYSROOT", &miri_sysroot);
let mut cargo = BootstrapCommand::from(cargo);
let _guard =
builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target);
cargo.run(builder);
cargo.arg("--print-sysroot");
builder.verbose(|| println!("running: {cargo:?}"));
let stdout = cargo.run_capture_stdout(builder).stdout();
let sysroot = stdout.trim_end();
builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
PathBuf::from(sysroot)
}
}
impl Step for Miri {
type Output = ();
const ONLY_HOSTS: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/miri")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Miri { target: run.target });
}
fn run(self, builder: &Builder<'_>) {
let host = builder.build.build;
let target = self.target;
let stage = builder.top_stage;
if stage == 0 {
eprintln!("miri cannot be tested at stage 0");
std::process::exit(1);
}
let target_compiler = builder.compiler(stage, host);
let host_compiler = builder.compiler(stage - 1, host);
let miri = builder.ensure(tool::Miri {
compiler: host_compiler,
target: host,
extra_features: Vec::new(),
});
builder.ensure(tool::CargoMiri {
compiler: host_compiler,
target: host,
extra_features: Vec::new(),
});
let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target);
builder.ensure(compile::Std::new(target_compiler, host));
let host_sysroot = builder.sysroot(target_compiler);
if !builder.config.dry_run() {
let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("miri_ui");
builder.clear_if_dirty(&ui_test_dep_dir, &miri_sysroot);
}
let mut cargo = tool::prepare_tool_cargo(
builder,
host_compiler,
Mode::ToolRustc,
host,
Kind::Test,
"src/tools/miri",
SourceType::InTree,
&[],
);
cargo.add_rustc_lib_path(builder);
let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder);
cargo.env("MIRI_SYSROOT", &miri_sysroot);
cargo.env("MIRI_HOST_SYSROOT", &host_sysroot);
cargo.env("MIRI", &miri);
cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg());
{
let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target);
let _time = helpers::timeit(builder);
cargo.run(builder);
}
if builder.config.test_args().is_empty() {
cargo.env("MIRIFLAGS", "-O -Zmir-opt-level=4 -Cdebug-assertions=yes");
cargo.env("MIRI_SKIP_UI_CHECKS", "1");
cargo.env_remove("RUSTC_BLESS");
cargo.args(["tests/pass", "tests/panic"]);
{
let _guard = builder.msg_sysroot_tool(
Kind::Test,
stage,
"miri (mir-opt-level 4)",
host,
target,
);
let _time = helpers::timeit(builder);
cargo.run(builder);
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CargoMiri {
target: TargetSelection,
}
impl Step for CargoMiri {
type Output = ();
const ONLY_HOSTS: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/miri/cargo-miri")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(CargoMiri { target: run.target });
}
fn run(self, builder: &Builder<'_>) {
let host = builder.build.build;
let target = self.target;
let stage = builder.top_stage;
if stage == 0 {
eprintln!("cargo-miri cannot be tested at stage 0");
std::process::exit(1);
}
let compiler = builder.compiler(stage, host);
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolStd, target,
Kind::MiriTest,
"src/tools/miri/test-cargo-miri",
SourceType::Submodule,
&[],
);
match builder.doc_tests {
DocTests::Yes => {}
DocTests::No => {
cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]);
}
DocTests::Only => {
cargo.arg("--doc");
}
}
cargo.arg("--").args(builder.config.test_args());
let mut cargo = BootstrapCommand::from(cargo);
{
let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target);
let _time = helpers::timeit(builder);
cargo.run(builder);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CompiletestTest {
host: TargetSelection,
}
impl Step for CompiletestTest {
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/compiletest")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(CompiletestTest { host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let host = self.host;
let compiler = builder.compiler(builder.top_stage, host);
builder.ensure(compile::Std::new(compiler, host));
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolStd,
host,
Kind::Test,
"src/tools/compiletest",
SourceType::InTree,
&[],
);
cargo.allow_features("test");
run_cargo_test(
cargo,
&[],
&[],
"compiletest",
"compiletest self test",
compiler,
host,
builder,
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Clippy {
stage: u32,
host: TargetSelection,
}
impl Step for Clippy {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/clippy")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Clippy { stage: run.builder.top_stage, host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let stage = self.stage;
let host = self.host;
let compiler = builder.compiler(stage, host);
builder.ensure(tool::Clippy { compiler, target: self.host, extra_features: Vec::new() });
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
host,
Kind::Test,
"src/tools/clippy",
SourceType::InTree,
&[],
);
cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler));
cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler));
let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir());
cargo.env("HOST_LIBS", host_libs);
cargo.add_rustc_lib_path(builder);
let cargo = prepare_cargo_test(cargo, &[], &[], "clippy", compiler, host, builder);
let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host);
if cargo.allow_failure().run(builder) {
return;
}
if !builder.config.cmd.bless() {
crate::exit!(1);
}
}
}
fn path_for_cargo(builder: &Builder<'_>, compiler: Compiler) -> OsString {
let path = builder.sysroot(compiler).join("bin");
let old_path = env::var_os("PATH").unwrap_or_default();
env::join_paths(iter::once(path).chain(env::split_paths(&old_path))).expect("")
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct RustdocTheme {
pub compiler: Compiler,
}
impl Step for RustdocTheme {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/rustdoc-themes")
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.target);
run.builder.ensure(RustdocTheme { compiler });
}
fn run(self, builder: &Builder<'_>) {
let rustdoc = builder.bootstrap_out.join("rustdoc");
let mut cmd = builder.tool_cmd(Tool::RustdocTheme);
cmd.arg(rustdoc.to_str().unwrap())
.arg(builder.src.join("src/librustdoc/html/static/css/rustdoc.css").to_str().unwrap())
.env("RUSTC_STAGE", self.compiler.stage.to_string())
.env("RUSTC_SYSROOT", builder.sysroot(self.compiler))
.env("RUSTDOC_LIBDIR", builder.sysroot_target_libdir(self.compiler, self.compiler.host))
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
.env("RUSTDOC_REAL", builder.rustdoc(self.compiler))
.env("RUSTC_BOOTSTRAP", "1");
cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
cmd.delay_failure().run(builder);
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct RustdocJSStd {
pub target: TargetSelection,
}
impl Step for RustdocJSStd {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let default = run.builder.config.nodejs.is_some();
run.suite_path("tests/rustdoc-js-std").default_condition(default)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(RustdocJSStd { target: run.target });
}
fn run(self, builder: &Builder<'_>) {
let nodejs =
builder.config.nodejs.as_ref().expect("need nodejs to run rustdoc-js-std tests");
let mut command = command(nodejs);
command
.arg(builder.src.join("src/tools/rustdoc-js/tester.js"))
.arg("--crate-name")
.arg("std")
.arg("--resource-suffix")
.arg(&builder.version)
.arg("--doc-folder")
.arg(builder.doc_out(self.target))
.arg("--test-folder")
.arg(builder.src.join("tests/rustdoc-js-std"));
for path in &builder.paths {
if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder)
{
if !p.ends_with(".js") {
eprintln!("A non-js file was given: `{}`", path.display());
panic!("Cannot run rustdoc-js-std tests");
}
command.arg("--test-file").arg(path);
}
}
builder.ensure(crate::core::build_steps::doc::Std::new(
builder.top_stage,
self.target,
DocumentationFormat::Html,
));
let _guard = builder.msg(
Kind::Test,
builder.top_stage,
"rustdoc-js-std",
builder.config.build,
self.target,
);
command.run(builder);
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct RustdocJSNotStd {
pub target: TargetSelection,
pub compiler: Compiler,
}
impl Step for RustdocJSNotStd {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let default = run.builder.config.nodejs.is_some();
run.suite_path("tests/rustdoc-js").default_condition(default)
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure(RustdocJSNotStd { target: run.target, compiler });
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(Compiletest {
compiler: self.compiler,
target: self.target,
mode: "js-doc-test",
suite: "rustdoc-js",
path: "tests/rustdoc-js",
compare_mode: None,
});
}
}
fn get_browser_ui_test_version_inner(
builder: &Builder<'_>,
npm: &Path,
global: bool,
) -> Option<String> {
let mut command = command(npm);
command.arg("list").arg("--parseable").arg("--long").arg("--depth=0");
if global {
command.arg("--global");
}
let lines = command.allow_failure().run_capture(builder).stdout();
lines
.lines()
.find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
.map(|v| v.to_owned())
}
fn get_browser_ui_test_version(builder: &Builder<'_>, npm: &Path) -> Option<String> {
get_browser_ui_test_version_inner(builder, npm, false)
.or_else(|| get_browser_ui_test_version_inner(builder, npm, true))
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct RustdocGUI {
pub target: TargetSelection,
pub compiler: Compiler,
}
impl Step for RustdocGUI {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
let run = run.suite_path("tests/rustdoc-gui");
run.lazy_default_condition(Box::new(move || {
builder.config.nodejs.is_some()
&& builder.doc_tests != DocTests::Only
&& builder
.config
.npm
.as_ref()
.map(|p| get_browser_ui_test_version(builder, p).is_some())
.unwrap_or(false)
}))
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure(RustdocGUI { target: run.target, compiler });
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(compile::Std::new(self.compiler, self.target));
let mut cmd = builder.tool_cmd(Tool::RustdocGUITest);
let out_dir = builder.test_out(self.target).join("rustdoc-gui");
builder.clear_if_dirty(&out_dir, &builder.rustdoc(self.compiler));
if let Some(src) = builder.config.src.to_str() {
cmd.arg("--rust-src").arg(src);
}
if let Some(out_dir) = out_dir.to_str() {
cmd.arg("--out-dir").arg(out_dir);
}
if let Some(initial_cargo) = builder.config.initial_cargo.to_str() {
cmd.arg("--initial-cargo").arg(initial_cargo);
}
cmd.arg("--jobs").arg(builder.jobs().to_string());
cmd.env("RUSTDOC", builder.rustdoc(self.compiler))
.env("RUSTC", builder.rustc(self.compiler));
add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No);
for path in &builder.paths {
if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
if !p.ends_with(".goml") {
eprintln!("A non-goml file was given: `{}`", path.display());
panic!("Cannot run rustdoc-gui tests");
}
if let Some(name) = path.file_name().and_then(|f| f.to_str()) {
cmd.arg("--goml-file").arg(name);
}
}
}
for test_arg in builder.config.test_args() {
cmd.arg("--test-arg").arg(test_arg);
}
if let Some(ref nodejs) = builder.config.nodejs {
cmd.arg("--nodejs").arg(nodejs);
}
if let Some(ref npm) = builder.config.npm {
cmd.arg("--npm").arg(npm);
}
let _time = helpers::timeit(builder);
let _guard = builder.msg_sysroot_tool(
Kind::Test,
self.compiler.stage,
"rustdoc-gui",
self.compiler.host,
self.target,
);
try_run_tests(builder, &mut cmd, true);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Tidy;
impl Step for Tidy {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn run(self, builder: &Builder<'_>) {
let mut cmd = builder.tool_cmd(Tool::Tidy);
cmd.arg(&builder.src);
cmd.arg(&builder.initial_cargo);
cmd.arg(&builder.out);
let jobs = builder.config.jobs.unwrap_or_else(|| {
8 * std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
});
cmd.arg(jobs.to_string());
if builder.is_verbose() {
cmd.arg("--verbose");
}
if builder.config.cmd.bless() {
cmd.arg("--bless");
}
if let Some(s) = builder.config.cmd.extra_checks() {
cmd.arg(format!("--extra-checks={s}"));
}
let mut args = std::env::args_os();
if args.any(|arg| arg == OsStr::new("--")) {
cmd.arg("--");
cmd.args(args);
}
if builder.config.channel == "dev" || builder.config.channel == "nightly" {
if !builder.config.json_output {
builder.info("fmt check");
if builder.initial_rustfmt().is_none() {
let inferred_rustfmt_dir = builder.initial_sysroot.join("bin");
eprintln!(
"\
ERROR: no `rustfmt` binary found in {PATH}
INFO: `rust.channel` is currently set to \"{CHAN}\"
HELP: if you are testing a beta branch, set `rust.channel` to \"beta\" in the `config.toml` file
HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to `x.py test`",
PATH = inferred_rustfmt_dir.display(),
CHAN = builder.config.channel,
);
crate::exit!(1);
}
let all = false;
crate::core::build_steps::format::format(
builder,
!builder.config.cmd.bless(),
all,
&[],
);
} else {
eprintln!(
"WARNING: `--json-output` is not supported on rustfmt, formatting will be skipped"
);
}
}
builder.info("tidy check");
cmd.delay_failure().run(builder);
builder.info("x.py completions check");
let [bash, zsh, fish, powershell] = ["x.py.sh", "x.py.zsh", "x.py.fish", "x.py.ps1"]
.map(|filename| builder.src.join("src/etc/completions").join(filename));
if builder.config.cmd.bless() {
builder.ensure(crate::core::build_steps::run::GenerateCompletions);
} else if get_completion(shells::Bash, &bash).is_some()
|| get_completion(shells::Fish, &fish).is_some()
|| get_completion(shells::PowerShell, &powershell).is_some()
|| crate::flags::get_completion(shells::Zsh, &zsh).is_some()
{
eprintln!(
"x.py completions were changed; run `x.py run generate-completions` to update them"
);
crate::exit!(1);
}
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let default = run.builder.doc_tests != DocTests::Only;
run.path("src/tools/tidy").default_condition(default)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Tidy);
}
}
fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf {
builder.out.join(host).join("test")
}
macro_rules! default_test {
($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr }) => {
test!($name { path: $path, mode: $mode, suite: $suite, default: true, host: false });
};
}
macro_rules! default_test_with_compare_mode {
($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr,
compare_mode: $compare_mode:expr }) => {
test_with_compare_mode!($name {
path: $path,
mode: $mode,
suite: $suite,
default: true,
host: false,
compare_mode: $compare_mode
});
};
}
macro_rules! host_test {
($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr }) => {
test!($name { path: $path, mode: $mode, suite: $suite, default: true, host: true });
};
}
macro_rules! test {
($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr, default: $default:expr,
host: $host:expr }) => {
test_definitions!($name {
path: $path,
mode: $mode,
suite: $suite,
default: $default,
host: $host,
compare_mode: None
});
};
}
macro_rules! test_with_compare_mode {
($name:ident { path: $path:expr, mode: $mode:expr, suite: $suite:expr, default: $default:expr,
host: $host:expr, compare_mode: $compare_mode:expr }) => {
test_definitions!($name {
path: $path,
mode: $mode,
suite: $suite,
default: $default,
host: $host,
compare_mode: Some($compare_mode)
});
};
}
macro_rules! test_definitions {
($name:ident {
path: $path:expr,
mode: $mode:expr,
suite: $suite:expr,
default: $default:expr,
host: $host:expr,
compare_mode: $compare_mode:expr
}) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl Step for $name {
type Output = ();
const DEFAULT: bool = $default;
const ONLY_HOSTS: bool = $host;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.suite_path($path)
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure($name { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(Compiletest {
compiler: self.compiler,
target: self.target,
mode: $mode,
suite: $suite,
path: $path,
compare_mode: $compare_mode,
})
}
}
};
}
macro_rules! coverage_test_alias {
($name:ident {
alias_and_mode: $alias_and_mode:expr, default: $default:expr, only_hosts: $only_hosts:expr $(,)? }) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl $name {
const MODE: &'static str = $alias_and_mode;
}
impl Step for $name {
type Output = ();
const DEFAULT: bool = $default;
const ONLY_HOSTS: bool = $only_hosts;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.alias($alias_and_mode)
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure($name { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
Coverage::run_coverage_tests(builder, self.compiler, self.target, Self::MODE);
}
}
};
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct RunMakeSupport {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl Step for RunMakeSupport {
type Output = PathBuf;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.never()
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() });
}
fn run(self, builder: &Builder<'_>) -> PathBuf {
builder.ensure(compile::Std::new(self.compiler, self.target));
let cargo = tool::prepare_tool_cargo(
builder,
self.compiler,
Mode::ToolStd,
self.target,
Kind::Build,
"src/tools/run-make-support",
SourceType::InTree,
&[],
);
cargo.into_cmd().run(builder);
let lib_name = "librun_make_support.rlib";
let lib = builder.tools_dir(self.compiler).join(lib_name);
let cargo_out = builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(lib_name);
builder.copy_link(&cargo_out, &lib);
lib
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateRunMakeSupport {
host: TargetSelection,
}
impl Step for CrateRunMakeSupport {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/run-make-support")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(CrateRunMakeSupport { host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let host = self.host;
let compiler = builder.compiler(0, host);
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolBootstrap,
host,
Kind::Test,
"src/tools/run-make-support",
SourceType::InTree,
&[],
);
cargo.allow_features("test");
run_cargo_test(
cargo,
&[],
&[],
"run-make-support",
"run-make-support self test",
compiler,
host,
builder,
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateBuildHelper {
host: TargetSelection,
}
impl Step for CrateBuildHelper {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/build_helper")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(CrateBuildHelper { host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let host = self.host;
let compiler = builder.compiler(0, host);
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolBootstrap,
host,
Kind::Test,
"src/build_helper",
SourceType::InTree,
&[],
);
cargo.allow_features("test");
run_cargo_test(
cargo,
&[],
&[],
"build_helper",
"build_helper self test",
compiler,
host,
builder,
);
}
}
default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" });
default_test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes" });
default_test!(Codegen { path: "tests/codegen", mode: "codegen", suite: "codegen" });
default_test!(CodegenUnits {
path: "tests/codegen-units",
mode: "codegen-units",
suite: "codegen-units"
});
default_test!(Incremental { path: "tests/incremental", mode: "incremental", suite: "incremental" });
default_test_with_compare_mode!(Debuginfo {
path: "tests/debuginfo",
mode: "debuginfo",
suite: "debuginfo",
compare_mode: "split-dwarf"
});
host_test!(UiFullDeps { path: "tests/ui-fulldeps", mode: "ui", suite: "ui-fulldeps" });
host_test!(Rustdoc { path: "tests/rustdoc", mode: "rustdoc", suite: "rustdoc" });
host_test!(RustdocUi { path: "tests/rustdoc-ui", mode: "ui", suite: "rustdoc-ui" });
host_test!(RustdocJson { path: "tests/rustdoc-json", mode: "rustdoc-json", suite: "rustdoc-json" });
host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" });
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct RunMake {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl Step for RunMake {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.suite_path("tests/run-make")
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() });
run.builder.ensure(RunMake { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(Compiletest {
compiler: self.compiler,
target: self.target,
mode: "run-make",
suite: "run-make",
path: "tests/run-make",
compare_mode: None,
});
}
}
default_test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly" });
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Coverage {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl Coverage {
const PATH: &'static str = "tests/coverage";
const SUITE: &'static str = "coverage";
fn run_coverage_tests(
builder: &Builder<'_>,
compiler: Compiler,
target: TargetSelection,
mode: &'static str,
) {
builder.ensure(Compiletest {
compiler,
target,
mode,
suite: Self::SUITE,
path: Self::PATH,
compare_mode: None,
});
}
}
impl Step for Coverage {
type Output = ();
const DEFAULT: bool = false;
const ONLY_HOSTS: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.suite_path(Self::PATH)
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure(Coverage { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
Self::run_coverage_tests(builder, self.compiler, self.target, CoverageMap::MODE);
Self::run_coverage_tests(builder, self.compiler, self.target, CoverageRun::MODE);
}
}
coverage_test_alias!(CoverageMap {
alias_and_mode: "coverage-map",
default: true,
only_hosts: false,
});
coverage_test_alias!(CoverageRun {
alias_and_mode: "coverage-run",
default: true,
only_hosts: true,
});
host_test!(CoverageRunRustdoc {
path: "tests/coverage-run-rustdoc",
mode: "coverage-run",
suite: "coverage-run-rustdoc"
});
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MirOpt {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl Step for MirOpt {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = false;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.suite_path("tests/mir-opt")
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure(MirOpt { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
let run = |target| {
builder.ensure(Compiletest {
compiler: self.compiler,
target,
mode: "mir-opt",
suite: "mir-opt",
path: "tests/mir-opt",
compare_mode: None,
})
};
run(self.target);
if builder.config.cmd.bless() {
for target in ["aarch64-unknown-linux-gnu", "i686-pc-windows-msvc"] {
run(TargetSelection::from_user(target));
}
for target in ["x86_64-apple-darwin", "i686-unknown-linux-musl"] {
let target = TargetSelection::from_user(target);
let panic_abort_target = builder.ensure(MirOptPanicAbortSyntheticTarget {
compiler: self.compiler,
base: target,
});
run(panic_abort_target);
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Compiletest {
compiler: Compiler,
target: TargetSelection,
mode: &'static str,
suite: &'static str,
path: &'static str,
compare_mode: Option<&'static str>,
}
impl Step for Compiletest {
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.never()
}
fn run(self, builder: &Builder<'_>) {
if builder.doc_tests == DocTests::Only {
return;
}
if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() {
eprintln!("\
ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail
HELP: to test the compiler, use `--stage 1` instead
HELP: to test the standard library, use `--stage 0 library/std` instead
NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`."
);
crate::exit!(1);
}
let mut compiler = self.compiler;
let target = self.target;
let mode = self.mode;
let suite = self.suite;
let suite_path = self.path;
if !builder.config.codegen_tests && suite == "codegen" {
return;
}
let stage_id = if suite == "ui-fulldeps" && compiler.stage == 1 {
let build = builder.build.build;
compiler = builder.compiler(compiler.stage - 1, build);
format!("stage{}-{}", compiler.stage + 1, build)
} else {
format!("stage{}-{}", compiler.stage, target)
};
if suite.ends_with("fulldeps") {
builder.ensure(compile::Rustc::new(compiler, target));
}
if suite == "debuginfo" {
builder.ensure(dist::DebuggerScripts {
sysroot: builder.sysroot(compiler).to_path_buf(),
host: target,
});
}
builder.ensure(TestHelpers { target: compiler.host });
if suite == "mir-opt" {
builder.ensure(compile::Std::new_for_mir_opt_tests(compiler, compiler.host));
} else {
builder.ensure(compile::Std::new(compiler, compiler.host));
}
if suite != "mir-opt" {
builder.ensure(TestHelpers { target });
}
let mut cmd = builder.tool_cmd(Tool::Compiletest);
if suite == "mir-opt" {
builder.ensure(compile::Std::new_for_mir_opt_tests(compiler, target));
} else {
builder.ensure(compile::Std::new(compiler, target));
}
builder.ensure(RemoteCopyLibs { compiler, target });
cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(compiler));
cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(compiler, target));
cmd.arg("--rustc-path").arg(builder.rustc(compiler));
cmd.arg("--minicore-path")
.arg(builder.src.join("tests").join("auxiliary").join("minicore.rs"));
let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js");
if mode == "run-make" {
let cargo_path = if builder.top_stage == 0 {
builder.initial_cargo.clone()
} else {
let compiler = builder.download_rustc().then_some(compiler).unwrap_or_else(||
builder.compiler(builder.top_stage - 1, compiler.host));
builder.ensure(tool::Cargo { compiler, target: compiler.host })
};
cmd.arg("--cargo-path").arg(cargo_path);
}
if mode == "rustdoc"
|| mode == "run-make"
|| (mode == "ui" && is_rustdoc)
|| mode == "js-doc-test"
|| mode == "rustdoc-json"
|| suite == "coverage-run-rustdoc"
{
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler));
}
if mode == "rustdoc-json" {
let json_compiler = compiler.with_stage(0);
cmd.arg("--jsondocck-path")
.arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }));
cmd.arg("--jsondoclint-path")
.arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target }));
}
if matches!(mode, "coverage-map" | "coverage-run") {
let coverage_dump = builder.tool_exe(Tool::CoverageDump);
cmd.arg("--coverage-dump-path").arg(coverage_dump);
}
cmd.arg("--src-base").arg(builder.src.join("tests").join(suite));
cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite));
let sysroot = if builder.top_stage == 0 {
builder.initial_sysroot.clone()
} else {
builder.sysroot(compiler).to_path_buf()
};
cmd.arg("--sysroot-base").arg(sysroot);
cmd.arg("--stage-id").arg(stage_id);
cmd.arg("--suite").arg(suite);
cmd.arg("--mode").arg(mode);
cmd.arg("--target").arg(target.rustc_target_arg());
cmd.arg("--host").arg(&*compiler.host.triple);
cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.build));
if builder.build.config.llvm_enzyme {
cmd.arg("--has-enzyme");
}
if builder.config.cmd.bless() {
cmd.arg("--bless");
}
if builder.config.cmd.force_rerun() {
cmd.arg("--force-rerun");
}
let compare_mode =
builder.config.cmd.compare_mode().or_else(|| {
if builder.config.test_compare_mode { self.compare_mode } else { None }
});
if let Some(ref pass) = builder.config.cmd.pass() {
cmd.arg("--pass");
cmd.arg(pass);
}
if let Some(ref run) = builder.config.cmd.run() {
cmd.arg("--run");
cmd.arg(run);
}
if let Some(ref nodejs) = builder.config.nodejs {
cmd.arg("--nodejs").arg(nodejs);
} else if mode == "js-doc-test" {
panic!("need nodejs to run js-doc-test suite");
}
if let Some(ref npm) = builder.config.npm {
cmd.arg("--npm").arg(npm);
}
if builder.config.rust_optimize_tests {
cmd.arg("--optimize-tests");
}
if builder.config.rust_randomize_layout {
cmd.arg("--rust-randomized-layout");
}
if builder.config.cmd.only_modified() {
cmd.arg("--only-modified");
}
if let Some(compiletest_diff_tool) = &builder.config.compiletest_diff_tool {
cmd.arg("--compiletest-diff-tool").arg(compiletest_diff_tool);
}
let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));
flags.extend(builder.config.cmd.compiletest_rustc_args().iter().map(|s| s.to_string()));
if suite != "mir-opt" {
if let Some(linker) = builder.linker(target) {
cmd.arg("--target-linker").arg(linker);
}
if let Some(linker) = builder.linker(compiler.host) {
cmd.arg("--host-linker").arg(linker);
}
}
let mut hostflags = flags.clone();
hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
for flag in hostflags {
cmd.arg("--host-rustcflags").arg(flag);
}
let mut targetflags = flags;
targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
targetflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
for flag in targetflags {
cmd.arg("--target-rustcflags").arg(flag);
}
cmd.arg("--python").arg(builder.python());
if let Some(ref gdb) = builder.config.gdb {
cmd.arg("--gdb").arg(gdb);
}
let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
let lldb_version = command(&lldb_exe)
.allow_failure()
.arg("--version")
.run_capture(builder)
.stdout_if_ok()
.and_then(|v| if v.trim().is_empty() { None } else { Some(v) });
if let Some(ref vers) = lldb_version {
cmd.arg("--lldb-version").arg(vers);
let lldb_python_dir = command(&lldb_exe)
.allow_failure()
.arg("-P")
.run_capture_stdout(builder)
.stdout_if_ok()
.map(|p| p.lines().next().expect("lldb Python dir not found").to_string());
if let Some(ref dir) = lldb_python_dir {
cmd.arg("--lldb-python-dir").arg(dir);
}
}
if helpers::forcing_clang_based_tests() {
let clang_exe = builder.llvm_out(target).join("bin").join("clang");
cmd.arg("--run-clang-based-tests-with").arg(clang_exe);
}
for exclude in &builder.config.skip {
cmd.arg("--skip");
cmd.arg(exclude);
}
let paths = match &builder.config.cmd {
Subcommand::Test { .. } => &builder.config.paths[..],
_ => &[],
};
let mut test_args: Vec<&str> = paths
.iter()
.filter_map(|p| helpers::is_valid_test_suite_arg(p, suite_path, builder))
.collect();
test_args.append(&mut builder.config.test_args());
if cfg!(windows) {
let test_args_win: Vec<String> =
test_args.iter().map(|s| s.replace('/', "\\")).collect();
cmd.args(&test_args_win);
} else {
cmd.args(&test_args);
}
if builder.is_verbose() {
cmd.arg("--verbose");
}
cmd.arg("--json");
if builder.config.rustc_debug_assertions {
cmd.arg("--with-rustc-debug-assertions");
}
if builder.config.std_debug_assertions {
cmd.arg("--with-std-debug-assertions");
}
let mut llvm_components_passed = false;
let mut copts_passed = false;
if builder.config.llvm_enabled(compiler.host) {
let llvm::LlvmResult { llvm_config, .. } =
builder.ensure(llvm::Llvm { target: builder.config.build });
if !builder.config.dry_run() {
let llvm_version =
command(&llvm_config).arg("--version").run_capture_stdout(builder).stdout();
let llvm_components =
command(&llvm_config).arg("--components").run_capture_stdout(builder).stdout();
cmd.arg("--llvm-version")
.arg(llvm_version.trim())
.arg("--llvm-components")
.arg(llvm_components.trim());
llvm_components_passed = true;
}
if !builder.is_rust_llvm(target) {
cmd.arg("--system-llvm");
}
if !builder.config.dry_run() && suite.ends_with("fulldeps") {
let llvm_libdir =
command(&llvm_config).arg("--libdir").run_capture_stdout(builder).stdout();
add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd);
}
if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") {
let llvm_bin_path = llvm_config
.parent()
.expect("Expected llvm-config to be contained in directory");
assert!(llvm_bin_path.is_dir());
cmd.arg("--llvm-bin-dir").arg(llvm_bin_path);
}
if !builder.config.dry_run() && mode == "run-make" {
if builder.config.lld_enabled {
let lld_install_root =
builder.ensure(llvm::Lld { target: builder.config.build });
let lld_bin_path = lld_install_root.join("bin");
let old_path = env::var_os("PATH").unwrap_or_default();
let new_path = env::join_paths(
std::iter::once(lld_bin_path).chain(env::split_paths(&old_path)),
)
.expect("Could not add LLD bin path to PATH");
cmd.env("PATH", new_path);
}
}
}
if !builder.config.dry_run() && mode == "run-make" {
cmd.arg("--cc")
.arg(builder.cc(target))
.arg("--cxx")
.arg(builder.cxx(target).unwrap())
.arg("--cflags")
.arg(builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "))
.arg("--cxxflags")
.arg(builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "));
copts_passed = true;
if let Some(ar) = builder.ar(target) {
cmd.arg("--ar").arg(ar);
}
}
if !llvm_components_passed {
cmd.arg("--llvm-components").arg("");
}
if !copts_passed {
cmd.arg("--cc")
.arg("")
.arg("--cxx")
.arg("")
.arg("--cflags")
.arg("")
.arg("--cxxflags")
.arg("");
}
if builder.remote_tested(target) {
cmd.arg("--remote-test-client").arg(builder.tool_exe(Tool::RemoteTestClient));
} else if let Some(tool) = builder.runner(target) {
cmd.arg("--runner").arg(tool);
}
if suite != "mir-opt" {
if !builder.config.dry_run() && target.is_msvc() {
for (k, v) in builder.cc.borrow()[&target].env() {
if k != "PATH" {
cmd.env(k, v);
}
}
}
}
if !builder.config.dry_run()
&& target.contains("msvc")
&& builder.config.sanitizers_enabled(target)
{
cmd.env("ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE", "1");
let asan_runtime_path =
builder.cc.borrow()[&target].path().parent().unwrap().to_path_buf();
let old_path = cmd
.get_envs()
.find_map(|(k, v)| (k == "PATH").then_some(v))
.flatten()
.map_or_else(|| env::var_os("PATH").unwrap_or_default(), |v| v.to_owned());
let new_path = env::join_paths(
env::split_paths(&old_path).chain(std::iter::once(asan_runtime_path)),
)
.expect("Could not add ASAN runtime path to PATH");
cmd.env("PATH", new_path);
}
cmd.env_remove("CARGO");
cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.env("RUSTC_FORCE_RUSTC_VERSION", "compiletest");
cmd.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
builder.add_rust_test_threads(&mut cmd);
if builder.config.sanitizers_enabled(target) {
cmd.env("RUSTC_SANITIZER_SUPPORT", "1");
}
if builder.config.profiler_enabled(target) {
cmd.arg("--profiler-runtime");
}
cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
cmd.arg("--adb-path").arg("adb");
cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
if target.contains("android") && !builder.config.dry_run() {
cmd.arg("--android-cross-path")
.arg(builder.cc(target).parent().unwrap().parent().unwrap());
} else {
cmd.arg("--android-cross-path").arg("");
}
if builder.config.cmd.rustfix_coverage() {
cmd.arg("--rustfix-coverage");
}
cmd.arg("--channel").arg(&builder.config.channel);
if !builder.config.omit_git_hash {
cmd.arg("--git-hash");
}
let git_config = builder.config.git_config();
cmd.arg("--git-repository").arg(git_config.git_repository);
cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email);
cmd.force_coloring_in_ci();
#[cfg(feature = "build-metrics")]
builder.metrics.begin_test_suite(
build_helper::metrics::TestSuiteMetadata::Compiletest {
suite: suite.into(),
mode: mode.into(),
compare_mode: None,
target: self.target.triple.to_string(),
host: self.compiler.host.triple.to_string(),
stage: self.compiler.stage,
},
builder,
);
let _group = builder.msg(
Kind::Test,
compiler.stage,
format!("compiletest suite={suite} mode={mode}"),
compiler.host,
target,
);
try_run_tests(builder, &mut cmd, false);
if let Some(compare_mode) = compare_mode {
cmd.arg("--compare-mode").arg(compare_mode);
#[cfg(feature = "build-metrics")]
builder.metrics.begin_test_suite(
build_helper::metrics::TestSuiteMetadata::Compiletest {
suite: suite.into(),
mode: mode.into(),
compare_mode: Some(compare_mode.into()),
target: self.target.triple.to_string(),
host: self.compiler.host.triple.to_string(),
stage: self.compiler.stage,
},
builder,
);
builder.info(&format!(
"Check compiletest suite={} mode={} compare_mode={} ({} -> {})",
suite, mode, compare_mode, &compiler.host, target
));
let _time = helpers::timeit(builder);
try_run_tests(builder, &mut cmd, false);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct BookTest {
compiler: Compiler,
path: PathBuf,
name: &'static str,
is_ext_doc: bool,
dependencies: Vec<&'static str>,
}
impl Step for BookTest {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.never()
}
fn run(self, builder: &Builder<'_>) {
if self.is_ext_doc {
self.run_ext_doc(builder);
} else {
self.run_local_doc(builder);
}
}
}
impl BookTest {
fn run_ext_doc(self, builder: &Builder<'_>) {
let compiler = self.compiler;
builder.ensure(compile::Std::new(compiler, compiler.host));
let mut rustdoc_path = builder.rustdoc(compiler);
rustdoc_path.pop();
let old_path = env::var_os("PATH").unwrap_or_default();
let new_path = env::join_paths(iter::once(rustdoc_path).chain(env::split_paths(&old_path)))
.expect("could not add rustdoc to PATH");
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
let path = builder.src.join(&self.path);
rustbook_cmd.env("RUSTC_BOOTSTRAP", "1");
rustbook_cmd.env("PATH", new_path).arg("test").arg(path);
let libs = if !self.dependencies.is_empty() {
let mut lib_paths = vec![];
for dep in self.dependencies {
let mode = Mode::ToolRustc;
let target = builder.config.build;
let cargo = tool::prepare_tool_cargo(
builder,
compiler,
mode,
target,
Kind::Build,
dep,
SourceType::Submodule,
&[],
);
let stamp = builder
.cargo_out(compiler, mode, target)
.join(PathBuf::from(dep).file_name().unwrap())
.with_extension("stamp");
let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
let directories = output_paths
.into_iter()
.filter_map(|p| p.parent().map(ToOwned::to_owned))
.fold(HashSet::new(), |mut set, dir| {
set.insert(dir);
set
});
lib_paths.extend(directories);
}
lib_paths
} else {
vec![]
};
if !libs.is_empty() {
let paths = libs
.into_iter()
.map(|path| path.into_os_string())
.collect::<Vec<OsString>>()
.join(OsStr::new(","));
rustbook_cmd.args([OsString::from("--library-path"), paths]);
}
builder.add_rust_test_threads(&mut rustbook_cmd);
let _guard = builder.msg(
Kind::Test,
compiler.stage,
format_args!("mdbook {}", self.path.display()),
compiler.host,
compiler.host,
);
let _time = helpers::timeit(builder);
let toolstate = if rustbook_cmd.delay_failure().run(builder) {
ToolState::TestPass
} else {
ToolState::TestFail
};
builder.save_toolstate(self.name, toolstate);
}
fn run_local_doc(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let host = self.compiler.host;
builder.ensure(compile::Std::new(compiler, host));
let _guard =
builder.msg(Kind::Test, compiler.stage, format!("book {}", self.name), host, host);
let mut stack = vec![builder.src.join(self.path)];
let _time = helpers::timeit(builder);
let mut files = Vec::new();
while let Some(p) = stack.pop() {
if p.is_dir() {
stack.extend(t!(p.read_dir()).map(|p| t!(p).path()));
continue;
}
if p.extension().and_then(|s| s.to_str()) != Some("md") {
continue;
}
files.push(p);
}
files.sort();
for file in files {
markdown_test(builder, compiler, &file);
}
}
}
macro_rules! test_book {
($(
$name:ident, $path:expr, $book_name:expr,
default=$default:expr
$(,submodules = $submodules:expr)?
$(,dependencies=$dependencies:expr)?
;
)+) => {
$(
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name {
compiler: Compiler,
}
impl Step for $name {
type Output = ();
const DEFAULT: bool = $default;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path($path)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure($name {
compiler: run.builder.compiler(run.builder.top_stage, run.target),
});
}
fn run(self, builder: &Builder<'_>) {
$(
for submodule in $submodules {
builder.require_submodule(submodule, None);
}
)*
let dependencies = vec![];
$(
let mut dependencies = dependencies;
for dep in $dependencies {
dependencies.push(dep);
}
)?
builder.ensure(BookTest {
compiler: self.compiler,
path: PathBuf::from($path),
name: $book_name,
is_ext_doc: !$default,
dependencies,
});
}
}
)+
}
}
test_book!(
Nomicon, "src/doc/nomicon", "nomicon", default=false, submodules=["src/doc/nomicon"];
Reference, "src/doc/reference", "reference", default=false, submodules=["src/doc/reference"];
RustdocBook, "src/doc/rustdoc", "rustdoc", default=true;
RustcBook, "src/doc/rustc", "rustc", default=true;
RustByExample, "src/doc/rust-by-example", "rust-by-example", default=false, submodules=["src/doc/rust-by-example"];
EmbeddedBook, "src/doc/embedded-book", "embedded-book", default=false, submodules=["src/doc/embedded-book"];
TheBook, "src/doc/book", "book", default=false, submodules=["src/doc/book"], dependencies=["src/doc/book/packages/trpl"];
UnstableBook, "src/doc/unstable-book", "unstable-book", default=true;
EditionGuide, "src/doc/edition-guide", "edition-guide", default=false, submodules=["src/doc/edition-guide"];
);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ErrorIndex {
compiler: Compiler,
}
impl Step for ErrorIndex {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/error_index_generator").alias("error-index")
}
fn make_run(run: RunConfig<'_>) {
let compiler = run.builder.compiler(run.builder.top_stage, run.builder.config.build);
run.builder.ensure(ErrorIndex { compiler });
}
fn run(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let dir = testdir(builder, compiler.host);
t!(fs::create_dir_all(&dir));
let output = dir.join("error-index.md");
let mut tool = tool::ErrorIndex::command(builder);
tool.arg("markdown").arg(&output);
let guard =
builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
let _time = helpers::timeit(builder);
tool.run_capture(builder);
drop(guard);
builder.ensure(compile::Std::new(compiler, compiler.host));
markdown_test(builder, compiler, &output);
}
}
fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> bool {
if let Ok(contents) = fs::read_to_string(markdown) {
if !contents.contains("```") {
return true;
}
}
builder.verbose(|| println!("doc tests for: {}", markdown.display()));
let mut cmd = builder.rustdoc_cmd(compiler);
builder.add_rust_test_threads(&mut cmd);
cmd.arg("-Z");
cmd.arg("unstable-options");
cmd.arg("--test");
cmd.arg(markdown);
cmd.env("RUSTC_BOOTSTRAP", "1");
let test_args = builder.config.test_args().join(" ");
cmd.arg("--test-args").arg(test_args);
cmd = cmd.delay_failure();
if !builder.config.verbose_tests {
cmd.run_capture(builder).is_success()
} else {
cmd.run(builder)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RustcGuide;
impl Step for RustcGuide {
type Output = ();
const DEFAULT: bool = false;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/doc/rustc-dev-guide")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(RustcGuide);
}
fn run(self, builder: &Builder<'_>) {
let relative_path = "src/doc/rustc-dev-guide";
builder.require_submodule(relative_path, None);
let src = builder.src.join(relative_path);
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook).delay_failure();
rustbook_cmd.arg("linkcheck").arg(&src);
let toolstate =
if rustbook_cmd.run(builder) { ToolState::TestPass } else { ToolState::TestFail };
builder.save_toolstate("rustc-dev-guide", toolstate);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateLibrustc {
compiler: Compiler,
target: TargetSelection,
crates: Vec<String>,
}
impl Step for CrateLibrustc {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.crate_or_deps("rustc-main").path("compiler")
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
let host = run.build_triple();
let compiler = builder.compiler_for(builder.top_stage, host, host);
let crates = run.make_run_crates(Alias::Compiler);
builder.ensure(CrateLibrustc { compiler, target: run.target, crates });
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(compile::Std::new(self.compiler, self.target));
builder.ensure(Crate {
compiler: self.compiler,
target: self.target,
mode: Mode::Rustc,
crates: self.crates,
});
}
}
#[allow(clippy::too_many_arguments)] fn run_cargo_test<'a>(
cargo: impl Into<BootstrapCommand>,
libtest_args: &[&str],
crates: &[String],
primary_crate: &str,
description: impl Into<Option<&'a str>>,
compiler: Compiler,
target: TargetSelection,
builder: &Builder<'_>,
) -> bool {
let mut cargo =
prepare_cargo_test(cargo, libtest_args, crates, primary_crate, compiler, target, builder);
let _time = helpers::timeit(builder);
let _group = description.into().and_then(|what| {
builder.msg_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target)
});
#[cfg(feature = "build-metrics")]
builder.metrics.begin_test_suite(
build_helper::metrics::TestSuiteMetadata::CargoPackage {
crates: crates.iter().map(|c| c.to_string()).collect(),
target: target.triple.to_string(),
host: compiler.host.triple.to_string(),
stage: compiler.stage,
},
builder,
);
add_flags_and_try_run_tests(builder, &mut cargo)
}
fn prepare_cargo_test(
cargo: impl Into<BootstrapCommand>,
libtest_args: &[&str],
crates: &[String],
primary_crate: &str,
compiler: Compiler,
target: TargetSelection,
builder: &Builder<'_>,
) -> BootstrapCommand {
let mut cargo = cargo.into();
if builder.config.cmd.bless() && !cargo.get_envs().any(|v| v.0 == "RUSTC_BLESS") {
cargo.env("RUSTC_BLESS", "Gesundheit");
}
if builder.kind == Kind::Test && !builder.fail_fast {
cargo.arg("--no-fail-fast");
}
if builder.config.json_output {
cargo.arg("--message-format=json");
}
match builder.doc_tests {
DocTests::Only => {
cargo.arg("--doc");
}
DocTests::No => {
let krate = &builder
.crates
.get(primary_crate)
.unwrap_or_else(|| panic!("missing crate {primary_crate}"));
if krate.has_lib {
cargo.arg("--lib");
}
cargo.args(["--bins", "--examples", "--tests", "--benches"]);
}
DocTests::Yes => {}
}
for krate in crates {
cargo.arg("-p").arg(krate);
}
cargo.arg("--").args(builder.config.test_args()).args(libtest_args);
if !builder.config.verbose_tests {
cargo.arg("--quiet");
}
if builder.kind != Kind::Miri {
let mut dylib_path = dylib_path();
dylib_path.insert(0, PathBuf::from(&*builder.sysroot_target_libdir(compiler, target)));
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
}
if builder.remote_tested(target) {
cargo.env(
format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)),
format!("{} run 0", builder.tool_exe(Tool::RemoteTestClient).display()),
);
} else if let Some(tool) = builder.runner(target) {
cargo.env(format!("CARGO_TARGET_{}_RUNNER", envify(&target.triple)), tool);
}
cargo
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Crate {
pub compiler: Compiler,
pub target: TargetSelection,
pub mode: Mode,
pub crates: Vec<String>,
}
impl Step for Crate {
type Output = ();
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.crate_or_deps("sysroot")
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
let host = run.build_triple();
let compiler = builder.compiler_for(builder.top_stage, host, host);
let crates = run
.paths
.iter()
.map(|p| builder.crate_paths[&p.assert_single_path().path].clone())
.collect();
builder.ensure(Crate { compiler, target: run.target, mode: Mode::Std, crates });
}
fn run(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let target = self.target;
let mode = self.mode;
builder.ensure(compile::Std::force_recompile(compiler, compiler.host));
let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
let mut cargo = if builder.kind == Kind::Miri {
if builder.top_stage == 0 {
eprintln!("ERROR: `x.py miri` requires stage 1 or higher");
std::process::exit(1);
}
let mut cargo = builder::Cargo::new(
builder,
compiler,
mode,
SourceType::InTree,
target,
Kind::MiriTest,
);
cargo.env("MIRI_REPLACE_LIBRS_IF_NOT_TEST", "1");
cargo.rustflag("-Zforce-unstable-if-unmarked");
cargo
} else {
if builder.config.build != target {
builder.ensure(compile::Std::force_recompile(compiler, target));
builder.ensure(RemoteCopyLibs { compiler, target });
}
builder::Cargo::new(builder, compiler, mode, SourceType::InTree, target, builder.kind)
};
match mode {
Mode::Std => {
if builder.kind == Kind::Miri {
cargo
.arg("--manifest-path")
.arg(builder.src.join("library/sysroot/Cargo.toml"));
} else {
compile::std_cargo(builder, target, compiler.stage, &mut cargo);
if builder.download_rustc() && compiler.stage > 0 {
let sysroot = builder
.out
.join(compiler.host)
.join(format!("stage{}-test-sysroot", compiler.stage));
cargo.env("RUSTC_SYSROOT", sysroot);
}
}
}
Mode::Rustc => {
compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
}
_ => panic!("can only test libraries"),
};
run_cargo_test(
cargo,
&[],
&self.crates,
&self.crates[0],
&*crate_description(&self.crates),
compiler,
target,
builder,
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateRustdoc {
host: TargetSelection,
}
impl Step for CrateRustdoc {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["src/librustdoc", "src/tools/rustdoc"])
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
builder.ensure(CrateRustdoc { host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let target = self.host;
let compiler = if builder.download_rustc() {
builder.compiler(builder.top_stage, target)
} else {
builder.compiler_for(builder.top_stage, target, target)
};
builder.ensure(compile::Std::new(compiler, target));
builder.ensure(compile::Rustc::new(compiler, target));
let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
target,
builder.kind,
"src/tools/rustdoc",
SourceType::InTree,
&[],
);
if self.host.contains("musl") {
cargo.arg("'-Ctarget-feature=-crt-static'");
}
let libdir = if builder.download_rustc() {
builder.rustc_libdir(compiler)
} else {
builder.sysroot_target_libdir(compiler, target).to_path_buf()
};
let mut dylib_path = dylib_path();
dylib_path.insert(0, PathBuf::from(&*libdir));
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
run_cargo_test(
cargo,
&[],
&["rustdoc:0.0.0".to_string()],
"rustdoc",
"rustdoc",
compiler,
target,
builder,
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrateRustdocJsonTypes {
host: TargetSelection,
}
impl Step for CrateRustdocJsonTypes {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/rustdoc-json-types")
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
builder.ensure(CrateRustdocJsonTypes { host: run.target });
}
fn run(self, builder: &Builder<'_>) {
let target = self.host;
let compiler = builder.compiler_for(builder.top_stage, target, target);
builder.ensure(compile::Rustc::new(compiler, target));
let cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolRustc,
target,
builder.kind,
"src/rustdoc-json-types",
SourceType::InTree,
&[],
);
let libtest_args = if self.host.contains("musl") {
["'-Ctarget-feature=-crt-static'"].as_slice()
} else {
&[]
};
run_cargo_test(
cargo,
libtest_args,
&["rustdoc-json-types".to_string()],
"rustdoc-json-types",
"rustdoc-json-types",
compiler,
target,
builder,
);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RemoteCopyLibs {
compiler: Compiler,
target: TargetSelection,
}
impl Step for RemoteCopyLibs {
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.never()
}
fn run(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let target = self.target;
if !builder.remote_tested(target) {
return;
}
builder.ensure(compile::Std::new(compiler, target));
builder.info(&format!("REMOTE copy libs to emulator ({target})"));
let server = builder.ensure(tool::RemoteTestServer { compiler, target });
let tool = builder.tool_exe(Tool::RemoteTestClient);
let mut cmd = command(&tool);
cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir());
if let Some(rootfs) = builder.qemu_rootfs(target) {
cmd.arg(rootfs);
}
cmd.run(builder);
for f in t!(builder.sysroot_target_libdir(compiler, target).read_dir()) {
let f = t!(f);
if helpers::is_dylib(&f.path()) {
command(&tool).arg("push").arg(f.path()).run(builder);
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Distcheck;
impl Step for Distcheck {
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.alias("distcheck")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Distcheck);
}
fn run(self, builder: &Builder<'_>) {
builder.info("Distcheck");
let dir = builder.tempdir().join("distcheck");
let _ = fs::remove_dir_all(&dir);
t!(fs::create_dir_all(&dir));
builder.ensure(dist::PlainSourceTarball);
builder.ensure(dist::Src);
command("tar")
.arg("-xf")
.arg(builder.ensure(dist::PlainSourceTarball).tarball())
.arg("--strip-components=1")
.current_dir(&dir)
.run(builder);
command("./configure")
.args(&builder.config.configure_args)
.arg("--enable-vendor")
.current_dir(&dir)
.run(builder);
command(helpers::make(&builder.config.build.triple))
.arg("check")
.current_dir(&dir)
.run(builder);
builder.info("Distcheck rust-src");
let dir = builder.tempdir().join("distcheck-src");
let _ = fs::remove_dir_all(&dir);
t!(fs::create_dir_all(&dir));
command("tar")
.arg("-xf")
.arg(builder.ensure(dist::Src).tarball())
.arg("--strip-components=1")
.current_dir(&dir)
.run(builder);
let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
command(&builder.initial_cargo)
.env("RUSTC_BOOTSTRAP", "1")
.arg("generate-lockfile")
.arg("--manifest-path")
.arg(&toml)
.current_dir(&dir)
.run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Bootstrap;
impl Step for Bootstrap {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn run(self, builder: &Builder<'_>) {
let host = builder.config.build;
let compiler = builder.compiler(0, host);
let _guard = builder.msg(Kind::Test, 0, "bootstrap", host, host);
builder.build.require_submodule("src/tools/cargo", None);
let mut check_bootstrap = command(builder.python());
check_bootstrap
.args(["-m", "unittest", "bootstrap_test.py"])
.env("BUILD_DIR", &builder.out)
.env("BUILD_PLATFORM", builder.build.build.triple)
.env("BOOTSTRAP_TEST_RUSTC_BIN", &builder.initial_rustc)
.env("BOOTSTRAP_TEST_CARGO_BIN", &builder.initial_cargo)
.current_dir(builder.src.join("src/bootstrap/"));
check_bootstrap.delay_failure().run(builder);
let mut cmd = command(&builder.initial_cargo);
cmd.arg("test")
.args(["--features", "bootstrap-self-test"])
.current_dir(builder.src.join("src/bootstrap"))
.env("RUSTFLAGS", "-Cdebuginfo=2")
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
.env("RUSTC_BOOTSTRAP", "1")
.env("RUSTDOC", builder.rustdoc(compiler))
.env("RUSTC", &builder.initial_rustc);
if let Some(flags) = option_env!("RUSTFLAGS") {
cmd.env("RUSTFLAGS", flags);
}
run_cargo_test(cmd, &["--test-threads=1"], &[], "bootstrap", None, compiler, host, builder);
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/bootstrap")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Bootstrap);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TierCheck {
pub compiler: Compiler,
}
impl Step for TierCheck {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/tier-check")
}
fn make_run(run: RunConfig<'_>) {
let compiler =
run.builder.compiler_for(run.builder.top_stage, run.builder.build.build, run.target);
run.builder.ensure(TierCheck { compiler });
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
let mut cargo = tool::prepare_tool_cargo(
builder,
self.compiler,
Mode::ToolStd,
self.compiler.host,
Kind::Run,
"src/tools/tier-check",
SourceType::InTree,
&[],
);
cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md"));
cargo.arg(builder.rustc(self.compiler));
if builder.is_verbose() {
cargo.arg("--verbose");
}
let _guard = builder.msg(
Kind::Test,
self.compiler.stage,
"platform support check",
self.compiler.host,
self.compiler.host,
);
BootstrapCommand::from(cargo).delay_failure().run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LintDocs {
pub compiler: Compiler,
pub target: TargetSelection,
}
impl Step for LintDocs {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/lint-docs")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(LintDocs {
compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build),
target: run.target,
});
}
fn run(self, builder: &Builder<'_>) {
builder.ensure(crate::core::build_steps::doc::RustcBook {
compiler: self.compiler,
target: self.target,
validate: true,
});
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RustInstaller;
impl Step for RustInstaller {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = true;
fn run(self, builder: &Builder<'_>) {
let bootstrap_host = builder.config.build;
let compiler = builder.compiler(0, bootstrap_host);
let cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolBootstrap,
bootstrap_host,
Kind::Test,
"src/tools/rust-installer",
SourceType::InTree,
&[],
);
let _guard = builder.msg(
Kind::Test,
compiler.stage,
"rust-installer",
bootstrap_host,
bootstrap_host,
);
run_cargo_test(cargo, &[], &[], "installer", None, compiler, bootstrap_host, builder);
if bootstrap_host != "x86_64-unknown-linux-gnu" {
return;
}
let mut cmd = command(builder.src.join("src/tools/rust-installer/test.sh"));
let tmpdir = testdir(builder, compiler.host).join("rust-installer");
let _ = std::fs::remove_dir_all(&tmpdir);
let _ = std::fs::create_dir_all(&tmpdir);
cmd.current_dir(&tmpdir);
cmd.env("CARGO_TARGET_DIR", tmpdir.join("cargo-target"));
cmd.env("CARGO", &builder.initial_cargo);
cmd.env("RUSTC", &builder.initial_rustc);
cmd.env("TMP_DIR", &tmpdir);
cmd.delay_failure().run(builder);
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/rust-installer")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Self);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TestHelpers {
pub target: TargetSelection,
}
impl Step for TestHelpers {
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("tests/auxiliary/rust_test_helpers.c")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(TestHelpers { target: run.target })
}
fn run(self, builder: &Builder<'_>) {
if builder.config.dry_run() {
return;
}
let target = if self.target == "x86_64-fortanix-unknown-sgx" {
TargetSelection::from_user("x86_64-unknown-linux-gnu")
} else {
self.target
};
let dst = builder.test_helpers_out(target);
let src = builder.src.join("tests/auxiliary/rust_test_helpers.c");
if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
return;
}
let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target);
t!(fs::create_dir_all(&dst));
let mut cfg = cc::Build::new();
if !target.is_msvc() {
if let Some(ar) = builder.ar(target) {
cfg.archiver(ar);
}
cfg.compiler(builder.cc(target));
}
cfg.cargo_metadata(false)
.out_dir(&dst)
.target(&target.triple)
.host(&builder.config.build.triple)
.opt_level(0)
.warnings(false)
.debug(false)
.file(builder.src.join("tests/auxiliary/rust_test_helpers.c"))
.compile("rust_test_helpers");
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CodegenCranelift {
compiler: Compiler,
target: TargetSelection,
}
impl Step for CodegenCranelift {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["compiler/rustc_codegen_cranelift"])
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
let host = run.build_triple();
let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
if builder.doc_tests == DocTests::Only {
return;
}
if builder.download_rustc() {
builder.info("CI rustc uses the default codegen backend. skipping");
return;
}
if !target_supports_cranelift_backend(run.target) {
builder.info("target not supported by rustc_codegen_cranelift. skipping");
return;
}
if builder.remote_tested(run.target) {
builder.info("remote testing is not supported by rustc_codegen_cranelift. skipping");
return;
}
if !builder.config.codegen_backends(run.target).contains(&"cranelift".to_owned()) {
builder.info("cranelift not in rust.codegen-backends. skipping");
return;
}
builder.ensure(CodegenCranelift { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let target = self.target;
builder.ensure(compile::Std::new(compiler, target));
let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
let build_cargo = || {
let mut cargo = builder::Cargo::new(
builder,
compiler,
Mode::Codegen, SourceType::InTree,
target,
Kind::Run,
);
cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
cargo
.arg("--manifest-path")
.arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
compile::rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
cargo.env("CARGO_BUILD_INCREMENTAL", "false");
cargo
};
builder.info(&format!(
"{} cranelift stage{} ({} -> {})",
Kind::Test.description(),
compiler.stage,
&compiler.host,
target
));
let _time = helpers::timeit(builder);
let download_dir = builder.out.join("cg_clif_download");
let mut cargo = build_cargo();
cargo
.arg("--")
.arg("test")
.arg("--download-dir")
.arg(&download_dir)
.arg("--out-dir")
.arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_clif"))
.arg("--no-unstable-features")
.arg("--use-backend")
.arg("cranelift")
.arg("--sysroot")
.arg("llvm")
.arg("--skip-test")
.arg("testsuite.extended_sysroot");
cargo.into_cmd().run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CodegenGCC {
compiler: Compiler,
target: TargetSelection,
}
impl Step for CodegenGCC {
type Output = ();
const DEFAULT: bool = true;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.paths(&["compiler/rustc_codegen_gcc"])
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
let host = run.build_triple();
let compiler = run.builder.compiler_for(run.builder.top_stage, host, host);
if builder.doc_tests == DocTests::Only {
return;
}
if builder.download_rustc() {
builder.info("CI rustc uses the default codegen backend. skipping");
return;
}
let triple = run.target.triple;
let target_supported =
if triple.contains("linux") { triple.contains("x86_64") } else { false };
if !target_supported {
builder.info("target not supported by rustc_codegen_gcc. skipping");
return;
}
if builder.remote_tested(run.target) {
builder.info("remote testing is not supported by rustc_codegen_gcc. skipping");
return;
}
if !builder.config.codegen_backends(run.target).contains(&"gcc".to_owned()) {
builder.info("gcc not in rust.codegen-backends. skipping");
return;
}
builder.ensure(CodegenGCC { compiler, target: run.target });
}
fn run(self, builder: &Builder<'_>) {
let compiler = self.compiler;
let target = self.target;
builder.ensure(compile::Std::new_with_extra_rust_args(compiler, target, &[
"-Csymbol-mangling-version=v0",
"-Cpanic=abort",
]));
let compiler = builder.compiler_for(compiler.stage, compiler.host, target);
let build_cargo = || {
let mut cargo = builder::Cargo::new(
builder,
compiler,
Mode::Codegen, SourceType::InTree,
target,
Kind::Run,
);
cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
cargo
.arg("--manifest-path")
.arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
compile::rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
cargo.env("CARGO_BUILD_INCREMENTAL", "false");
cargo.rustflag("-Cpanic=abort");
cargo
};
builder.info(&format!(
"{} GCC stage{} ({} -> {})",
Kind::Test.description(),
compiler.stage,
&compiler.host,
target
));
let _time = helpers::timeit(builder);
let mut cargo = build_cargo();
cargo
.arg("--")
.arg("test")
.arg("--use-system-gcc")
.arg("--use-backend")
.arg("gcc")
.arg("--out-dir")
.arg(builder.stage_out(compiler, Mode::ToolRustc).join("cg_gcc"))
.arg("--release")
.arg("--mini-tests")
.arg("--std-tests");
cargo.args(builder.config.test_args());
cargo.into_cmd().run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TestFloatParse {
path: PathBuf,
host: TargetSelection,
}
impl Step for TestFloatParse {
type Output = ();
const ONLY_HOSTS: bool = true;
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/etc/test-float-parse")
}
fn make_run(run: RunConfig<'_>) {
for path in run.paths {
let path = path.assert_single_path().path.clone();
run.builder.ensure(Self { path, host: run.target });
}
}
fn run(self, builder: &Builder<'_>) {
let bootstrap_host = builder.config.build;
let compiler = builder.compiler(builder.top_stage, bootstrap_host);
let path = self.path.to_str().unwrap();
let crate_name = self.path.components().last().unwrap().as_os_str().to_str().unwrap();
builder.ensure(tool::TestFloatParse { host: self.host });
let cargo_test = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolStd,
bootstrap_host,
Kind::Test,
path,
SourceType::InTree,
&[],
);
run_cargo_test(
cargo_test,
&[],
&[],
crate_name,
crate_name,
compiler,
bootstrap_host,
builder,
);
let mut cargo_run = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolStd,
bootstrap_host,
Kind::Run,
path,
SourceType::InTree,
&[],
);
if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) {
cargo_run.args(["--", "--skip-huge"]);
}
cargo_run.into_cmd().run(builder);
}
}
#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
pub struct CollectLicenseMetadata;
impl Step for CollectLicenseMetadata {
type Output = PathBuf;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/collect-license-metadata")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(CollectLicenseMetadata);
}
fn run(self, builder: &Builder<'_>) -> Self::Output {
let Some(reuse) = &builder.config.reuse else {
panic!("REUSE is required to collect the license metadata");
};
let dest = builder.src.join("license-metadata.json");
let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
cmd.env("REUSE_EXE", reuse);
cmd.env("DEST", &dest);
cmd.env("ONLY_CHECK", "1");
cmd.run(builder);
dest
}
}