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