bootstrap/core/build_steps/
synthetic_targets.rs

1//! In some cases, parts of bootstrap need to change part of a target spec just for one or a few
2//! steps. Adding these targets to rustc proper would "leak" this implementation detail of
3//! bootstrap, and would make it more complex to apply additional changes if the need arises.
4//!
5//! To address that problem, this module implements support for "synthetic targets". Synthetic
6//! targets are custom target specs generated using builtin target specs as their base. You can use
7//! one of the target specs already defined in this module, or create new ones by adding a new step
8//! that calls create_synthetic_target.
9
10use crate::Compiler;
11use crate::core::builder::{Builder, ShouldRun, Step};
12use crate::core::config::TargetSelection;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub(crate) struct MirOptPanicAbortSyntheticTarget {
16    pub(crate) compiler: Compiler,
17    pub(crate) base: TargetSelection,
18}
19
20impl Step for MirOptPanicAbortSyntheticTarget {
21    type Output = TargetSelection;
22
23    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
24        run.never()
25    }
26
27    fn is_default_step(_builder: &Builder<'_>) -> bool {
28        true
29    }
30
31    fn run(self, builder: &Builder<'_>) -> Self::Output {
32        create_synthetic_target(builder, self.compiler, "miropt-abort", self.base, |spec| {
33            spec.insert("panic-strategy".into(), "abort".into());
34        })
35    }
36}
37
38fn create_synthetic_target(
39    builder: &Builder<'_>,
40    compiler: Compiler,
41    suffix: &str,
42    base: TargetSelection,
43    customize: impl FnOnce(&mut serde_json::Map<String, serde_json::Value>),
44) -> TargetSelection {
45    if base.contains("synthetic") {
46        // This check is not strictly needed, but nothing currently needs recursive synthetic
47        // targets. If the need arises, removing this in the future *SHOULD* be safe.
48        panic!("cannot create synthetic targets with other synthetic targets as their base");
49    }
50
51    let name = format!("{base}-synthetic-{suffix}");
52    let path = builder.out.join("synthetic-target-specs").join(format!("{name}.json"));
53    std::fs::create_dir_all(path.parent().unwrap()).unwrap();
54
55    if builder.config.dry_run() {
56        std::fs::write(&path, b"dry run\n").unwrap();
57        return TargetSelection::create_synthetic(&name, path.to_str().unwrap());
58    }
59
60    let mut cmd = builder.rustc_cmd(compiler);
61    cmd.arg("--target").arg(base.rustc_target_arg());
62    cmd.args(["-Zunstable-options", "--print", "target-spec-json"]);
63
64    // If `rust.channel` is set to either beta or stable, rustc will complain that
65    // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here.
66    cmd.env("RUSTC_BOOTSTRAP", "1");
67
68    let output = cmd.run_capture(builder).stdout();
69    let mut spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap();
70    let spec_map = spec.as_object_mut().unwrap();
71
72    // The `is-builtin` attribute of a spec needs to be removed, otherwise rustc will complain.
73    spec_map.remove("is-builtin");
74
75    customize(spec_map);
76
77    std::fs::write(&path, serde_json::to_vec_pretty(&spec).unwrap()).unwrap();
78    TargetSelection::create_synthetic(&name, path.to_str().unwrap())
79}