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