|
| 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 | +
|
| 10 | +use crate::builder::{Builder, ShouldRun, Step}; |
| 11 | +use crate::config::TargetSelection; |
| 12 | +use crate::Compiler; |
| 13 | +use std::process::{Command, Stdio}; |
| 14 | + |
| 15 | +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| 16 | +pub(crate) struct MirOptPanicAbortSyntheticTarget { |
| 17 | + pub(crate) compiler: Compiler, |
| 18 | + pub(crate) base: TargetSelection, |
| 19 | +} |
| 20 | + |
| 21 | +impl Step for MirOptPanicAbortSyntheticTarget { |
| 22 | + type Output = TargetSelection; |
| 23 | + const DEFAULT: bool = true; |
| 24 | + const ONLY_HOSTS: bool = false; |
| 25 | + |
| 26 | + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { |
| 27 | + run.never() |
| 28 | + } |
| 29 | + |
| 30 | + fn run(self, builder: &Builder<'_>) -> Self::Output { |
| 31 | + create_synthetic_target(builder, self.compiler, "miropt-abort", self.base, |spec| { |
| 32 | + spec.insert("panic-strategy".into(), "abort".into()); |
| 33 | + }) |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +fn create_synthetic_target( |
| 38 | + builder: &Builder<'_>, |
| 39 | + compiler: Compiler, |
| 40 | + suffix: &str, |
| 41 | + base: TargetSelection, |
| 42 | + customize: impl FnOnce(&mut serde_json::Map<String, serde_json::Value>), |
| 43 | +) -> TargetSelection { |
| 44 | + if base.contains("synthetic") { |
| 45 | + // This check is not strictly needed, but nothing currently needs recursive synthetic |
| 46 | + // targets. If the need arises, removing this in the future *SHOULD* be safe. |
| 47 | + panic!("cannot create synthetic targets with other synthetic targets as their base"); |
| 48 | + } |
| 49 | + |
| 50 | + let name = format!("{base}-synthetic-{suffix}"); |
| 51 | + let path = builder.out.join("synthetic-target-specs").join(format!("{name}.json")); |
| 52 | + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); |
| 53 | + |
| 54 | + if builder.config.dry_run() { |
| 55 | + std::fs::write(&path, b"dry run\n").unwrap(); |
| 56 | + return TargetSelection::create_synthetic(&name, path.to_str().unwrap()); |
| 57 | + } |
| 58 | + |
| 59 | + let mut cmd = Command::new(builder.rustc(compiler)); |
| 60 | + cmd.arg("--target").arg(base.rustc_target_arg()); |
| 61 | + cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); |
| 62 | + cmd.stdout(Stdio::piped()); |
| 63 | + |
| 64 | + let output = cmd.spawn().unwrap().wait_with_output().unwrap(); |
| 65 | + if !output.status.success() { |
| 66 | + panic!("failed to gather the target spec for {base}"); |
| 67 | + } |
| 68 | + |
| 69 | + let mut spec: serde_json::Value = serde_json::from_slice(&output.stdout).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 | + let target = TargetSelection::create_synthetic(&name, path.to_str().unwrap()); |
| 79 | + crate::cc_detect::find_target(builder, target); |
| 80 | + |
| 81 | + target |
| 82 | +} |
0 commit comments