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