1#[expect(clippy::module_inception)]
23mod config;
24pub mod flags;
25pub mod target_selection;
26#[cfg(test)]
27mod tests;
28pub mod toml;
29
30use std::collections::HashSet;
31use std::path::PathBuf;
32
33use build_helper::exit;
34pub use config::*;
35use serde::de::Unexpected;
36use serde::{Deserialize, Deserializer};
37use serde_derive::Deserialize;
38pub use target_selection::TargetSelection;
39pub use toml::BUILDER_CONFIG_FILENAME;
40pub use toml::change_id::ChangeId;
41pub use toml::rust::BootstrapOverrideLld;
42pub use toml::target::Target;
43
44use crate::Display;
45use crate::str::FromStr;
46
47#[macro_export]
49macro_rules! define_config {
50 ($(#[$attr:meta])* struct $name:ident {
51 $(
52 $(#[$field_attr:meta])*
53 $field:ident: Option<$field_ty:ty> = $field_key:literal,
54 )*
55 }) => {
56 $(#[$attr])*
57 pub struct $name {
58 $(
59 $(#[$field_attr])*
60 pub $field: Option<$field_ty>,
61 )*
62 }
63
64 impl Merge for $name {
65 fn merge(
66 &mut self,
67 _parent_config_path: Option<PathBuf>,
68 _included_extensions: &mut HashSet<PathBuf>,
69 other: Self,
70 replace: ReplaceOpt
71 ) {
72 $(
73 match replace {
74 ReplaceOpt::IgnoreDuplicate => {
75 if self.$field.is_none() {
76 self.$field = other.$field;
77 }
78 },
79 ReplaceOpt::Override => {
80 if other.$field.is_some() {
81 self.$field = other.$field;
82 }
83 }
84 ReplaceOpt::ErrorOnDuplicate => {
85 if other.$field.is_some() {
86 if self.$field.is_some() {
87 if cfg!(test) {
88 panic!("overriding existing option")
89 } else {
90 eprintln!("overriding existing option: `{}`", stringify!($field));
91 exit!(2);
92 }
93 } else {
94 self.$field = other.$field;
95 }
96 }
97 }
98 }
99 )*
100 }
101 }
102
103 impl<'de> Deserialize<'de> for $name {
107 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108 where
109 D: Deserializer<'de>,
110 {
111 struct Field;
112 impl<'de> serde::de::Visitor<'de> for Field {
113 type Value = $name;
114 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.write_str(concat!("struct ", stringify!($name)))
116 }
117
118 #[inline]
119 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
120 where
121 A: serde::de::MapAccess<'de>,
122 {
123 $(let mut $field: Option<$field_ty> = None;)*
124 while let Some(key) =
125 match serde::de::MapAccess::next_key::<String>(&mut map) {
126 Ok(val) => val,
127 Err(err) => {
128 return Err(err);
129 }
130 }
131 {
132 match &*key {
133 $($field_key => {
134 if $field.is_some() {
135 return Err(<A::Error as serde::de::Error>::duplicate_field(
136 $field_key,
137 ));
138 }
139 $field = match serde::de::MapAccess::next_value::<$field_ty>(
140 &mut map,
141 ) {
142 Ok(val) => Some(val),
143 Err(err) => {
144 return Err(err);
145 }
146 };
147 })*
148 key => {
149 return Err(serde::de::Error::unknown_field(key, FIELDS));
150 }
151 }
152 }
153 Ok($name { $($field),* })
154 }
155 }
156 const FIELDS: &'static [&'static str] = &[
157 $($field_key,)*
158 ];
159 Deserializer::deserialize_struct(
160 deserializer,
161 stringify!($name),
162 FIELDS,
163 Field,
164 )
165 }
166 }
167 }
168}
169
170#[macro_export]
171macro_rules! check_ci_llvm {
172 ($name:expr) => {
173 assert!(
174 $name.is_none(),
175 "setting {} is incompatible with download-ci-llvm.",
176 stringify!($name).replace("_", "-")
177 );
178 };
179}
180
181pub(crate) trait Merge {
182 fn merge(
183 &mut self,
184 parent_config_path: Option<PathBuf>,
185 included_extensions: &mut HashSet<PathBuf>,
186 other: Self,
187 replace: ReplaceOpt,
188 );
189}
190
191impl<T> Merge for Option<T> {
192 fn merge(
193 &mut self,
194 _parent_config_path: Option<PathBuf>,
195 _included_extensions: &mut HashSet<PathBuf>,
196 other: Self,
197 replace: ReplaceOpt,
198 ) {
199 match replace {
200 ReplaceOpt::IgnoreDuplicate => {
201 if self.is_none() {
202 *self = other;
203 }
204 }
205 ReplaceOpt::Override => {
206 if other.is_some() {
207 *self = other;
208 }
209 }
210 ReplaceOpt::ErrorOnDuplicate => {
211 if other.is_some() {
212 if self.is_some() {
213 if cfg!(test) {
214 panic!("overriding existing option")
215 } else {
216 eprintln!("overriding existing option");
217 exit!(2);
218 }
219 } else {
220 *self = other;
221 }
222 }
223 }
224 }
225 }
226}
227
228#[derive(Clone, Debug, Default, Eq, PartialEq)]
229pub enum CompilerBuiltins {
230 #[default]
231 BuildRustOnly,
233 BuildLLVMFuncs,
237 LinkLLVMBuiltinsLib(String),
240}
241
242impl<'de> Deserialize<'de> for CompilerBuiltins {
243 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
244 where
245 D: Deserializer<'de>,
246 {
247 Ok(match Deserialize::deserialize(deserializer)? {
248 StringOrBool::Bool(false) => Self::BuildRustOnly,
249 StringOrBool::Bool(true) => Self::BuildLLVMFuncs,
250 StringOrBool::String(path) => Self::LinkLLVMBuiltinsLib(path),
251 })
252 }
253}
254
255#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
256pub enum DebuginfoLevel {
257 #[default]
258 None,
259 LineDirectivesOnly,
260 LineTablesOnly,
261 Limited,
262 Full,
263}
264
265impl<'de> Deserialize<'de> for DebuginfoLevel {
268 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
269 where
270 D: Deserializer<'de>,
271 {
272 use serde::de::Error;
273
274 Ok(match Deserialize::deserialize(deserializer)? {
275 StringOrInt::String(s) if s == "none" => DebuginfoLevel::None,
276 StringOrInt::Int(0) => DebuginfoLevel::None,
277 StringOrInt::String(s) if s == "line-directives-only" => {
278 DebuginfoLevel::LineDirectivesOnly
279 }
280 StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly,
281 StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited,
282 StringOrInt::Int(1) => DebuginfoLevel::Limited,
283 StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full,
284 StringOrInt::Int(2) => DebuginfoLevel::Full,
285 StringOrInt::Int(n) => {
286 let other = serde::de::Unexpected::Signed(n);
287 return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
288 }
289 StringOrInt::String(s) => {
290 let other = serde::de::Unexpected::Str(&s);
291 return Err(D::Error::invalid_value(
292 other,
293 &"expected none, line-tables-only, limited, or full",
294 ));
295 }
296 })
297 }
298}
299
300impl Display for DebuginfoLevel {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 use DebuginfoLevel::*;
304 f.write_str(match self {
305 None => "0",
306 LineDirectivesOnly => "line-directives-only",
307 LineTablesOnly => "line-tables-only",
308 Limited => "1",
309 Full => "2",
310 })
311 }
312}
313
314#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
315#[serde(untagged)]
316pub enum StringOrBool {
317 String(String),
318 Bool(bool),
319}
320
321impl Default for StringOrBool {
322 fn default() -> StringOrBool {
323 StringOrBool::Bool(false)
324 }
325}
326
327impl StringOrBool {
328 pub fn is_string_or_true(&self) -> bool {
329 matches!(self, Self::String(_) | Self::Bool(true))
330 }
331}
332
333#[derive(Deserialize)]
334#[serde(untagged)]
335pub enum StringOrInt {
336 String(String),
337 Int(i64),
338}
339
340#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
341pub enum LlvmLibunwind {
342 #[default]
343 No,
344 InTree,
345 System,
346}
347
348impl FromStr for LlvmLibunwind {
349 type Err = String;
350
351 fn from_str(value: &str) -> Result<Self, Self::Err> {
352 match value {
353 "no" => Ok(Self::No),
354 "in-tree" => Ok(Self::InTree),
355 "system" => Ok(Self::System),
356 invalid => Err(format!("Invalid value '{invalid}' for rust.llvm-libunwind config.")),
357 }
358 }
359}
360
361#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
362pub enum SplitDebuginfo {
363 Packed,
364 Unpacked,
365 #[default]
366 Off,
367}
368
369impl std::str::FromStr for SplitDebuginfo {
370 type Err = ();
371
372 fn from_str(s: &str) -> Result<Self, Self::Err> {
373 match s {
374 "packed" => Ok(SplitDebuginfo::Packed),
375 "unpacked" => Ok(SplitDebuginfo::Unpacked),
376 "off" => Ok(SplitDebuginfo::Off),
377 _ => Err(()),
378 }
379 }
380}
381
382#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
383pub enum CompressDebuginfo {
384 Zlib,
385 #[default]
386 Off,
387}
388
389impl CompressDebuginfo {
390 fn default_on() -> Self {
391 Self::Zlib
392 }
393}
394
395impl std::str::FromStr for CompressDebuginfo {
396 type Err = ();
397
398 fn from_str(s: &str) -> Result<Self, Self::Err> {
399 match s {
400 "zlib" => Ok(CompressDebuginfo::Zlib),
401 "off" => Ok(CompressDebuginfo::Off),
402 _ => Err(()),
403 }
404 }
405}
406
407impl<'de> Deserialize<'de> for CompressDebuginfo {
408 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
409 where
410 D: Deserializer<'de>,
411 {
412 use serde::de::Error;
413
414 Ok(match Deserialize::deserialize(deserializer)? {
415 StringOrBool::Bool(value) => {
416 if value {
417 CompressDebuginfo::default_on()
418 } else {
419 CompressDebuginfo::Off
420 }
421 }
422 StringOrBool::String(value) => CompressDebuginfo::from_str(&value).map_err(|_| {
423 D::Error::invalid_value(Unexpected::Str(&value), &"`zlib` or `off`")
424 })?,
425 })
426 }
427}
428
429#[derive(Copy, Clone, Debug)]
431pub enum ReplaceOpt {
432 IgnoreDuplicate,
434 Override,
436 ErrorOnDuplicate,
438}
439
440#[derive(Clone, Default)]
441pub enum DryRun {
442 #[default]
444 Disabled,
445 SelfCheck,
447 UserSelected,
449}
450
451#[derive(Default, Clone, PartialEq, Debug)]
453pub enum RustcLto {
454 Off,
455 #[default]
456 ThinLocal,
457 Thin,
458 Fat,
459}
460
461impl std::str::FromStr for RustcLto {
462 type Err = String;
463
464 fn from_str(s: &str) -> Result<Self, Self::Err> {
465 match s {
466 "thin-local" => Ok(RustcLto::ThinLocal),
467 "thin" => Ok(RustcLto::Thin),
468 "fat" => Ok(RustcLto::Fat),
469 "off" => Ok(RustcLto::Off),
470 _ => Err(format!("Invalid value for rustc LTO: {s}")),
471 }
472 }
473}
474
475#[derive(Default, Debug, Clone, PartialEq)]
477pub enum GccCiMode {
478 BuildLocally,
480 #[default]
483 DownloadFromCi,
484}
485
486pub fn threads_from_config(v: u32) -> u32 {
487 match v {
488 0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
489 n => n,
490 }
491}