bootstrap/core/build_steps/
run.rs

1//! Build-and-run steps for in-repo tools
2//!
3//! A bit of a hodge-podge as e.g. if a tool's a test fixture it should be in `build_steps::test`.
4//! If it can be reached from `./x.py run` it can go here.
5
6use std::path::PathBuf;
7
8use build_helper::exit;
9use clap_complete::{Generator, shells};
10
11use crate::core::build_steps::dist::distdir;
12use crate::core::build_steps::test;
13use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool};
14use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor};
15use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata};
16use crate::core::config::TargetSelection;
17use crate::core::config::flags::get_completion;
18use crate::utils::exec::command;
19use crate::{Mode, t};
20
21#[derive(Debug, Clone, Hash, PartialEq, Eq)]
22pub struct BuildManifest;
23
24impl Step for BuildManifest {
25    type Output = ();
26    const IS_HOST: bool = true;
27
28    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
29        run.path("src/tools/build-manifest")
30    }
31
32    fn make_run(run: RunConfig<'_>) {
33        run.builder.ensure(BuildManifest);
34    }
35
36    fn run(self, builder: &Builder<'_>) {
37        // This gets called by `promote-release`
38        // (https://github.com/rust-lang/promote-release).
39        let mut cmd = builder.tool_cmd(Tool::BuildManifest);
40        let sign = builder.config.dist_sign_folder.as_ref().unwrap_or_else(|| {
41            panic!("\n\nfailed to specify `dist.sign-folder` in `bootstrap.toml`\n\n")
42        });
43        let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| {
44            panic!("\n\nfailed to specify `dist.upload-addr` in `bootstrap.toml`\n\n")
45        });
46
47        let today = command("date").arg("+%Y-%m-%d").run_capture_stdout(builder).stdout();
48
49        cmd.arg(sign);
50        cmd.arg(distdir(builder));
51        cmd.arg(today.trim());
52        cmd.arg(addr);
53        cmd.arg(&builder.config.channel);
54
55        builder.create_dir(&distdir(builder));
56        cmd.run(builder);
57    }
58}
59
60#[derive(Debug, Clone, Hash, PartialEq, Eq)]
61pub struct BumpStage0;
62
63impl Step for BumpStage0 {
64    type Output = ();
65    const IS_HOST: bool = true;
66
67    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
68        run.path("src/tools/bump-stage0")
69    }
70
71    fn make_run(run: RunConfig<'_>) {
72        run.builder.ensure(BumpStage0);
73    }
74
75    fn run(self, builder: &Builder<'_>) -> Self::Output {
76        let mut cmd = builder.tool_cmd(Tool::BumpStage0);
77        cmd.args(builder.config.args());
78        cmd.run(builder);
79    }
80}
81
82#[derive(Debug, Clone, Hash, PartialEq, Eq)]
83pub struct ReplaceVersionPlaceholder;
84
85impl Step for ReplaceVersionPlaceholder {
86    type Output = ();
87    const IS_HOST: bool = true;
88
89    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
90        run.path("src/tools/replace-version-placeholder")
91    }
92
93    fn make_run(run: RunConfig<'_>) {
94        run.builder.ensure(ReplaceVersionPlaceholder);
95    }
96
97    fn run(self, builder: &Builder<'_>) -> Self::Output {
98        let mut cmd = builder.tool_cmd(Tool::ReplaceVersionPlaceholder);
99        cmd.arg(&builder.src);
100        cmd.run(builder);
101    }
102}
103
104/// Invoke the Miri tool on a specified file.
105///
106/// Note that Miri always executed on the host, as it is an interpreter.
107/// That means that `x run miri --target FOO` will build miri for the host,
108/// prepare a miri sysroot for the target `FOO` and then execute miri with
109/// the target `FOO`.
110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
111pub struct Miri {
112    /// The build compiler that will build miri and the target compiler to which miri links.
113    compilers: RustcPrivateCompilers,
114    /// The target which will miri interpret.
115    target: TargetSelection,
116}
117
118impl Step for Miri {
119    type Output = ();
120
121    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
122        run.path("src/tools/miri")
123    }
124
125    fn make_run(run: RunConfig<'_>) {
126        let builder = run.builder;
127
128        // `x run` uses stage 0 by default, but miri does not work well with stage 0.
129        // Change the stage to 1 if it's not set explicitly.
130        let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 {
131            builder.top_stage
132        } else {
133            1
134        };
135
136        if stage == 0 {
137            eprintln!("ERROR: miri cannot be run at stage 0");
138            exit!(1);
139        }
140
141        // Miri always runs on the host, because it can interpret code for any target
142        let compilers = RustcPrivateCompilers::new(builder, stage, builder.host_target);
143
144        run.builder.ensure(Miri { compilers, target: run.target });
145    }
146
147    fn run(self, builder: &Builder<'_>) {
148        let host = builder.build.host_target;
149        let compilers = self.compilers;
150        let target = self.target;
151
152        builder.ensure(tool::Miri::from_compilers(compilers));
153
154        // Get a target sysroot for Miri.
155        let miri_sysroot =
156            test::Miri::build_miri_sysroot(builder, compilers.target_compiler(), target);
157
158        // # Run miri.
159        // Running it via `cargo run` as that figures out the right dylib path.
160        // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so.
161        let mut miri = tool::prepare_tool_cargo(
162            builder,
163            compilers.build_compiler(),
164            Mode::ToolRustcPrivate,
165            host,
166            Kind::Run,
167            "src/tools/miri",
168            SourceType::InTree,
169            &[],
170        );
171        miri.add_rustc_lib_path(builder);
172        miri.arg("--").arg("--target").arg(target.rustc_target_arg());
173
174        // miri tests need to know about the stage sysroot
175        miri.arg("--sysroot").arg(miri_sysroot);
176
177        // Forward arguments. This may contain further arguments to the program
178        // after another --, so this must be at the end.
179        miri.args(builder.config.args());
180
181        miri.into_cmd().run(builder);
182    }
183
184    fn metadata(&self) -> Option<StepMetadata> {
185        Some(StepMetadata::run("miri", self.target).built_by(self.compilers.build_compiler()))
186    }
187}
188
189#[derive(Debug, Clone, Hash, PartialEq, Eq)]
190pub struct CollectLicenseMetadata;
191
192impl Step for CollectLicenseMetadata {
193    type Output = PathBuf;
194    const IS_HOST: bool = true;
195
196    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
197        run.path("src/tools/collect-license-metadata")
198    }
199
200    fn make_run(run: RunConfig<'_>) {
201        run.builder.ensure(CollectLicenseMetadata);
202    }
203
204    fn run(self, builder: &Builder<'_>) -> Self::Output {
205        let Some(reuse) = &builder.config.reuse else {
206            panic!("REUSE is required to collect the license metadata");
207        };
208
209        let dest = builder.src.join("license-metadata.json");
210
211        let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
212        cmd.env("REUSE_EXE", reuse);
213        cmd.env("DEST", &dest);
214        cmd.run(builder);
215
216        dest
217    }
218}
219
220#[derive(Debug, Clone, Hash, PartialEq, Eq)]
221pub struct GenerateCopyright;
222
223impl Step for GenerateCopyright {
224    type Output = Vec<PathBuf>;
225    const IS_HOST: bool = true;
226
227    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
228        run.path("src/tools/generate-copyright")
229    }
230
231    fn make_run(run: RunConfig<'_>) {
232        run.builder.ensure(GenerateCopyright);
233    }
234
235    fn run(self, builder: &Builder<'_>) -> Self::Output {
236        let license_metadata = builder.src.join("license-metadata.json");
237        let dest = builder.out.join("COPYRIGHT.html");
238        let dest_libstd = builder.out.join("COPYRIGHT-library.html");
239
240        let paths_to_vendor = default_paths_to_vendor(builder);
241        for (_, submodules) in &paths_to_vendor {
242            for submodule in submodules {
243                builder.build.require_submodule(submodule, None);
244            }
245        }
246        let cargo_manifests = paths_to_vendor
247            .into_iter()
248            .map(|(path, _submodules)| path.to_str().unwrap().to_string())
249            .inspect(|path| assert!(!path.contains(','), "{path} contains a comma in its name"))
250            .collect::<Vec<_>>()
251            .join(",");
252
253        let vendored_sources = if let Some(path) = builder.vendored_crates_path() {
254            path
255        } else {
256            let cache_dir = builder.out.join("tmp").join("generate-copyright-vendor");
257            builder.ensure(Vendor {
258                sync_args: Vec::new(),
259                versioned_dirs: true,
260                root_dir: builder.src.clone(),
261                output_dir: cache_dir.clone(),
262            });
263            cache_dir
264        };
265
266        let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
267        cmd.env("CARGO_MANIFESTS", &cargo_manifests);
268        cmd.env("LICENSE_METADATA", &license_metadata);
269        cmd.env("DEST", &dest);
270        cmd.env("DEST_LIBSTD", &dest_libstd);
271        cmd.env("SRC_DIR", &builder.src);
272        cmd.env("VENDOR_DIR", &vendored_sources);
273        cmd.env("CARGO", &builder.initial_cargo);
274        cmd.env("CARGO_HOME", t!(home::cargo_home()));
275        // it is important that generate-copyright runs from the root of the
276        // source tree, because it uses relative paths
277        cmd.current_dir(&builder.src);
278        cmd.run(builder);
279
280        vec![dest, dest_libstd]
281    }
282}
283
284#[derive(Debug, Clone, Hash, PartialEq, Eq)]
285pub struct GenerateWindowsSys;
286
287impl Step for GenerateWindowsSys {
288    type Output = ();
289    const IS_HOST: bool = true;
290
291    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
292        run.path("src/tools/generate-windows-sys")
293    }
294
295    fn make_run(run: RunConfig<'_>) {
296        run.builder.ensure(GenerateWindowsSys);
297    }
298
299    fn run(self, builder: &Builder<'_>) {
300        let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys);
301        cmd.arg(&builder.src);
302        cmd.run(builder);
303    }
304}
305
306/// Return tuples of (shell, file containing completions).
307pub fn get_completion_paths(builder: &Builder<'_>) -> Vec<(&'static dyn Generator, PathBuf)> {
308    vec![
309        (&shells::Bash as &'static dyn Generator, builder.src.join("src/etc/completions/x.py.sh")),
310        (&shells::Zsh, builder.src.join("src/etc/completions/x.py.zsh")),
311        (&shells::Fish, builder.src.join("src/etc/completions/x.py.fish")),
312        (&shells::PowerShell, builder.src.join("src/etc/completions/x.py.ps1")),
313        (&shells::Bash, builder.src.join("src/etc/completions/x.sh")),
314        (&shells::Zsh, builder.src.join("src/etc/completions/x.zsh")),
315        (&shells::Fish, builder.src.join("src/etc/completions/x.fish")),
316        (&shells::PowerShell, builder.src.join("src/etc/completions/x.ps1")),
317    ]
318}
319
320#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub struct GenerateCompletions;
322
323impl Step for GenerateCompletions {
324    type Output = ();
325
326    /// Uses `clap_complete` to generate shell completions.
327    fn run(self, builder: &Builder<'_>) {
328        for (shell, path) in get_completion_paths(builder) {
329            if let Some(comp) = get_completion(shell, &path) {
330                std::fs::write(&path, comp).unwrap_or_else(|e| {
331                    panic!("writing completion into {} failed: {e:?}", path.display())
332                });
333            }
334        }
335    }
336
337    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
338        run.alias("generate-completions")
339    }
340
341    fn make_run(run: RunConfig<'_>) {
342        run.builder.ensure(GenerateCompletions);
343    }
344}
345
346#[derive(Debug, Clone, Hash, PartialEq, Eq)]
347pub struct UnicodeTableGenerator;
348
349impl Step for UnicodeTableGenerator {
350    type Output = ();
351    const IS_HOST: bool = true;
352
353    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
354        run.path("src/tools/unicode-table-generator")
355    }
356
357    fn make_run(run: RunConfig<'_>) {
358        run.builder.ensure(UnicodeTableGenerator);
359    }
360
361    fn run(self, builder: &Builder<'_>) {
362        let mut cmd = builder.tool_cmd(Tool::UnicodeTableGenerator);
363        cmd.arg(builder.src.join("library/core/src/unicode/unicode_data.rs"));
364        cmd.run(builder);
365    }
366}
367
368#[derive(Debug, Clone, Hash, PartialEq, Eq)]
369pub struct FeaturesStatusDump;
370
371impl Step for FeaturesStatusDump {
372    type Output = ();
373    const IS_HOST: bool = true;
374
375    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
376        run.path("src/tools/features-status-dump")
377    }
378
379    fn make_run(run: RunConfig<'_>) {
380        run.builder.ensure(FeaturesStatusDump);
381    }
382
383    fn run(self, builder: &Builder<'_>) {
384        let mut cmd = builder.tool_cmd(Tool::FeaturesStatusDump);
385
386        cmd.arg("--library-path");
387        cmd.arg(builder.src.join("library"));
388
389        cmd.arg("--compiler-path");
390        cmd.arg(builder.src.join("compiler"));
391
392        cmd.arg("--output-path");
393        cmd.arg(builder.out.join("features-status-dump.json"));
394
395        cmd.run(builder);
396    }
397}
398
399/// Dummy step that can be used to deliberately trigger bootstrap's step cycle
400/// detector, for automated and manual testing.
401#[derive(Clone, Debug, PartialEq, Eq, Hash)]
402pub struct CyclicStep {
403    n: u32,
404}
405
406impl Step for CyclicStep {
407    type Output = ();
408
409    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
410        run.alias("cyclic-step")
411    }
412
413    fn make_run(run: RunConfig<'_>) {
414        // Start with n=2, so that we build up a few stack entries before panicking.
415        run.builder.ensure(CyclicStep { n: 2 })
416    }
417
418    fn run(self, builder: &Builder<'_>) -> Self::Output {
419        // When n=0, the step will try to ensure itself, causing a step cycle.
420        builder.ensure(CyclicStep { n: self.n.saturating_sub(1) })
421    }
422}
423
424/// Step to manually run the coverage-dump tool (`./x run coverage-dump`).
425///
426/// The coverage-dump tool is an internal detail of coverage tests, so this run
427/// step is only needed when testing coverage-dump manually.
428#[derive(Debug, Clone, Hash, PartialEq, Eq)]
429pub struct CoverageDump;
430
431impl Step for CoverageDump {
432    type Output = ();
433
434    const DEFAULT: bool = false;
435    const IS_HOST: bool = true;
436
437    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
438        run.path("src/tools/coverage-dump")
439    }
440
441    fn make_run(run: RunConfig<'_>) {
442        run.builder.ensure(Self {});
443    }
444
445    fn run(self, builder: &Builder<'_>) {
446        let mut cmd = builder.tool_cmd(Tool::CoverageDump);
447        cmd.args(&builder.config.free_args);
448        cmd.run(builder);
449    }
450}
451
452#[derive(Debug, Clone, PartialEq, Eq, Hash)]
453pub struct Rustfmt;
454
455impl Step for Rustfmt {
456    type Output = ();
457    const IS_HOST: bool = true;
458
459    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
460        run.path("src/tools/rustfmt")
461    }
462
463    fn make_run(run: RunConfig<'_>) {
464        run.builder.ensure(Rustfmt);
465    }
466
467    fn run(self, builder: &Builder<'_>) {
468        let host = builder.build.host_target;
469
470        // `x run` uses stage 0 by default but rustfmt does not work well with stage 0.
471        // Change the stage to 1 if it's not set explicitly.
472        let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 {
473            builder.top_stage
474        } else {
475            1
476        };
477
478        if stage == 0 {
479            eprintln!("rustfmt cannot be run at stage 0");
480            eprintln!("HELP: Use `x fmt` to use stage 0 rustfmt.");
481            std::process::exit(1);
482        }
483
484        let compilers = RustcPrivateCompilers::new(builder, stage, host);
485        let rustfmt_build = builder.ensure(tool::Rustfmt::from_compilers(compilers));
486
487        let mut rustfmt = tool::prepare_tool_cargo(
488            builder,
489            rustfmt_build.build_compiler,
490            Mode::ToolRustcPrivate,
491            host,
492            Kind::Run,
493            "src/tools/rustfmt",
494            SourceType::InTree,
495            &[],
496        );
497
498        rustfmt.args(["--bin", "rustfmt", "--"]);
499        rustfmt.args(builder.config.args());
500
501        rustfmt.into_cmd().run(builder);
502    }
503}