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