bootstrap/core/config/
target_selection.rs

1use std::fmt;
2
3use crate::core::config::SplitDebuginfo;
4use crate::utils::cache::{INTERNER, Interned};
5use crate::{Path, env};
6
7#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
8// N.B.: This type is used everywhere, and the entire codebase relies on it being Copy.
9// Making !Copy is highly nontrivial!
10pub struct TargetSelection {
11    pub triple: Interned<String>,
12    pub file: Option<Interned<String>>,
13    pub synthetic: bool,
14}
15
16/// Newtype over `Vec<TargetSelection>` so we can implement custom parsing logic
17#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
18pub struct TargetSelectionList(pub Vec<TargetSelection>);
19
20pub fn target_selection_list(s: &str) -> Result<TargetSelectionList, String> {
21    Ok(TargetSelectionList(
22        s.split(',').filter(|s| !s.is_empty()).map(TargetSelection::from_user).collect(),
23    ))
24}
25
26impl TargetSelection {
27    pub fn from_user(selection: &str) -> Self {
28        let path = Path::new(selection);
29
30        let (triple, file) = if path.exists() {
31            let triple = path
32                .file_stem()
33                .expect("Target specification file has no file stem")
34                .to_str()
35                .expect("Target specification file stem is not UTF-8");
36
37            (triple, Some(selection))
38        } else {
39            (selection, None)
40        };
41
42        let triple = INTERNER.intern_str(triple);
43        let file = file.map(|f| INTERNER.intern_str(f));
44
45        Self { triple, file, synthetic: false }
46    }
47
48    pub fn create_synthetic(triple: &str, file: &str) -> Self {
49        Self {
50            triple: INTERNER.intern_str(triple),
51            file: Some(INTERNER.intern_str(file)),
52            synthetic: true,
53        }
54    }
55
56    pub fn rustc_target_arg(&self) -> &str {
57        self.file.as_ref().unwrap_or(&self.triple)
58    }
59
60    pub fn contains(&self, needle: &str) -> bool {
61        self.triple.contains(needle)
62    }
63
64    pub fn starts_with(&self, needle: &str) -> bool {
65        self.triple.starts_with(needle)
66    }
67
68    pub fn ends_with(&self, needle: &str) -> bool {
69        self.triple.ends_with(needle)
70    }
71
72    // See src/bootstrap/synthetic_targets.rs
73    pub fn is_synthetic(&self) -> bool {
74        self.synthetic
75    }
76
77    pub fn is_msvc(&self) -> bool {
78        self.contains("msvc")
79    }
80
81    pub fn is_windows(&self) -> bool {
82        self.contains("windows")
83    }
84
85    pub fn is_windows_gnu(&self) -> bool {
86        self.ends_with("windows-gnu")
87    }
88
89    pub fn is_cygwin(&self) -> bool {
90        self.is_windows() &&
91        // ref. https://cygwin.com/pipermail/cygwin/2022-February/250802.html
92        env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin"))
93    }
94
95    pub fn needs_crt_begin_end(&self) -> bool {
96        self.contains("musl") && !self.contains("unikraft")
97    }
98
99    /// Path to the file defining the custom target, if any.
100    pub fn filepath(&self) -> Option<&Path> {
101        self.file.as_ref().map(Path::new)
102    }
103}
104
105impl fmt::Display for TargetSelection {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        write!(f, "{}", self.triple)?;
108        if let Some(file) = self.file {
109            write!(f, "({file})")?;
110        }
111        Ok(())
112    }
113}
114
115impl fmt::Debug for TargetSelection {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        write!(f, "{self}")
118    }
119}
120
121impl PartialEq<&str> for TargetSelection {
122    fn eq(&self, other: &&str) -> bool {
123        self.triple == *other
124    }
125}
126
127// Targets are often used as directory names throughout bootstrap.
128// This impl makes it more ergonomics to use them as such.
129impl AsRef<Path> for TargetSelection {
130    fn as_ref(&self) -> &Path {
131        self.triple.as_ref()
132    }
133}
134
135impl SplitDebuginfo {
136    /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
137    /// `rust.split-debuginfo` in `bootstrap.example.toml`.
138    pub fn default_for_platform(target: TargetSelection) -> Self {
139        if target.contains("apple") {
140            SplitDebuginfo::Unpacked
141        } else if target.is_windows() {
142            SplitDebuginfo::Packed
143        } else {
144            SplitDebuginfo::Off
145        }
146    }
147}