bootstrap/core/build_steps/
tool.rs

1//! This module handles building and managing various tools in bootstrap
2//! build system.
3//!
4//! **What It Does**
5//! - Defines how tools are built, configured and installed.
6//! - Manages tool dependencies and build steps.
7//! - Copies built tool binaries to the correct locations.
8//!
9//! Each Rust tool **MUST** utilize `ToolBuild` inside their `Step` logic,
10//! return `ToolBuildResult` and should never prepare `cargo` invocations manually.
11
12use std::ffi::OsStr;
13use std::path::PathBuf;
14use std::{env, fs};
15
16#[cfg(feature = "tracing")]
17use tracing::instrument;
18
19use crate::core::build_steps::compile::is_lto_stage;
20use crate::core::build_steps::toolstate::ToolState;
21use crate::core::build_steps::{compile, llvm};
22use crate::core::builder;
23use crate::core::builder::{
24    Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step, StepMetadata, cargo_profile_var,
25};
26use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection};
27use crate::utils::exec::{BootstrapCommand, command};
28use crate::utils::helpers::{add_dylib_path, exe, t};
29use crate::{Compiler, FileType, Kind, Mode, gha};
30
31#[derive(Debug, Clone, Hash, PartialEq, Eq)]
32pub enum SourceType {
33    InTree,
34    Submodule,
35}
36
37#[derive(Debug, Clone, Hash, PartialEq, Eq)]
38pub enum ToolArtifactKind {
39    Binary,
40    Library,
41}
42
43#[derive(Debug, Clone, Hash, PartialEq, Eq)]
44struct ToolBuild {
45    /// Compiler that will build this tool.
46    build_compiler: Compiler,
47    target: TargetSelection,
48    tool: &'static str,
49    path: &'static str,
50    mode: Mode,
51    source_type: SourceType,
52    extra_features: Vec<String>,
53    /// Nightly-only features that are allowed (comma-separated list).
54    allow_features: &'static str,
55    /// Additional arguments to pass to the `cargo` invocation.
56    cargo_args: Vec<String>,
57    /// Whether the tool builds a binary or a library.
58    artifact_kind: ToolArtifactKind,
59}
60
61impl Builder<'_> {
62    #[track_caller]
63    pub(crate) fn msg_tool(
64        &self,
65        kind: Kind,
66        mode: Mode,
67        tool: &str,
68        build_stage: u32,
69        host: &TargetSelection,
70        target: &TargetSelection,
71    ) -> Option<gha::Group> {
72        match mode {
73            // depends on compiler stage, different to host compiler
74            Mode::ToolRustc => self.msg_sysroot_tool(
75                kind,
76                build_stage,
77                format_args!("tool {tool}"),
78                *host,
79                *target,
80            ),
81            // doesn't depend on compiler, same as host compiler
82            _ => self.msg(kind, build_stage, format_args!("tool {tool}"), *host, *target),
83        }
84    }
85}
86
87/// Result of the tool build process. Each `Step` in this module is responsible
88/// for using this type as `type Output = ToolBuildResult;`
89#[derive(Clone)]
90pub struct ToolBuildResult {
91    /// Artifact path of the corresponding tool that was built.
92    pub tool_path: PathBuf,
93    /// Compiler used to build the tool. For non-`ToolRustc` tools this is equal to `target_compiler`.
94    /// For `ToolRustc` this is one stage before of the `target_compiler`.
95    pub build_compiler: Compiler,
96    /// Target compiler passed to `Step`.
97    pub target_compiler: Compiler,
98}
99
100impl Step for ToolBuild {
101    type Output = ToolBuildResult;
102
103    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
104        run.never()
105    }
106
107    /// Builds a tool in `src/tools`
108    ///
109    /// This will build the specified tool with the specified `host` compiler in
110    /// `stage` into the normal cargo output directory.
111    fn run(mut self, builder: &Builder<'_>) -> ToolBuildResult {
112        let target = self.target;
113        let mut tool = self.tool;
114        let path = self.path;
115
116        let target_compiler = self.build_compiler;
117        self.build_compiler = if self.mode == Mode::ToolRustc {
118            get_tool_rustc_compiler(builder, self.build_compiler)
119        } else {
120            self.build_compiler
121        };
122
123        match self.mode {
124            Mode::ToolRustc => {
125                // If compiler was forced, its artifacts should have been prepared earlier.
126                if !self.build_compiler.is_forced_compiler() {
127                    builder.std(self.build_compiler, self.build_compiler.host);
128                    builder.ensure(compile::Rustc::new(self.build_compiler, target));
129                }
130            }
131            Mode::ToolStd => {
132                // If compiler was forced, its artifacts should have been prepared earlier.
133                if !self.build_compiler.is_forced_compiler() {
134                    builder.std(self.build_compiler, target);
135                }
136            }
137            Mode::ToolBootstrap | Mode::ToolTarget => {} // uses downloaded stage0 compiler libs
138            _ => panic!("unexpected Mode for tool build"),
139        }
140
141        let mut cargo = prepare_tool_cargo(
142            builder,
143            self.build_compiler,
144            self.mode,
145            target,
146            Kind::Build,
147            path,
148            self.source_type,
149            &self.extra_features,
150        );
151
152        // The stage0 compiler changes infrequently and does not directly depend on code
153        // in the current working directory. Therefore, caching it with sccache should be
154        // useful.
155        // This is only performed for non-incremental builds, as ccache cannot deal with these.
156        if let Some(ref ccache) = builder.config.ccache
157            && matches!(self.mode, Mode::ToolBootstrap)
158            && !builder.config.incremental
159        {
160            cargo.env("RUSTC_WRAPPER", ccache);
161        }
162
163        // Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer)
164        // could use the additional optimizations.
165        if self.mode == Mode::ToolRustc && is_lto_stage(&self.build_compiler) {
166            let lto = match builder.config.rust_lto {
167                RustcLto::Off => Some("off"),
168                RustcLto::Thin => Some("thin"),
169                RustcLto::Fat => Some("fat"),
170                RustcLto::ThinLocal => None,
171            };
172            if let Some(lto) = lto {
173                cargo.env(cargo_profile_var("LTO", &builder.config), lto);
174            }
175        }
176
177        if !self.allow_features.is_empty() {
178            cargo.allow_features(self.allow_features);
179        }
180
181        cargo.args(self.cargo_args);
182
183        let _guard = builder.msg_tool(
184            Kind::Build,
185            self.mode,
186            self.tool,
187            // A stage N tool is built with the stage N-1 compiler.
188            self.build_compiler.stage + 1,
189            &self.build_compiler.host,
190            &self.target,
191        );
192
193        // we check this below
194        let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {});
195
196        builder.save_toolstate(
197            tool,
198            if build_success { ToolState::TestFail } else { ToolState::BuildFail },
199        );
200
201        if !build_success {
202            crate::exit!(1);
203        } else {
204            // HACK(#82501): on Windows, the tools directory gets added to PATH when running tests, and
205            // compiletest confuses HTML tidy with the in-tree tidy. Name the in-tree tidy something
206            // different so the problem doesn't come up.
207            if tool == "tidy" {
208                tool = "rust-tidy";
209            }
210            let tool_path = match self.artifact_kind {
211                ToolArtifactKind::Binary => {
212                    copy_link_tool_bin(builder, self.build_compiler, self.target, self.mode, tool)
213                }
214                ToolArtifactKind::Library => builder
215                    .cargo_out(self.build_compiler, self.mode, self.target)
216                    .join(format!("lib{tool}.rlib")),
217            };
218
219            ToolBuildResult { tool_path, build_compiler: self.build_compiler, target_compiler }
220        }
221    }
222}
223
224#[expect(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this.
225pub fn prepare_tool_cargo(
226    builder: &Builder<'_>,
227    compiler: Compiler,
228    mode: Mode,
229    target: TargetSelection,
230    cmd_kind: Kind,
231    path: &str,
232    source_type: SourceType,
233    extra_features: &[String],
234) -> CargoCommand {
235    let mut cargo = builder::Cargo::new(builder, compiler, mode, source_type, target, cmd_kind);
236
237    let path = PathBuf::from(path);
238    let dir = builder.src.join(&path);
239    cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
240
241    let mut features = extra_features.to_vec();
242    if builder.build.config.cargo_native_static {
243        if path.ends_with("cargo")
244            || path.ends_with("clippy")
245            || path.ends_with("miri")
246            || path.ends_with("rustfmt")
247        {
248            cargo.env("LIBZ_SYS_STATIC", "1");
249        }
250        if path.ends_with("cargo") {
251            features.push("all-static".to_string());
252        }
253    }
254
255    // build.tool.TOOL_NAME.features in bootstrap.toml allows specifying which features to enable
256    // for a specific tool. `extra_features` instead is not controlled by the toml and provides
257    // features that are always enabled for a specific tool (e.g. "in-rust-tree" for rust-analyzer).
258    // Finally, `prepare_tool_cargo` above here might add more features to adapt the build
259    // to the chosen flags (e.g. "all-static" for cargo if `cargo_native_static` is true).
260    builder
261        .config
262        .tool
263        .iter()
264        .filter(|(tool_name, _)| path.file_name().and_then(OsStr::to_str) == Some(tool_name))
265        .for_each(|(_, tool)| features.extend(tool.features.clone().unwrap_or_default()));
266
267    // clippy tests need to know about the stage sysroot. Set them consistently while building to
268    // avoid rebuilding when running tests.
269    cargo.env("SYSROOT", builder.sysroot(compiler));
270
271    // if tools are using lzma we want to force the build script to build its
272    // own copy
273    cargo.env("LZMA_API_STATIC", "1");
274
275    // CFG_RELEASE is needed by rustfmt (and possibly other tools) which
276    // import rustc-ap-rustc_attr which requires this to be set for the
277    // `#[cfg(version(...))]` attribute.
278    cargo.env("CFG_RELEASE", builder.rust_release());
279    cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel);
280    cargo.env("CFG_VERSION", builder.rust_version());
281    cargo.env("CFG_RELEASE_NUM", &builder.version);
282    cargo.env("DOC_RUST_LANG_ORG_CHANNEL", builder.doc_rust_lang_org_channel());
283
284    if let Some(ref ver_date) = builder.rust_info().commit_date() {
285        cargo.env("CFG_VER_DATE", ver_date);
286    }
287
288    if let Some(ref ver_hash) = builder.rust_info().sha() {
289        cargo.env("CFG_VER_HASH", ver_hash);
290    }
291
292    if let Some(description) = &builder.config.description {
293        cargo.env("CFG_VER_DESCRIPTION", description);
294    }
295
296    let info = builder.config.git_info(builder.config.omit_git_hash, &dir);
297    if let Some(sha) = info.sha() {
298        cargo.env("CFG_COMMIT_HASH", sha);
299    }
300
301    if let Some(sha_short) = info.sha_short() {
302        cargo.env("CFG_SHORT_COMMIT_HASH", sha_short);
303    }
304
305    if let Some(date) = info.commit_date() {
306        cargo.env("CFG_COMMIT_DATE", date);
307    }
308
309    if !features.is_empty() {
310        cargo.arg("--features").arg(features.join(", "));
311    }
312
313    // Enable internal lints for clippy and rustdoc
314    // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
315    // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
316    //
317    // NOTE: We unconditionally set this here to avoid recompiling tools between `x check $tool`
318    // and `x test $tool` executions.
319    // See https://github.com/rust-lang/rust/issues/116538
320    cargo.rustflag("-Zunstable-options");
321
322    // NOTE: The root cause of needing `-Zon-broken-pipe=kill` in the first place is because `rustc`
323    // and `rustdoc` doesn't gracefully handle I/O errors due to usages of raw std `println!` macros
324    // which panics upon encountering broken pipes. `-Zon-broken-pipe=kill` just papers over that
325    // and stops rustc/rustdoc ICEing on e.g. `rustc --print=sysroot | false`.
326    //
327    // cargo explicitly does not want the `-Zon-broken-pipe=kill` paper because it does actually use
328    // variants of `println!` that handles I/O errors gracefully. It's also a breaking change for a
329    // spawn process not written in Rust, especially if the language default handler is not
330    // `SIG_IGN`. Thankfully cargo tests will break if we do set the flag.
331    //
332    // For the cargo discussion, see
333    // <https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/Applying.20.60-Zon-broken-pipe.3Dkill.60.20flags.20in.20bootstrap.3F>.
334    //
335    // For the rustc discussion, see
336    // <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Internal.20lint.20for.20raw.20.60print!.60.20and.20.60println!.60.3F>
337    // for proper solutions.
338    if !path.ends_with("cargo") {
339        // Use an untracked env var `FORCE_ON_BROKEN_PIPE_KILL` here instead of `RUSTFLAGS`.
340        // `RUSTFLAGS` is tracked by cargo. Conditionally omitting `-Zon-broken-pipe=kill` from
341        // `RUSTFLAGS` causes unnecessary tool rebuilds due to cache invalidation from building e.g.
342        // cargo *without* `-Zon-broken-pipe=kill` but then rustdoc *with* `-Zon-broken-pipe=kill`.
343        cargo.env("FORCE_ON_BROKEN_PIPE_KILL", "-Zon-broken-pipe=kill");
344    }
345
346    cargo
347}
348
349/// Handle stage-off logic for `ToolRustc` tools when necessary.
350pub(crate) fn get_tool_rustc_compiler(
351    builder: &Builder<'_>,
352    target_compiler: Compiler,
353) -> Compiler {
354    if target_compiler.is_forced_compiler() {
355        return target_compiler;
356    }
357
358    if builder.download_rustc() && target_compiler.stage == 1 {
359        // We shouldn't drop to stage0 compiler when using CI rustc.
360        return builder.compiler(1, builder.config.host_target);
361    }
362
363    // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise
364    // we'd have stageN/bin/rustc and stageN/bin/$rustc_tool be effectively different stage
365    // compilers, which isn't what we want. Rustc tools should be linked in the same way as the
366    // compiler it's paired with, so it must be built with the previous stage compiler.
367    builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.host_target)
368}
369
370/// Determines how to build a `ToolTarget`, i.e. which compiler should be used to compile it.
371/// The compiler stage is automatically bumped if we need to cross-compile a stage 1 tool.
372pub enum ToolTargetBuildMode {
373    /// Build the tool using rustc that corresponds to the selected CLI stage.
374    Build(TargetSelection),
375    /// Build the tool so that it can be attached to the sysroot of the passed compiler.
376    /// Since we always dist stage 2+, the compiler that builds the tool in this case has to be
377    /// stage 1+.
378    Dist(Compiler),
379}
380
381/// Returns compiler that is able to compile a `ToolTarget` tool with the given `mode`.
382pub(crate) fn get_tool_target_compiler(
383    builder: &Builder<'_>,
384    mode: ToolTargetBuildMode,
385) -> Compiler {
386    let (target, build_compiler_stage) = match mode {
387        ToolTargetBuildMode::Build(target) => {
388            assert!(builder.top_stage > 0);
389            // If we want to build a stage N tool, we need to compile it with stage N-1 rustc
390            (target, builder.top_stage - 1)
391        }
392        ToolTargetBuildMode::Dist(target_compiler) => {
393            assert!(target_compiler.stage > 0);
394            // If we want to dist a stage N rustc, we want to attach stage N tool to it.
395            // And to build that tool, we need to compile it with stage N-1 rustc
396            (target_compiler.host, target_compiler.stage - 1)
397        }
398    };
399
400    let compiler = if builder.host_target == target {
401        builder.compiler(build_compiler_stage, builder.host_target)
402    } else {
403        // If we are cross-compiling a stage 1 tool, we cannot do that with a stage 0 compiler,
404        // so we auto-bump the tool's stage to 2, which means we need a stage 1 compiler.
405        builder.compiler(build_compiler_stage.max(1), builder.host_target)
406    };
407    builder.std(compiler, target);
408    compiler
409}
410
411/// Links a built tool binary with the given `name` from the build directory to the
412/// tools directory.
413fn copy_link_tool_bin(
414    builder: &Builder<'_>,
415    compiler: Compiler,
416    target: TargetSelection,
417    mode: Mode,
418    name: &str,
419) -> PathBuf {
420    let cargo_out = builder.cargo_out(compiler, mode, target).join(exe(name, target));
421    let bin = builder.tools_dir(compiler).join(exe(name, target));
422    builder.copy_link(&cargo_out, &bin, FileType::Executable);
423    bin
424}
425
426macro_rules! bootstrap_tool {
427    ($(
428        $name:ident, $path:expr, $tool_name:expr
429        $(,is_external_tool = $external:expr)*
430        $(,is_unstable_tool = $unstable:expr)*
431        $(,allow_features = $allow_features:expr)?
432        $(,submodules = $submodules:expr)?
433        $(,artifact_kind = $artifact_kind:expr)?
434        ;
435    )+) => {
436        #[derive(PartialEq, Eq, Clone)]
437        pub enum Tool {
438            $(
439                $name,
440            )+
441        }
442
443        impl<'a> Builder<'a> {
444            pub fn tool_exe(&self, tool: Tool) -> PathBuf {
445                match tool {
446                    $(Tool::$name =>
447                        self.ensure($name {
448                            compiler: self.compiler(0, self.config.host_target),
449                            target: self.config.host_target,
450                        }).tool_path,
451                    )+
452                }
453            }
454        }
455
456        $(
457            #[derive(Debug, Clone, Hash, PartialEq, Eq)]
458        pub struct $name {
459            pub compiler: Compiler,
460            pub target: TargetSelection,
461        }
462
463        impl Step for $name {
464            type Output = ToolBuildResult;
465
466            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
467                run.path($path)
468            }
469
470            fn make_run(run: RunConfig<'_>) {
471                run.builder.ensure($name {
472                    // snapshot compiler
473                    compiler: run.builder.compiler(0, run.builder.config.host_target),
474                    target: run.target,
475                });
476            }
477
478            #[cfg_attr(
479                feature = "tracing",
480                instrument(
481                    level = "debug",
482                    name = $tool_name,
483                    skip_all,
484                ),
485            )]
486            fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
487                $(
488                    for submodule in $submodules {
489                        builder.require_submodule(submodule, None);
490                    }
491                )*
492
493                let is_unstable = false $(|| $unstable)*;
494                let compiletest_wants_stage0 = $tool_name == "compiletest" && builder.config.compiletest_use_stage0_libtest;
495
496                builder.ensure(ToolBuild {
497                    build_compiler: self.compiler,
498                    target: self.target,
499                    tool: $tool_name,
500                    mode: if is_unstable && !compiletest_wants_stage0 {
501                        // use in-tree libraries for unstable features
502                        Mode::ToolStd
503                    } else {
504                        Mode::ToolBootstrap
505                    },
506                    path: $path,
507                    source_type: if false $(|| $external)* {
508                        SourceType::Submodule
509                    } else {
510                        SourceType::InTree
511                    },
512                    extra_features: vec![],
513                    allow_features: {
514                        let mut _value = "";
515                        $( _value = $allow_features; )?
516                        _value
517                    },
518                    cargo_args: vec![],
519                    artifact_kind: if false $(|| $artifact_kind == ToolArtifactKind::Library)* {
520                        ToolArtifactKind::Library
521                    } else {
522                        ToolArtifactKind::Binary
523                    }
524                })
525            }
526
527            fn metadata(&self) -> Option<StepMetadata> {
528                Some(
529                    StepMetadata::build(stringify!($name), self.target)
530                        .built_by(self.compiler)
531                )
532            }
533        }
534        )+
535    }
536}
537
538pub(crate) const COMPILETEST_ALLOW_FEATURES: &str = "internal_output_capture";
539
540bootstrap_tool!(
541    // This is marked as an external tool because it includes dependencies
542    // from submodules. Trying to keep the lints in sync between all the repos
543    // is a bit of a pain. Unfortunately it means the rustbook source itself
544    // doesn't deny warnings, but it is a relatively small piece of code.
545    Rustbook, "src/tools/rustbook", "rustbook", is_external_tool = true, submodules = SUBMODULES_FOR_RUSTBOOK;
546    UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen";
547    Tidy, "src/tools/tidy", "tidy";
548    Linkchecker, "src/tools/linkchecker", "linkchecker";
549    CargoTest, "src/tools/cargotest", "cargotest";
550    Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;
551    BuildManifest, "src/tools/build-manifest", "build-manifest";
552    RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
553    RustInstaller, "src/tools/rust-installer", "rust-installer";
554    RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes";
555    LintDocs, "src/tools/lint-docs", "lint-docs";
556    JsonDocCk, "src/tools/jsondocck", "jsondocck";
557    JsonDocLint, "src/tools/jsondoclint", "jsondoclint";
558    HtmlChecker, "src/tools/html-checker", "html-checker";
559    BumpStage0, "src/tools/bump-stage0", "bump-stage0";
560    ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
561    CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
562    GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
563    GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
564    // rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features.
565    RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;
566    CoverageDump, "src/tools/coverage-dump", "coverage-dump";
567    UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
568    FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
569    OptimizedDist, "src/tools/opt-dist", "opt-dist", submodules = &["src/tools/rustc-perf"];
570    RunMakeSupport, "src/tools/run-make-support", "run_make_support", artifact_kind = ToolArtifactKind::Library;
571);
572
573/// These are the submodules that are required for rustbook to work due to
574/// depending on mdbook plugins.
575pub static SUBMODULES_FOR_RUSTBOOK: &[&str] = &["src/doc/book", "src/doc/reference"];
576
577/// The [rustc-perf](https://github.com/rust-lang/rustc-perf) benchmark suite, which is added
578/// as a submodule at `src/tools/rustc-perf`.
579#[derive(Debug, Clone, Hash, PartialEq, Eq)]
580pub struct RustcPerf {
581    pub compiler: Compiler,
582    pub target: TargetSelection,
583}
584
585impl Step for RustcPerf {
586    /// Path to the built `collector` binary.
587    type Output = ToolBuildResult;
588
589    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
590        run.path("src/tools/rustc-perf")
591    }
592
593    fn make_run(run: RunConfig<'_>) {
594        run.builder.ensure(RustcPerf {
595            compiler: run.builder.compiler(0, run.builder.config.host_target),
596            target: run.target,
597        });
598    }
599
600    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
601        // We need to ensure the rustc-perf submodule is initialized.
602        builder.require_submodule("src/tools/rustc-perf", None);
603
604        let tool = ToolBuild {
605            build_compiler: self.compiler,
606            target: self.target,
607            tool: "collector",
608            mode: Mode::ToolBootstrap,
609            path: "src/tools/rustc-perf",
610            source_type: SourceType::Submodule,
611            extra_features: Vec::new(),
612            allow_features: "",
613            // Only build the collector package, which is used for benchmarking through
614            // a CLI.
615            cargo_args: vec!["-p".to_string(), "collector".to_string()],
616            artifact_kind: ToolArtifactKind::Binary,
617        };
618        let res = builder.ensure(tool.clone());
619        // We also need to symlink the `rustc-fake` binary to the corresponding directory,
620        // because `collector` expects it in the same directory.
621        copy_link_tool_bin(builder, tool.build_compiler, tool.target, tool.mode, "rustc-fake");
622
623        res
624    }
625}
626
627#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
628pub struct ErrorIndex {
629    pub compiler: Compiler,
630}
631
632impl ErrorIndex {
633    pub fn command(builder: &Builder<'_>) -> BootstrapCommand {
634        // Error-index-generator links with the rustdoc library, so we need to add `rustc_lib_paths`
635        // for rustc_private and libLLVM.so, and `sysroot_lib` for libstd, etc.
636        let host = builder.config.host_target;
637        let compiler = builder.compiler_for(builder.top_stage, host, host);
638        let mut cmd = command(builder.ensure(ErrorIndex { compiler }).tool_path);
639        let mut dylib_paths = builder.rustc_lib_paths(compiler);
640        dylib_paths.push(builder.sysroot_target_libdir(compiler, compiler.host));
641        add_dylib_path(dylib_paths, &mut cmd);
642        cmd
643    }
644}
645
646impl Step for ErrorIndex {
647    type Output = ToolBuildResult;
648
649    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
650        run.path("src/tools/error_index_generator")
651    }
652
653    fn make_run(run: RunConfig<'_>) {
654        // NOTE: This `make_run` isn't used in normal situations, only if you
655        // manually build the tool with `x.py build
656        // src/tools/error-index-generator` which almost nobody does.
657        // Normally, `x.py test` or `x.py doc` will use the
658        // `ErrorIndex::command` function instead.
659        let compiler = run.builder.compiler(run.builder.top_stage, run.builder.config.host_target);
660        run.builder.ensure(ErrorIndex { compiler });
661    }
662
663    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
664        builder.ensure(ToolBuild {
665            build_compiler: self.compiler,
666            target: self.compiler.host,
667            tool: "error_index_generator",
668            mode: Mode::ToolRustc,
669            path: "src/tools/error_index_generator",
670            source_type: SourceType::InTree,
671            extra_features: Vec::new(),
672            allow_features: "",
673            cargo_args: Vec::new(),
674            artifact_kind: ToolArtifactKind::Binary,
675        })
676    }
677}
678
679#[derive(Debug, Clone, Hash, PartialEq, Eq)]
680pub struct RemoteTestServer {
681    pub build_compiler: Compiler,
682    pub target: TargetSelection,
683}
684
685impl Step for RemoteTestServer {
686    type Output = ToolBuildResult;
687
688    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
689        run.path("src/tools/remote-test-server")
690    }
691
692    fn make_run(run: RunConfig<'_>) {
693        run.builder.ensure(RemoteTestServer {
694            build_compiler: get_tool_target_compiler(
695                run.builder,
696                ToolTargetBuildMode::Build(run.target),
697            ),
698            target: run.target,
699        });
700    }
701
702    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
703        builder.ensure(ToolBuild {
704            build_compiler: self.build_compiler,
705            target: self.target,
706            tool: "remote-test-server",
707            mode: Mode::ToolTarget,
708            path: "src/tools/remote-test-server",
709            source_type: SourceType::InTree,
710            extra_features: Vec::new(),
711            allow_features: "",
712            cargo_args: Vec::new(),
713            artifact_kind: ToolArtifactKind::Binary,
714        })
715    }
716
717    fn metadata(&self) -> Option<StepMetadata> {
718        Some(StepMetadata::build("remote-test-server", self.target).built_by(self.build_compiler))
719    }
720}
721
722#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
723pub struct Rustdoc {
724    /// This should only ever be 0 or 2.
725    /// We sometimes want to reference the "bootstrap" rustdoc, which is why this option is here.
726    pub compiler: Compiler,
727}
728
729impl Step for Rustdoc {
730    type Output = ToolBuildResult;
731    const DEFAULT: bool = true;
732    const ONLY_HOSTS: bool = true;
733
734    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
735        run.path("src/tools/rustdoc").path("src/librustdoc")
736    }
737
738    fn make_run(run: RunConfig<'_>) {
739        run.builder
740            .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.target) });
741    }
742
743    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
744        let target_compiler = self.compiler;
745        let target = target_compiler.host;
746
747        if target_compiler.stage == 0 {
748            if !target_compiler.is_snapshot(builder) {
749                panic!("rustdoc in stage 0 must be snapshot rustdoc");
750            }
751
752            return ToolBuildResult {
753                tool_path: builder.initial_rustdoc.clone(),
754                build_compiler: target_compiler,
755                target_compiler,
756            };
757        }
758
759        let bin_rustdoc = || {
760            let sysroot = builder.sysroot(target_compiler);
761            let bindir = sysroot.join("bin");
762            t!(fs::create_dir_all(&bindir));
763            let bin_rustdoc = bindir.join(exe("rustdoc", target_compiler.host));
764            let _ = fs::remove_file(&bin_rustdoc);
765            bin_rustdoc
766        };
767
768        // If CI rustc is enabled and we haven't modified the rustdoc sources,
769        // use the precompiled rustdoc from CI rustc's sysroot to speed up bootstrapping.
770        if builder.download_rustc()
771            && target_compiler.stage > 0
772            && builder.rust_info().is_managed_git_subrepository()
773        {
774            let files_to_track = &["src/librustdoc", "src/tools/rustdoc", "src/rustdoc-json-types"];
775
776            // Check if unchanged
777            if !builder.config.has_changes_from_upstream(files_to_track) {
778                let precompiled_rustdoc = builder
779                    .config
780                    .ci_rustc_dir()
781                    .join("bin")
782                    .join(exe("rustdoc", target_compiler.host));
783
784                let bin_rustdoc = bin_rustdoc();
785                builder.copy_link(&precompiled_rustdoc, &bin_rustdoc, FileType::Executable);
786
787                return ToolBuildResult {
788                    tool_path: bin_rustdoc,
789                    build_compiler: target_compiler,
790                    target_compiler,
791                };
792            }
793        }
794
795        // The presence of `target_compiler` ensures that the necessary libraries (codegen backends,
796        // compiler libraries, ...) are built. Rustdoc does not require the presence of any
797        // libraries within sysroot_libdir (i.e., rustlib), though doctests may want it (since
798        // they'll be linked to those libraries). As such, don't explicitly `ensure` any additional
799        // libraries here. The intuition here is that If we've built a compiler, we should be able
800        // to build rustdoc.
801        //
802        let mut extra_features = Vec::new();
803        if builder.config.jemalloc(target) {
804            extra_features.push("jemalloc".to_string());
805        }
806
807        let ToolBuildResult { tool_path, build_compiler, target_compiler } =
808            builder.ensure(ToolBuild {
809                build_compiler: target_compiler,
810                target,
811                // Cargo adds a number of paths to the dylib search path on windows, which results in
812                // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
813                // rustdoc a different name.
814                tool: "rustdoc_tool_binary",
815                mode: Mode::ToolRustc,
816                path: "src/tools/rustdoc",
817                source_type: SourceType::InTree,
818                extra_features,
819                allow_features: "",
820                cargo_args: Vec::new(),
821                artifact_kind: ToolArtifactKind::Binary,
822            });
823
824        // don't create a stage0-sysroot/bin directory.
825        if target_compiler.stage > 0 {
826            if builder.config.rust_debuginfo_level_tools == DebuginfoLevel::None {
827                // Due to LTO a lot of debug info from C++ dependencies such as jemalloc can make it into
828                // our final binaries
829                compile::strip_debug(builder, target, &tool_path);
830            }
831            let bin_rustdoc = bin_rustdoc();
832            builder.copy_link(&tool_path, &bin_rustdoc, FileType::Executable);
833            ToolBuildResult { tool_path: bin_rustdoc, build_compiler, target_compiler }
834        } else {
835            ToolBuildResult { tool_path, build_compiler, target_compiler }
836        }
837    }
838
839    fn metadata(&self) -> Option<StepMetadata> {
840        Some(
841            StepMetadata::build("rustdoc", self.compiler.host)
842                // rustdoc is ToolRustc, so stage N rustdoc is built by stage N-1 rustc
843                // FIXME: make this stage deduction automatic somehow
844                // FIXME: log the compiler that actually built ToolRustc steps
845                .stage(self.compiler.stage.saturating_sub(1)),
846        )
847    }
848}
849
850#[derive(Debug, Clone, Hash, PartialEq, Eq)]
851pub struct Cargo {
852    pub compiler: Compiler,
853    pub target: TargetSelection,
854}
855
856impl Step for Cargo {
857    type Output = ToolBuildResult;
858    const DEFAULT: bool = true;
859    const ONLY_HOSTS: bool = true;
860
861    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
862        let builder = run.builder;
863        run.path("src/tools/cargo").default_condition(builder.tool_enabled("cargo"))
864    }
865
866    fn make_run(run: RunConfig<'_>) {
867        run.builder.ensure(Cargo {
868            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
869            target: run.target,
870        });
871    }
872
873    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
874        builder.build.require_submodule("src/tools/cargo", None);
875
876        builder.ensure(ToolBuild {
877            build_compiler: self.compiler,
878            target: self.target,
879            tool: "cargo",
880            mode: Mode::ToolRustc,
881            path: "src/tools/cargo",
882            source_type: SourceType::Submodule,
883            extra_features: Vec::new(),
884            allow_features: "",
885            cargo_args: Vec::new(),
886            artifact_kind: ToolArtifactKind::Binary,
887        })
888    }
889}
890
891/// Represents a built LldWrapper, the `lld-wrapper` tool itself, and a directory
892/// containing a build of LLD.
893#[derive(Clone)]
894pub struct BuiltLldWrapper {
895    tool: ToolBuildResult,
896    lld_dir: PathBuf,
897}
898
899#[derive(Debug, Clone, Hash, PartialEq, Eq)]
900pub struct LldWrapper {
901    pub build_compiler: Compiler,
902    pub target: TargetSelection,
903}
904
905impl LldWrapper {
906    /// Returns `LldWrapper` that should be **used** by the passed compiler.
907    pub fn for_use_by_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
908        Self {
909            build_compiler: get_tool_target_compiler(
910                builder,
911                ToolTargetBuildMode::Dist(target_compiler),
912            ),
913            target: target_compiler.host,
914        }
915    }
916}
917
918impl Step for LldWrapper {
919    type Output = BuiltLldWrapper;
920
921    const ONLY_HOSTS: bool = true;
922
923    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
924        run.path("src/tools/lld-wrapper")
925    }
926
927    fn make_run(run: RunConfig<'_>) {
928        run.builder.ensure(LldWrapper {
929            build_compiler: get_tool_target_compiler(
930                run.builder,
931                ToolTargetBuildMode::Build(run.target),
932            ),
933            target: run.target,
934        });
935    }
936
937    #[cfg_attr(
938        feature = "tracing",
939        instrument(
940            level = "debug",
941            name = "LldWrapper::run",
942            skip_all,
943            fields(build_compiler = ?self.build_compiler),
944        ),
945    )]
946    fn run(self, builder: &Builder<'_>) -> Self::Output {
947        let lld_dir = builder.ensure(llvm::Lld { target: self.target });
948        let tool = builder.ensure(ToolBuild {
949            build_compiler: self.build_compiler,
950            target: self.target,
951            tool: "lld-wrapper",
952            mode: Mode::ToolTarget,
953            path: "src/tools/lld-wrapper",
954            source_type: SourceType::InTree,
955            extra_features: Vec::new(),
956            allow_features: "",
957            cargo_args: Vec::new(),
958            artifact_kind: ToolArtifactKind::Binary,
959        });
960        BuiltLldWrapper { tool, lld_dir }
961    }
962
963    fn metadata(&self) -> Option<StepMetadata> {
964        Some(StepMetadata::build("LldWrapper", self.target).built_by(self.build_compiler))
965    }
966}
967
968pub(crate) fn copy_lld_artifacts(
969    builder: &Builder<'_>,
970    lld_wrapper: BuiltLldWrapper,
971    target_compiler: Compiler,
972) {
973    let target = target_compiler.host;
974
975    let libdir_bin = builder.sysroot_target_bindir(target_compiler, target);
976    t!(fs::create_dir_all(&libdir_bin));
977
978    let src_exe = exe("lld", target);
979    let dst_exe = exe("rust-lld", target);
980
981    builder.copy_link(
982        &lld_wrapper.lld_dir.join("bin").join(src_exe),
983        &libdir_bin.join(dst_exe),
984        FileType::Executable,
985    );
986    let self_contained_lld_dir = libdir_bin.join("gcc-ld");
987    t!(fs::create_dir_all(&self_contained_lld_dir));
988
989    for name in crate::LLD_FILE_NAMES {
990        builder.copy_link(
991            &lld_wrapper.tool.tool_path,
992            &self_contained_lld_dir.join(exe(name, target)),
993            FileType::Executable,
994        );
995    }
996}
997
998/// Builds the `wasm-component-ld` linker wrapper, which is shipped with rustc to be executed on the
999/// host platform where rustc runs.
1000#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1001pub struct WasmComponentLd {
1002    build_compiler: Compiler,
1003    target: TargetSelection,
1004}
1005
1006impl WasmComponentLd {
1007    /// Returns `WasmComponentLd` that should be **used** by the passed compiler.
1008    pub fn for_use_by_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
1009        Self {
1010            build_compiler: get_tool_target_compiler(
1011                builder,
1012                ToolTargetBuildMode::Dist(target_compiler),
1013            ),
1014            target: target_compiler.host,
1015        }
1016    }
1017}
1018
1019impl Step for WasmComponentLd {
1020    type Output = ToolBuildResult;
1021
1022    const ONLY_HOSTS: bool = true;
1023
1024    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1025        run.path("src/tools/wasm-component-ld")
1026    }
1027
1028    fn make_run(run: RunConfig<'_>) {
1029        run.builder.ensure(WasmComponentLd {
1030            build_compiler: get_tool_target_compiler(
1031                run.builder,
1032                ToolTargetBuildMode::Build(run.target),
1033            ),
1034            target: run.target,
1035        });
1036    }
1037
1038    #[cfg_attr(
1039        feature = "tracing",
1040        instrument(
1041            level = "debug",
1042            name = "WasmComponentLd::run",
1043            skip_all,
1044            fields(build_compiler = ?self.build_compiler),
1045        ),
1046    )]
1047    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
1048        builder.ensure(ToolBuild {
1049            build_compiler: self.build_compiler,
1050            target: self.target,
1051            tool: "wasm-component-ld",
1052            mode: Mode::ToolTarget,
1053            path: "src/tools/wasm-component-ld",
1054            source_type: SourceType::InTree,
1055            extra_features: vec![],
1056            allow_features: "",
1057            cargo_args: vec![],
1058            artifact_kind: ToolArtifactKind::Binary,
1059        })
1060    }
1061
1062    fn metadata(&self) -> Option<StepMetadata> {
1063        Some(StepMetadata::build("WasmComponentLd", self.target).built_by(self.build_compiler))
1064    }
1065}
1066
1067#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1068pub struct RustAnalyzer {
1069    pub compiler: Compiler,
1070    pub target: TargetSelection,
1071}
1072
1073impl RustAnalyzer {
1074    pub const ALLOW_FEATURES: &'static str = "rustc_private,proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink,proc_macro_def_site";
1075}
1076
1077impl Step for RustAnalyzer {
1078    type Output = ToolBuildResult;
1079    const DEFAULT: bool = true;
1080    const ONLY_HOSTS: bool = true;
1081
1082    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1083        let builder = run.builder;
1084        run.path("src/tools/rust-analyzer").default_condition(builder.tool_enabled("rust-analyzer"))
1085    }
1086
1087    fn make_run(run: RunConfig<'_>) {
1088        run.builder.ensure(RustAnalyzer {
1089            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
1090            target: run.target,
1091        });
1092    }
1093
1094    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
1095        builder.ensure(ToolBuild {
1096            build_compiler: self.compiler,
1097            target: self.target,
1098            tool: "rust-analyzer",
1099            mode: Mode::ToolRustc,
1100            path: "src/tools/rust-analyzer",
1101            extra_features: vec!["in-rust-tree".to_owned()],
1102            source_type: SourceType::InTree,
1103            allow_features: RustAnalyzer::ALLOW_FEATURES,
1104            cargo_args: Vec::new(),
1105            artifact_kind: ToolArtifactKind::Binary,
1106        })
1107    }
1108}
1109
1110#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1111pub struct RustAnalyzerProcMacroSrv {
1112    pub compiler: Compiler,
1113    pub target: TargetSelection,
1114}
1115
1116impl Step for RustAnalyzerProcMacroSrv {
1117    type Output = Option<ToolBuildResult>;
1118    const DEFAULT: bool = true;
1119    const ONLY_HOSTS: bool = true;
1120
1121    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1122        let builder = run.builder;
1123        // Allow building `rust-analyzer-proc-macro-srv` both as part of the `rust-analyzer` and as a stand-alone tool.
1124        run.path("src/tools/rust-analyzer")
1125            .path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
1126            .default_condition(
1127                builder.tool_enabled("rust-analyzer")
1128                    || builder.tool_enabled("rust-analyzer-proc-macro-srv"),
1129            )
1130    }
1131
1132    fn make_run(run: RunConfig<'_>) {
1133        run.builder.ensure(RustAnalyzerProcMacroSrv {
1134            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
1135            target: run.target,
1136        });
1137    }
1138
1139    fn run(self, builder: &Builder<'_>) -> Option<ToolBuildResult> {
1140        let tool_result = builder.ensure(ToolBuild {
1141            build_compiler: self.compiler,
1142            target: self.target,
1143            tool: "rust-analyzer-proc-macro-srv",
1144            mode: Mode::ToolRustc,
1145            path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli",
1146            extra_features: vec!["in-rust-tree".to_owned()],
1147            source_type: SourceType::InTree,
1148            allow_features: RustAnalyzer::ALLOW_FEATURES,
1149            cargo_args: Vec::new(),
1150            artifact_kind: ToolArtifactKind::Binary,
1151        });
1152
1153        // Copy `rust-analyzer-proc-macro-srv` to `<sysroot>/libexec/`
1154        // so that r-a can use it.
1155        let libexec_path = builder.sysroot(self.compiler).join("libexec");
1156        t!(fs::create_dir_all(&libexec_path));
1157        builder.copy_link(
1158            &tool_result.tool_path,
1159            &libexec_path.join("rust-analyzer-proc-macro-srv"),
1160            FileType::Executable,
1161        );
1162
1163        Some(tool_result)
1164    }
1165}
1166
1167#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1168pub struct LlvmBitcodeLinker {
1169    build_compiler: Compiler,
1170    target: TargetSelection,
1171}
1172
1173impl LlvmBitcodeLinker {
1174    /// Returns `LlvmBitcodeLinker` that will be **compiled** by the passed compiler, for the given
1175    /// `target`.
1176    pub fn from_build_compiler(build_compiler: Compiler, target: TargetSelection) -> Self {
1177        Self { build_compiler, target }
1178    }
1179
1180    /// Returns `LlvmBitcodeLinker` that should be **used** by the passed compiler.
1181    pub fn from_target_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
1182        Self {
1183            build_compiler: get_tool_target_compiler(
1184                builder,
1185                ToolTargetBuildMode::Dist(target_compiler),
1186            ),
1187            target: target_compiler.host,
1188        }
1189    }
1190
1191    /// Return a compiler that is able to build this tool for the given `target`.
1192    pub fn get_build_compiler_for_target(
1193        builder: &Builder<'_>,
1194        target: TargetSelection,
1195    ) -> Compiler {
1196        get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target))
1197    }
1198}
1199
1200impl Step for LlvmBitcodeLinker {
1201    type Output = ToolBuildResult;
1202    const DEFAULT: bool = true;
1203    const ONLY_HOSTS: bool = true;
1204
1205    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1206        let builder = run.builder;
1207        run.path("src/tools/llvm-bitcode-linker")
1208            .default_condition(builder.tool_enabled("llvm-bitcode-linker"))
1209    }
1210
1211    fn make_run(run: RunConfig<'_>) {
1212        run.builder.ensure(LlvmBitcodeLinker {
1213            build_compiler: Self::get_build_compiler_for_target(run.builder, run.target),
1214            target: run.target,
1215        });
1216    }
1217
1218    #[cfg_attr(
1219        feature = "tracing",
1220        instrument(level = "debug", name = "LlvmBitcodeLinker::run", skip_all)
1221    )]
1222    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
1223        builder.ensure(ToolBuild {
1224            build_compiler: self.build_compiler,
1225            target: self.target,
1226            tool: "llvm-bitcode-linker",
1227            mode: Mode::ToolTarget,
1228            path: "src/tools/llvm-bitcode-linker",
1229            source_type: SourceType::InTree,
1230            extra_features: vec![],
1231            allow_features: "",
1232            cargo_args: Vec::new(),
1233            artifact_kind: ToolArtifactKind::Binary,
1234        })
1235    }
1236
1237    fn metadata(&self) -> Option<StepMetadata> {
1238        Some(StepMetadata::build("LlvmBitcodeLinker", self.target).built_by(self.build_compiler))
1239    }
1240}
1241
1242#[derive(Debug, Clone, Hash, PartialEq, Eq)]
1243pub struct LibcxxVersionTool {
1244    pub target: TargetSelection,
1245}
1246
1247#[expect(dead_code)]
1248#[derive(Debug, Clone)]
1249pub enum LibcxxVersion {
1250    Gnu(usize),
1251    Llvm(usize),
1252}
1253
1254impl Step for LibcxxVersionTool {
1255    type Output = LibcxxVersion;
1256    const DEFAULT: bool = false;
1257    const ONLY_HOSTS: bool = true;
1258
1259    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1260        run.never()
1261    }
1262
1263    fn run(self, builder: &Builder<'_>) -> LibcxxVersion {
1264        let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version");
1265        let executable = out_dir.join(exe("libcxx-version", self.target));
1266
1267        // This is a sanity-check specific step, which means it is frequently called (when using
1268        // CI LLVM), and compiling `src/tools/libcxx-version/main.cpp` at the beginning of the bootstrap
1269        // invocation adds a fair amount of overhead to the process (see https://github.com/rust-lang/rust/issues/126423).
1270        // Therefore, we want to avoid recompiling this file unnecessarily.
1271        if !executable.exists() {
1272            if !out_dir.exists() {
1273                t!(fs::create_dir_all(&out_dir));
1274            }
1275
1276            let compiler = builder.cxx(self.target).unwrap();
1277            let mut cmd = command(compiler);
1278
1279            cmd.arg("-o")
1280                .arg(&executable)
1281                .arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
1282
1283            cmd.run(builder);
1284
1285            if !executable.exists() {
1286                panic!("Something went wrong. {} is not present", executable.display());
1287            }
1288        }
1289
1290        let version_output = command(executable).run_capture_stdout(builder).stdout();
1291
1292        let version_str = version_output.split_once("version:").unwrap().1;
1293        let version = version_str.trim().parse::<usize>().unwrap();
1294
1295        if version_output.starts_with("libstdc++") {
1296            LibcxxVersion::Gnu(version)
1297        } else if version_output.starts_with("libc++") {
1298            LibcxxVersion::Llvm(version)
1299        } else {
1300            panic!("Coudln't recognize the standard library version.");
1301        }
1302    }
1303}
1304
1305macro_rules! tool_extended {
1306    (
1307        $name:ident {
1308            path: $path:expr,
1309            tool_name: $tool_name:expr,
1310            stable: $stable:expr
1311            $( , add_bins_to_sysroot: $add_bins_to_sysroot:expr )?
1312            $( , add_features: $add_features:expr )?
1313            $( , cargo_args: $cargo_args:expr )?
1314            $( , )?
1315        }
1316    ) => {
1317        #[derive(Debug, Clone, Hash, PartialEq, Eq)]
1318        pub struct $name {
1319            pub compiler: Compiler,
1320            pub target: TargetSelection,
1321        }
1322
1323        impl Step for $name {
1324            type Output = ToolBuildResult;
1325            const DEFAULT: bool = true; // Overridden by `should_run_tool_build_step`
1326            const ONLY_HOSTS: bool = true;
1327
1328            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1329                should_run_tool_build_step(
1330                    run,
1331                    $tool_name,
1332                    $path,
1333                    $stable,
1334                )
1335            }
1336
1337            fn make_run(run: RunConfig<'_>) {
1338                run.builder.ensure($name {
1339                    compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
1340                    target: run.target,
1341                });
1342            }
1343
1344            fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
1345                let Self { compiler, target } = self;
1346                run_tool_build_step(
1347                    builder,
1348                    compiler,
1349                    target,
1350                    $tool_name,
1351                    $path,
1352                    None $( .or(Some(&$add_bins_to_sysroot)) )?,
1353                    None $( .or(Some($add_features)) )?,
1354                    None $( .or(Some($cargo_args)) )?,
1355                )
1356            }
1357
1358            fn metadata(&self) -> Option<StepMetadata> {
1359                // FIXME: refactor extended tool steps to make the build_compiler explicit,
1360                // it is offset by one now for rustc tools
1361                Some(
1362                    StepMetadata::build($tool_name, self.target)
1363                        .built_by(self.compiler.with_stage(self.compiler.stage.saturating_sub(1)))
1364                )
1365            }
1366        }
1367    }
1368}
1369
1370fn should_run_tool_build_step<'a>(
1371    run: ShouldRun<'a>,
1372    tool_name: &'static str,
1373    path: &'static str,
1374    stable: bool,
1375) -> ShouldRun<'a> {
1376    let builder = run.builder;
1377    run.path(path).default_condition(
1378        builder.config.extended
1379            && builder.config.tools.as_ref().map_or(
1380                // By default, on nightly/dev enable all tools, else only
1381                // build stable tools.
1382                stable || builder.build.unstable_features(),
1383                // If `tools` is set, search list for this tool.
1384                |tools| {
1385                    tools.iter().any(|tool| match tool.as_ref() {
1386                        "clippy" => tool_name == "clippy-driver",
1387                        x => tool_name == x,
1388                    })
1389                },
1390            ),
1391    )
1392}
1393
1394#[expect(clippy::too_many_arguments)] // silence overeager clippy lint
1395fn run_tool_build_step(
1396    builder: &Builder<'_>,
1397    compiler: Compiler,
1398    target: TargetSelection,
1399    tool_name: &'static str,
1400    path: &'static str,
1401    add_bins_to_sysroot: Option<&[&str]>,
1402    add_features: Option<fn(&Builder<'_>, TargetSelection, &mut Vec<String>)>,
1403    cargo_args: Option<&[&'static str]>,
1404) -> ToolBuildResult {
1405    let mut extra_features = Vec::new();
1406    if let Some(func) = add_features {
1407        func(builder, target, &mut extra_features);
1408    }
1409
1410    let ToolBuildResult { tool_path, build_compiler, target_compiler } =
1411        builder.ensure(ToolBuild {
1412            build_compiler: compiler,
1413            target,
1414            tool: tool_name,
1415            mode: Mode::ToolRustc,
1416            path,
1417            extra_features,
1418            source_type: SourceType::InTree,
1419            allow_features: "",
1420            cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(),
1421            artifact_kind: ToolArtifactKind::Binary,
1422        });
1423
1424    if let Some(add_bins_to_sysroot) = add_bins_to_sysroot
1425        && !add_bins_to_sysroot.is_empty()
1426        && target_compiler.stage > 0
1427    {
1428        let bindir = builder.sysroot(target_compiler).join("bin");
1429        t!(fs::create_dir_all(&bindir));
1430
1431        for add_bin in add_bins_to_sysroot {
1432            let bin_destination = bindir.join(exe(add_bin, target_compiler.host));
1433            builder.copy_link(&tool_path, &bin_destination, FileType::Executable);
1434        }
1435
1436        // Return a path into the bin dir.
1437        let path = bindir.join(exe(tool_name, target_compiler.host));
1438        ToolBuildResult { tool_path: path, build_compiler, target_compiler }
1439    } else {
1440        ToolBuildResult { tool_path, build_compiler, target_compiler }
1441    }
1442}
1443
1444tool_extended!(Cargofmt {
1445    path: "src/tools/rustfmt",
1446    tool_name: "cargo-fmt",
1447    stable: true,
1448    add_bins_to_sysroot: ["cargo-fmt"]
1449});
1450tool_extended!(CargoClippy {
1451    path: "src/tools/clippy",
1452    tool_name: "cargo-clippy",
1453    stable: true,
1454    add_bins_to_sysroot: ["cargo-clippy"]
1455});
1456tool_extended!(Clippy {
1457    path: "src/tools/clippy",
1458    tool_name: "clippy-driver",
1459    stable: true,
1460    add_bins_to_sysroot: ["clippy-driver"],
1461    add_features: |builder, target, features| {
1462        if builder.config.jemalloc(target) {
1463            features.push("jemalloc".to_string());
1464        }
1465    }
1466});
1467tool_extended!(Miri {
1468    path: "src/tools/miri",
1469    tool_name: "miri",
1470    stable: false,
1471    add_bins_to_sysroot: ["miri"],
1472    // Always compile also tests when building miri. Otherwise feature unification can cause rebuilds between building and testing miri.
1473    cargo_args: &["--all-targets"],
1474});
1475tool_extended!(CargoMiri {
1476    path: "src/tools/miri/cargo-miri",
1477    tool_name: "cargo-miri",
1478    stable: false,
1479    add_bins_to_sysroot: ["cargo-miri"]
1480});
1481tool_extended!(Rustfmt {
1482    path: "src/tools/rustfmt",
1483    tool_name: "rustfmt",
1484    stable: true,
1485    add_bins_to_sysroot: ["rustfmt"]
1486});
1487
1488#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1489pub struct TestFloatParse {
1490    pub host: TargetSelection,
1491}
1492
1493impl TestFloatParse {
1494    pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128";
1495}
1496
1497impl Step for TestFloatParse {
1498    type Output = ToolBuildResult;
1499    const ONLY_HOSTS: bool = true;
1500    const DEFAULT: bool = false;
1501
1502    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
1503        run.path("src/tools/test-float-parse")
1504    }
1505
1506    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
1507        let bootstrap_host = builder.config.host_target;
1508        let compiler = builder.compiler(builder.top_stage, bootstrap_host);
1509
1510        builder.ensure(ToolBuild {
1511            build_compiler: compiler,
1512            target: bootstrap_host,
1513            tool: "test-float-parse",
1514            mode: Mode::ToolStd,
1515            path: "src/tools/test-float-parse",
1516            source_type: SourceType::InTree,
1517            extra_features: Vec::new(),
1518            allow_features: Self::ALLOW_FEATURES,
1519            cargo_args: Vec::new(),
1520            artifact_kind: ToolArtifactKind::Binary,
1521        })
1522    }
1523}
1524
1525impl Builder<'_> {
1526    /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
1527    /// `host`.
1528    pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand {
1529        let mut cmd = command(self.tool_exe(tool));
1530        let compiler = self.compiler(0, self.config.host_target);
1531        let host = &compiler.host;
1532        // Prepares the `cmd` provided to be able to run the `compiler` provided.
1533        //
1534        // Notably this munges the dynamic library lookup path to point to the
1535        // right location to run `compiler`.
1536        let mut lib_paths: Vec<PathBuf> =
1537            vec![self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("deps")];
1538
1539        // On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make
1540        // mode) and that C compiler may need some extra PATH modification. Do
1541        // so here.
1542        if compiler.host.is_msvc() {
1543            let curpaths = env::var_os("PATH").unwrap_or_default();
1544            let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
1545            for (k, v) in self.cc[&compiler.host].env() {
1546                if k != "PATH" {
1547                    continue;
1548                }
1549                for path in env::split_paths(v) {
1550                    if !curpaths.contains(&path) {
1551                        lib_paths.push(path);
1552                    }
1553                }
1554            }
1555        }
1556
1557        add_dylib_path(lib_paths, &mut cmd);
1558
1559        // Provide a RUSTC for this command to use.
1560        cmd.env("RUSTC", &self.initial_rustc);
1561
1562        cmd
1563    }
1564}