bootstrap/core/config/
target_selection.rs1use 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)]
8pub struct TargetSelection {
11 pub triple: Interned<String>,
12 pub file: Option<Interned<String>>,
13 pub synthetic: bool,
14}
15
16#[derive(Clone, Default, PartialEq, Eq, 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 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_windows_gnullvm(&self) -> bool {
90 self.ends_with("windows-gnullvm")
91 }
92
93 pub fn is_cygwin(&self) -> bool {
94 self.is_windows() &&
95 env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin"))
97 }
98
99 pub fn needs_crt_begin_end(&self) -> bool {
100 self.contains("musl") && !self.contains("unikraft")
101 }
102
103 pub fn filepath(&self) -> Option<&Path> {
105 self.file.as_ref().map(Path::new)
106 }
107}
108
109impl fmt::Display for TargetSelection {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "{}", self.triple)?;
112 if let Some(file) = self.file {
113 write!(f, "({file})")?;
114 }
115 Ok(())
116 }
117}
118
119impl fmt::Debug for TargetSelection {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 write!(f, "{self}")
122 }
123}
124
125impl PartialEq<&str> for TargetSelection {
126 fn eq(&self, other: &&str) -> bool {
127 self.triple == *other
128 }
129}
130
131impl AsRef<Path> for TargetSelection {
134 fn as_ref(&self) -> &Path {
135 self.triple.as_ref()
136 }
137}
138
139impl SplitDebuginfo {
140 pub fn default_for_platform(target: TargetSelection) -> Self {
143 if target.contains("apple") {
144 SplitDebuginfo::Unpacked
145 } else if target.is_windows() {
146 SplitDebuginfo::Packed
147 } else {
148 SplitDebuginfo::Off
149 }
150 }
151}