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