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