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 clap_complete::{Generator, shells};
9
10use crate::core::build_steps::dist::distdir;
11use crate::core::build_steps::test;
12use crate::core::build_steps::tool::{self, RustcPrivateCompilers, SourceType, Tool};
13use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor};
14use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
15use crate::core::config::TargetSelection;
16use crate::core::config::flags::get_completion;
17use crate::utils::exec::command;
18use crate::{Mode, t};
19
20#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
21pub struct BuildManifest;
22
23impl Step for BuildManifest {
24    type Output = ();
25    const IS_HOST: bool = true;
26
27    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
28        run.path("src/tools/build-manifest")
29    }
30
31    fn make_run(run: RunConfig<'_>) {
32        run.builder.ensure(BuildManifest);
33    }
34
35    fn run(self, builder: &Builder<'_>) {
36        // This gets called by `promote-release`
37        // (https://github.com/rust-lang/promote-release).
38        let mut cmd = builder.tool_cmd(Tool::BuildManifest);
39        let sign = builder.config.dist_sign_folder.as_ref().unwrap_or_else(|| {
40            panic!("\n\nfailed to specify `dist.sign-folder` in `bootstrap.toml`\n\n")
41        });
42        let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| {
43            panic!("\n\nfailed to specify `dist.upload-addr` in `bootstrap.toml`\n\n")
44        });
45
46        let today = command("date").arg("+%Y-%m-%d").run_capture_stdout(builder).stdout();
47
48        cmd.arg(sign);
49        cmd.arg(distdir(builder));
50        cmd.arg(today.trim());
51        cmd.arg(addr);
52        cmd.arg(&builder.config.channel);
53
54        builder.create_dir(&distdir(builder));
55        cmd.run(builder);
56    }
57}
58
59#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
60pub struct BumpStage0;
61
62impl Step for BumpStage0 {
63    type Output = ();
64    const IS_HOST: bool = true;
65
66    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
67        run.path("src/tools/bump-stage0")
68    }
69
70    fn make_run(run: RunConfig<'_>) {
71        run.builder.ensure(BumpStage0);
72    }
73
74    fn run(self, builder: &Builder<'_>) -> Self::Output {
75        let mut cmd = builder.tool_cmd(Tool::BumpStage0);
76        cmd.args(builder.config.args());
77        cmd.run(builder);
78    }
79}
80
81#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
82pub struct ReplaceVersionPlaceholder;
83
84impl Step for ReplaceVersionPlaceholder {
85    type Output = ();
86    const IS_HOST: bool = true;
87
88    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
89        run.path("src/tools/replace-version-placeholder")
90    }
91
92    fn make_run(run: RunConfig<'_>) {
93        run.builder.ensure(ReplaceVersionPlaceholder);
94    }
95
96    fn run(self, builder: &Builder<'_>) -> Self::Output {
97        let mut cmd = builder.tool_cmd(Tool::ReplaceVersionPlaceholder);
98        cmd.arg(&builder.src);
99        cmd.run(builder);
100    }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Hash)]
104pub struct Miri {
105    target: TargetSelection,
106}
107
108impl Step for Miri {
109    type Output = ();
110
111    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
112        run.path("src/tools/miri")
113    }
114
115    fn make_run(run: RunConfig<'_>) {
116        run.builder.ensure(Miri { target: run.target });
117    }
118
119    fn run(self, builder: &Builder<'_>) {
120        let host = builder.build.host_target;
121        let target = self.target;
122
123        // `x run` uses stage 0 by default but miri does not work well with stage 0.
124        // Change the stage to 1 if it's not set explicitly.
125        let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 {
126            builder.top_stage
127        } else {
128            1
129        };
130
131        if stage == 0 {
132            eprintln!("miri cannot be run at stage 0");
133            std::process::exit(1);
134        }
135
136        // This compiler runs on the host, we'll just use it for the target.
137        let compilers = RustcPrivateCompilers::new(builder, stage, target);
138        let miri_build = builder.ensure(tool::Miri::from_compilers(compilers));
139        let host_compiler = miri_build.build_compiler;
140
141        // Get a target sysroot for Miri.
142        let miri_sysroot =
143            test::Miri::build_miri_sysroot(builder, compilers.target_compiler(), target);
144
145        // # Run miri.
146        // Running it via `cargo run` as that figures out the right dylib path.
147        // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so.
148        let mut miri = tool::prepare_tool_cargo(
149            builder,
150            host_compiler,
151            Mode::ToolRustc,
152            host,
153            Kind::Run,
154            "src/tools/miri",
155            SourceType::InTree,
156            &[],
157        );
158        miri.add_rustc_lib_path(builder);
159        miri.arg("--").arg("--target").arg(target.rustc_target_arg());
160
161        // miri tests need to know about the stage sysroot
162        miri.arg("--sysroot").arg(miri_sysroot);
163
164        // Forward arguments. This may contain further arguments to the program
165        // after another --, so this must be at the end.
166        miri.args(builder.config.args());
167
168        miri.into_cmd().run(builder);
169    }
170}
171
172#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
173pub struct CollectLicenseMetadata;
174
175impl Step for CollectLicenseMetadata {
176    type Output = PathBuf;
177    const IS_HOST: bool = true;
178
179    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
180        run.path("src/tools/collect-license-metadata")
181    }
182
183    fn make_run(run: RunConfig<'_>) {
184        run.builder.ensure(CollectLicenseMetadata);
185    }
186
187    fn run(self, builder: &Builder<'_>) -> Self::Output {
188        let Some(reuse) = &builder.config.reuse else {
189            panic!("REUSE is required to collect the license metadata");
190        };
191
192        let dest = builder.src.join("license-metadata.json");
193
194        let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
195        cmd.env("REUSE_EXE", reuse);
196        cmd.env("DEST", &dest);
197        cmd.run(builder);
198
199        dest
200    }
201}
202
203#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
204pub struct GenerateCopyright;
205
206impl Step for GenerateCopyright {
207    type Output = Vec<PathBuf>;
208    const IS_HOST: bool = true;
209
210    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
211        run.path("src/tools/generate-copyright")
212    }
213
214    fn make_run(run: RunConfig<'_>) {
215        run.builder.ensure(GenerateCopyright);
216    }
217
218    fn run(self, builder: &Builder<'_>) -> Self::Output {
219        let license_metadata = builder.src.join("license-metadata.json");
220        let dest = builder.out.join("COPYRIGHT.html");
221        let dest_libstd = builder.out.join("COPYRIGHT-library.html");
222
223        let paths_to_vendor = default_paths_to_vendor(builder);
224        for (_, submodules) in &paths_to_vendor {
225            for submodule in submodules {
226                builder.build.require_submodule(submodule, None);
227            }
228        }
229        let cargo_manifests = paths_to_vendor
230            .into_iter()
231            .map(|(path, _submodules)| path.to_str().unwrap().to_string())
232            .inspect(|path| assert!(!path.contains(','), "{path} contains a comma in its name"))
233            .collect::<Vec<_>>()
234            .join(",");
235
236        let vendored_sources = if let Some(path) = builder.vendored_crates_path() {
237            path
238        } else {
239            let cache_dir = builder.out.join("tmp").join("generate-copyright-vendor");
240            builder.ensure(Vendor {
241                sync_args: Vec::new(),
242                versioned_dirs: true,
243                root_dir: builder.src.clone(),
244                output_dir: cache_dir.clone(),
245            });
246            cache_dir
247        };
248
249        let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
250        cmd.env("CARGO_MANIFESTS", &cargo_manifests);
251        cmd.env("LICENSE_METADATA", &license_metadata);
252        cmd.env("DEST", &dest);
253        cmd.env("DEST_LIBSTD", &dest_libstd);
254        cmd.env("SRC_DIR", &builder.src);
255        cmd.env("VENDOR_DIR", &vendored_sources);
256        cmd.env("CARGO", &builder.initial_cargo);
257        cmd.env("CARGO_HOME", t!(home::cargo_home()));
258        // it is important that generate-copyright runs from the root of the
259        // source tree, because it uses relative paths
260        cmd.current_dir(&builder.src);
261        cmd.run(builder);
262
263        vec![dest, dest_libstd]
264    }
265}
266
267#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
268pub struct GenerateWindowsSys;
269
270impl Step for GenerateWindowsSys {
271    type Output = ();
272    const IS_HOST: bool = true;
273
274    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
275        run.path("src/tools/generate-windows-sys")
276    }
277
278    fn make_run(run: RunConfig<'_>) {
279        run.builder.ensure(GenerateWindowsSys);
280    }
281
282    fn run(self, builder: &Builder<'_>) {
283        let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys);
284        cmd.arg(&builder.src);
285        cmd.run(builder);
286    }
287}
288
289/// Return tuples of (shell, file containing completions).
290pub fn get_completion_paths(builder: &Builder<'_>) -> Vec<(&'static dyn Generator, PathBuf)> {
291    vec![
292        (&shells::Bash as &'static dyn Generator, builder.src.join("src/etc/completions/x.py.sh")),
293        (&shells::Zsh, builder.src.join("src/etc/completions/x.py.zsh")),
294        (&shells::Fish, builder.src.join("src/etc/completions/x.py.fish")),
295        (&shells::PowerShell, builder.src.join("src/etc/completions/x.py.ps1")),
296        (&shells::Bash, builder.src.join("src/etc/completions/x.sh")),
297        (&shells::Zsh, builder.src.join("src/etc/completions/x.zsh")),
298        (&shells::Fish, builder.src.join("src/etc/completions/x.fish")),
299        (&shells::PowerShell, builder.src.join("src/etc/completions/x.ps1")),
300    ]
301}
302
303#[derive(Debug, Clone, PartialEq, Eq, Hash)]
304pub struct GenerateCompletions;
305
306impl Step for GenerateCompletions {
307    type Output = ();
308
309    /// Uses `clap_complete` to generate shell completions.
310    fn run(self, builder: &Builder<'_>) {
311        for (shell, path) in get_completion_paths(builder) {
312            if let Some(comp) = get_completion(shell, &path) {
313                std::fs::write(&path, comp).unwrap_or_else(|e| {
314                    panic!("writing completion into {} failed: {e:?}", path.display())
315                });
316            }
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 IS_HOST: 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 IS_HOST: 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 IS_HOST: 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 IS_HOST: 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.host_target;
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 compilers = RustcPrivateCompilers::new(builder, stage, host);
468        let rustfmt_build = builder.ensure(tool::Rustfmt::from_compilers(compilers));
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}