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::Mode;
9use crate::core::build_steps::dist::distdir;
10use crate::core::build_steps::test;
11use crate::core::build_steps::tool::{self, SourceType, Tool};
12use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor};
13use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
14use crate::core::config::TargetSelection;
15use crate::core::config::flags::get_completion;
16use crate::utils::exec::command;
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        let stage = builder.top_stage;
122        if stage == 0 {
123            eprintln!("miri cannot be run at stage 0");
124            std::process::exit(1);
125        }
126
127        // This compiler runs on the host, we'll just use it for the target.
128        let target_compiler = builder.compiler(stage, host);
129        let host_compiler = tool::get_tool_rustc_compiler(builder, target_compiler);
130
131        // Get a target sysroot for Miri.
132        let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target);
133
134        // # Run miri.
135        // Running it via `cargo run` as that figures out the right dylib path.
136        // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so.
137        let mut miri = tool::prepare_tool_cargo(
138            builder,
139            host_compiler,
140            Mode::ToolRustc,
141            host,
142            Kind::Run,
143            "src/tools/miri",
144            SourceType::InTree,
145            &[],
146        );
147        miri.add_rustc_lib_path(builder);
148        miri.arg("--").arg("--target").arg(target.rustc_target_arg());
149
150        // miri tests need to know about the stage sysroot
151        miri.arg("--sysroot").arg(miri_sysroot);
152
153        // Forward arguments. This may contain further arguments to the program
154        // after another --, so this must be at the end.
155        miri.args(builder.config.args());
156
157        miri.into_cmd().run(builder);
158    }
159}
160
161#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
162pub struct CollectLicenseMetadata;
163
164impl Step for CollectLicenseMetadata {
165    type Output = PathBuf;
166    const ONLY_HOSTS: bool = true;
167
168    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
169        run.path("src/tools/collect-license-metadata")
170    }
171
172    fn make_run(run: RunConfig<'_>) {
173        run.builder.ensure(CollectLicenseMetadata);
174    }
175
176    fn run(self, builder: &Builder<'_>) -> Self::Output {
177        let Some(reuse) = &builder.config.reuse else {
178            panic!("REUSE is required to collect the license metadata");
179        };
180
181        let dest = builder.src.join("license-metadata.json");
182
183        let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
184        cmd.env("REUSE_EXE", reuse);
185        cmd.env("DEST", &dest);
186        cmd.run(builder);
187
188        dest
189    }
190}
191
192#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
193pub struct GenerateCopyright;
194
195impl Step for GenerateCopyright {
196    type Output = Vec<PathBuf>;
197    const ONLY_HOSTS: bool = true;
198
199    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
200        run.path("src/tools/generate-copyright")
201    }
202
203    fn make_run(run: RunConfig<'_>) {
204        run.builder.ensure(GenerateCopyright);
205    }
206
207    fn run(self, builder: &Builder<'_>) -> Self::Output {
208        let license_metadata = builder.src.join("license-metadata.json");
209        let dest = builder.out.join("COPYRIGHT.html");
210        let dest_libstd = builder.out.join("COPYRIGHT-library.html");
211
212        let paths_to_vendor = default_paths_to_vendor(builder);
213        for (_, submodules) in &paths_to_vendor {
214            for submodule in submodules {
215                builder.build.require_submodule(submodule, None);
216            }
217        }
218        let cargo_manifests = paths_to_vendor
219            .into_iter()
220            .map(|(path, _submodules)| path.to_str().unwrap().to_string())
221            .inspect(|path| assert!(!path.contains(','), "{path} contains a comma in its name"))
222            .collect::<Vec<_>>()
223            .join(",");
224
225        let vendored_sources = if let Some(path) = builder.vendored_crates_path() {
226            path
227        } else {
228            let cache_dir = builder.out.join("tmp").join("generate-copyright-vendor");
229            builder.ensure(Vendor {
230                sync_args: Vec::new(),
231                versioned_dirs: true,
232                root_dir: builder.src.clone(),
233                output_dir: cache_dir.clone(),
234            });
235            cache_dir
236        };
237
238        let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
239        cmd.env("CARGO_MANIFESTS", &cargo_manifests);
240        cmd.env("LICENSE_METADATA", &license_metadata);
241        cmd.env("DEST", &dest);
242        cmd.env("DEST_LIBSTD", &dest_libstd);
243        cmd.env("SRC_DIR", &builder.src);
244        cmd.env("VENDOR_DIR", &vendored_sources);
245        cmd.env("CARGO", &builder.initial_cargo);
246        // it is important that generate-copyright runs from the root of the
247        // source tree, because it uses relative paths
248        cmd.current_dir(&builder.src);
249        cmd.run(builder);
250
251        vec![dest, dest_libstd]
252    }
253}
254
255#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
256pub struct GenerateWindowsSys;
257
258impl Step for GenerateWindowsSys {
259    type Output = ();
260    const ONLY_HOSTS: bool = true;
261
262    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
263        run.path("src/tools/generate-windows-sys")
264    }
265
266    fn make_run(run: RunConfig<'_>) {
267        run.builder.ensure(GenerateWindowsSys);
268    }
269
270    fn run(self, builder: &Builder<'_>) {
271        let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys);
272        cmd.arg(&builder.src);
273        cmd.run(builder);
274    }
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Hash)]
278pub struct GenerateCompletions;
279
280macro_rules! generate_completions {
281    ( $( ( $shell:ident, $filename:expr ) ),* ) => {
282        $(
283            if let Some(comp) = get_completion($shell, &$filename) {
284                std::fs::write(&$filename, comp).expect(&format!("writing {} completion", stringify!($shell)));
285            }
286        )*
287    };
288}
289
290impl Step for GenerateCompletions {
291    type Output = ();
292
293    /// Uses `clap_complete` to generate shell completions.
294    fn run(self, builder: &Builder<'_>) {
295        use clap_complete::shells::{Bash, Fish, PowerShell, Zsh};
296
297        generate_completions!(
298            (Bash, builder.src.join("src/etc/completions/x.py.sh")),
299            (Zsh, builder.src.join("src/etc/completions/x.py.zsh")),
300            (Fish, builder.src.join("src/etc/completions/x.py.fish")),
301            (PowerShell, builder.src.join("src/etc/completions/x.py.ps1")),
302            (Bash, builder.src.join("src/etc/completions/x.sh")),
303            (Zsh, builder.src.join("src/etc/completions/x.zsh")),
304            (Fish, builder.src.join("src/etc/completions/x.fish")),
305            (PowerShell, builder.src.join("src/etc/completions/x.ps1"))
306        );
307    }
308
309    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
310        run.alias("generate-completions")
311    }
312
313    fn make_run(run: RunConfig<'_>) {
314        run.builder.ensure(GenerateCompletions);
315    }
316}
317
318#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
319pub struct UnicodeTableGenerator;
320
321impl Step for UnicodeTableGenerator {
322    type Output = ();
323    const ONLY_HOSTS: bool = true;
324
325    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
326        run.path("src/tools/unicode-table-generator")
327    }
328
329    fn make_run(run: RunConfig<'_>) {
330        run.builder.ensure(UnicodeTableGenerator);
331    }
332
333    fn run(self, builder: &Builder<'_>) {
334        let mut cmd = builder.tool_cmd(Tool::UnicodeTableGenerator);
335        cmd.arg(builder.src.join("library/core/src/unicode/unicode_data.rs"));
336        cmd.run(builder);
337    }
338}
339
340#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
341pub struct FeaturesStatusDump;
342
343impl Step for FeaturesStatusDump {
344    type Output = ();
345    const ONLY_HOSTS: bool = true;
346
347    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
348        run.path("src/tools/features-status-dump")
349    }
350
351    fn make_run(run: RunConfig<'_>) {
352        run.builder.ensure(FeaturesStatusDump);
353    }
354
355    fn run(self, builder: &Builder<'_>) {
356        let mut cmd = builder.tool_cmd(Tool::FeaturesStatusDump);
357
358        cmd.arg("--library-path");
359        cmd.arg(builder.src.join("library"));
360
361        cmd.arg("--compiler-path");
362        cmd.arg(builder.src.join("compiler"));
363
364        cmd.arg("--output-path");
365        cmd.arg(builder.out.join("features-status-dump.json"));
366
367        cmd.run(builder);
368    }
369}
370
371/// Dummy step that can be used to deliberately trigger bootstrap's step cycle
372/// detector, for automated and manual testing.
373#[derive(Clone, Debug, PartialEq, Eq, Hash)]
374pub struct CyclicStep {
375    n: u32,
376}
377
378impl Step for CyclicStep {
379    type Output = ();
380
381    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
382        run.alias("cyclic-step")
383    }
384
385    fn make_run(run: RunConfig<'_>) {
386        // Start with n=2, so that we build up a few stack entries before panicking.
387        run.builder.ensure(CyclicStep { n: 2 })
388    }
389
390    fn run(self, builder: &Builder<'_>) -> Self::Output {
391        // When n=0, the step will try to ensure itself, causing a step cycle.
392        builder.ensure(CyclicStep { n: self.n.saturating_sub(1) })
393    }
394}