1#[expect(clippy::module_inception)]
12mod config;
13pub mod flags;
14pub(crate) mod macros;
15pub mod target_selection;
16#[cfg(test)]
17mod tests;
18pub mod toml;
19
20use std::collections::HashSet;
21use std::fmt::Display;
22use std::path::PathBuf;
23use std::str::FromStr;
24
25pub use config::*;
26use serde::de::Unexpected;
27use serde::{Deserialize, Deserializer};
28use serde_derive::Deserialize;
29pub use target_selection::TargetSelection;
30pub use toml::BUILDER_CONFIG_FILENAME;
31pub use toml::change_id::ChangeId;
32pub use toml::rust::BootstrapOverrideLld;
33pub use toml::target::Target;
34
35use crate::utils::helpers;
36
37pub(crate) trait Merge {
38 fn merge(
39 &mut self,
40 parent_config_path: Option<PathBuf>,
41 included_extensions: &mut HashSet<PathBuf>,
42 other: Self,
43 replace: ReplaceOpt,
44 );
45}
46
47impl<T> Merge for Option<T> {
48 fn merge(
49 &mut self,
50 _parent_config_path: Option<PathBuf>,
51 _included_extensions: &mut HashSet<PathBuf>,
52 other: Self,
53 replace: ReplaceOpt,
54 ) {
55 match replace {
56 ReplaceOpt::IgnoreDuplicate => {
57 if self.is_none() {
58 *self = other;
59 }
60 }
61 ReplaceOpt::Override => {
62 if other.is_some() {
63 *self = other;
64 }
65 }
66 ReplaceOpt::ErrorOnDuplicate => {
67 if other.is_some() {
68 if self.is_some() {
69 if cfg!(test) {
70 panic!("overriding existing option")
71 } else {
72 eprintln!("overriding existing option");
73 helpers::exit_process(2);
74 }
75 } else {
76 *self = other;
77 }
78 }
79 }
80 }
81 }
82}
83
84#[derive(Clone, Debug, Default, Eq, PartialEq)]
85pub enum CompilerBuiltins {
86 #[default]
87 BuildRustOnly,
89 BuildLLVMFuncs,
93 LinkLLVMBuiltinsLib(String),
96}
97
98impl<'de> Deserialize<'de> for CompilerBuiltins {
99 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
100 where
101 D: Deserializer<'de>,
102 {
103 Ok(match Deserialize::deserialize(deserializer)? {
104 StringOrBool::Bool(false) => Self::BuildRustOnly,
105 StringOrBool::Bool(true) => Self::BuildLLVMFuncs,
106 StringOrBool::String(path) => Self::LinkLLVMBuiltinsLib(path),
107 })
108 }
109}
110
111#[derive(Copy, Clone, Debug, PartialEq, Eq)]
112pub enum Allocator {
113 System,
114 Jemalloc,
115}
116
117impl Allocator {
118 pub fn feature_name(self) -> Option<&'static str> {
119 match self {
120 Allocator::System => None,
121 Allocator::Jemalloc => Some("jemalloc"),
122 }
123 }
124}
125
126impl<'de> Deserialize<'de> for Allocator {
127 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128 where
129 D: Deserializer<'de>,
130 {
131 let name = String::deserialize(deserializer)?;
132 match name.as_str() {
133 "system" => Ok(Self::System),
134 "jemalloc" => Ok(Self::Jemalloc),
135 other => Err(serde::de::Error::unknown_variant(other, &["system", "jemalloc"])),
136 }
137 }
138}
139
140#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
141pub enum DebuginfoLevel {
142 #[default]
143 None,
144 LineDirectivesOnly,
145 LineTablesOnly,
146 Limited,
147 Full,
148}
149
150impl<'de> Deserialize<'de> for DebuginfoLevel {
153 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154 where
155 D: Deserializer<'de>,
156 {
157 use serde::de::Error;
158
159 Ok(match Deserialize::deserialize(deserializer)? {
160 StringOrInt::String(s) if s == "none" => DebuginfoLevel::None,
161 StringOrInt::Int(0) => DebuginfoLevel::None,
162 StringOrInt::String(s) if s == "line-directives-only" => {
163 DebuginfoLevel::LineDirectivesOnly
164 }
165 StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly,
166 StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited,
167 StringOrInt::Int(1) => DebuginfoLevel::Limited,
168 StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full,
169 StringOrInt::Int(2) => DebuginfoLevel::Full,
170 StringOrInt::Int(n) => {
171 let other = serde::de::Unexpected::Signed(n);
172 return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
173 }
174 StringOrInt::String(s) => {
175 let other = serde::de::Unexpected::Str(&s);
176 return Err(D::Error::invalid_value(
177 other,
178 &"expected none, line-tables-only, limited, or full",
179 ));
180 }
181 })
182 }
183}
184
185impl Display for DebuginfoLevel {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 use DebuginfoLevel::*;
189 f.write_str(match self {
190 None => "0",
191 LineDirectivesOnly => "line-directives-only",
192 LineTablesOnly => "line-tables-only",
193 Limited => "1",
194 Full => "2",
195 })
196 }
197}
198
199#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
200#[serde(untagged)]
201pub enum StringOrBool {
202 String(String),
203 Bool(bool),
204}
205
206impl Default for StringOrBool {
207 fn default() -> StringOrBool {
208 StringOrBool::Bool(false)
209 }
210}
211
212impl StringOrBool {
213 pub fn is_string_or_true(&self) -> bool {
214 matches!(self, Self::String(_) | Self::Bool(true))
215 }
216}
217
218#[derive(Deserialize)]
219#[serde(untagged)]
220pub enum StringOrInt {
221 String(String),
222 Int(i64),
223}
224
225#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
226pub enum LlvmLibunwind {
227 #[default]
228 No,
229 InTree,
230 System,
231}
232
233impl FromStr for LlvmLibunwind {
234 type Err = String;
235
236 fn from_str(value: &str) -> Result<Self, Self::Err> {
237 match value {
238 "no" => Ok(Self::No),
239 "in-tree" => Ok(Self::InTree),
240 "system" => Ok(Self::System),
241 invalid => Err(format!("Invalid value '{invalid}' for rust.llvm-libunwind config.")),
242 }
243 }
244}
245
246#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
247pub enum SplitDebuginfo {
248 Packed,
249 Unpacked,
250 #[default]
251 Off,
252}
253
254impl std::str::FromStr for SplitDebuginfo {
255 type Err = ();
256
257 fn from_str(s: &str) -> Result<Self, Self::Err> {
258 match s {
259 "packed" => Ok(SplitDebuginfo::Packed),
260 "unpacked" => Ok(SplitDebuginfo::Unpacked),
261 "off" => Ok(SplitDebuginfo::Off),
262 _ => Err(()),
263 }
264 }
265}
266
267#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
268pub enum CompressDebuginfo {
269 Zlib,
270 #[default]
271 Off,
272}
273
274impl CompressDebuginfo {
275 fn default_on() -> Self {
276 Self::Zlib
277 }
278}
279
280impl std::str::FromStr for CompressDebuginfo {
281 type Err = ();
282
283 fn from_str(s: &str) -> Result<Self, Self::Err> {
284 match s {
285 "zlib" => Ok(CompressDebuginfo::Zlib),
286 "off" => Ok(CompressDebuginfo::Off),
287 _ => Err(()),
288 }
289 }
290}
291
292impl<'de> Deserialize<'de> for CompressDebuginfo {
293 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
294 where
295 D: Deserializer<'de>,
296 {
297 use serde::de::Error;
298
299 Ok(match Deserialize::deserialize(deserializer)? {
300 StringOrBool::Bool(value) => {
301 if value {
302 CompressDebuginfo::default_on()
303 } else {
304 CompressDebuginfo::Off
305 }
306 }
307 StringOrBool::String(value) => CompressDebuginfo::from_str(&value).map_err(|_| {
308 D::Error::invalid_value(Unexpected::Str(&value), &"`zlib` or `off`")
309 })?,
310 })
311 }
312}
313
314#[derive(Copy, Clone, Debug)]
316pub enum ReplaceOpt {
317 IgnoreDuplicate,
319 Override,
321 ErrorOnDuplicate,
323}
324
325#[derive(Clone, Default)]
326pub enum DryRun {
327 #[default]
329 Disabled,
330 SelfCheck,
332 UserSelected,
334}
335
336#[derive(Default, Clone, PartialEq, Debug)]
338pub enum RustcLto {
339 Off,
340 #[default]
341 ThinLocal,
342 Thin,
343 Fat,
344}
345
346impl std::str::FromStr for RustcLto {
347 type Err = String;
348
349 fn from_str(s: &str) -> Result<Self, Self::Err> {
350 match s {
351 "thin-local" => Ok(RustcLto::ThinLocal),
352 "thin" => Ok(RustcLto::Thin),
353 "fat" => Ok(RustcLto::Fat),
354 "off" => Ok(RustcLto::Off),
355 _ => Err(format!("Invalid value for rustc LTO: {s}")),
356 }
357 }
358}
359
360#[derive(Default, Debug, Clone, PartialEq)]
362pub enum GccCiMode {
363 BuildLocally,
365 #[default]
368 DownloadFromCi,
369}
370
371#[derive(Clone, Debug, PartialEq)]
372pub enum DebuggerPath {
373 Path(PathBuf),
375 Discover,
377}
378
379impl<'d> Deserialize<'d> for DebuggerPath {
380 fn deserialize<D>(deserializer: D) -> Result<Self, <D as serde::Deserializer<'d>>::Error>
381 where
382 D: serde::Deserializer<'d>,
383 {
384 let value = String::deserialize(deserializer)?;
385 match value.as_str() {
386 "discover" => Ok(Self::Discover),
387 path => Ok(Self::Path(PathBuf::from(path))),
388 }
389 }
390}
391
392pub fn threads_from_config(v: u32) -> u32 {
393 match v {
394 0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
395 n => n,
396 }
397}