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