bootstrap/core/config/
mod.rs

1//! Entry point for the `config` module.
2//!
3//! This module defines two macros:
4//!
5//! - `define_config!`: A declarative macro used instead of `#[derive(Deserialize)]` to reduce
6//!   compile time and binary size, especially for the bootstrap binary.
7//!
8//! - `check_ci_llvm!`: A compile-time assertion macro that ensures certain settings are
9//!   not enabled when `download-ci-llvm` is active.
10//!
11//! A declarative macro is used here in place of a procedural derive macro to minimize
12//! the compile time of the bootstrap process.
13//!
14//! Additionally, this module defines common types, enums, and helper functions used across
15//! various TOML configuration sections in `bootstrap.toml`.
16//!
17//! It provides shared definitions for:
18//! - Data types deserialized from TOML.
19//! - Utility enums for specific configuration options.
20//! - Helper functions for managing configuration values.
21
22#[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// We are using a decl macro instead of a derive proc macro here to reduce the compile time of bootstrap.
47#[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        // The following is a trimmed version of what serde_derive generates. All parts not relevant
97        // for toml deserialization have been removed. This reduces the binary size and improves
98        // compile time of bootstrap.
99        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(Clone, Debug, Default, Eq, PartialEq)]
222pub enum CompilerBuiltins {
223    #[default]
224    // Only build native rust intrinsic compiler functions.
225    BuildRustOnly,
226    // Some intrinsic functions have a C implementation provided by LLVM's
227    // compiler-rt builtins library. Build them from the LLVM source included
228    // with Rust.
229    BuildLLVMFuncs,
230    // Similar to BuildLLVMFuncs, but specify a path to an existing library
231    // containing LLVM's compiler-rt builtins instead of compiling them.
232    LinkLLVMBuiltinsLib(String),
233}
234
235impl<'de> Deserialize<'de> for CompilerBuiltins {
236    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
237    where
238        D: Deserializer<'de>,
239    {
240        Ok(match Deserialize::deserialize(deserializer)? {
241            StringOrBool::Bool(false) => Self::BuildRustOnly,
242            StringOrBool::Bool(true) => Self::BuildLLVMFuncs,
243            StringOrBool::String(path) => Self::LinkLLVMBuiltinsLib(path),
244        })
245    }
246}
247
248#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
249pub enum DebuginfoLevel {
250    #[default]
251    None,
252    LineDirectivesOnly,
253    LineTablesOnly,
254    Limited,
255    Full,
256}
257
258// NOTE: can't derive(Deserialize) because the intermediate trip through toml::Value only
259// deserializes i64, and derive() only generates visit_u64
260impl<'de> Deserialize<'de> for DebuginfoLevel {
261    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
262    where
263        D: Deserializer<'de>,
264    {
265        use serde::de::Error;
266
267        Ok(match Deserialize::deserialize(deserializer)? {
268            StringOrInt::String(s) if s == "none" => DebuginfoLevel::None,
269            StringOrInt::Int(0) => DebuginfoLevel::None,
270            StringOrInt::String(s) if s == "line-directives-only" => {
271                DebuginfoLevel::LineDirectivesOnly
272            }
273            StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly,
274            StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited,
275            StringOrInt::Int(1) => DebuginfoLevel::Limited,
276            StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full,
277            StringOrInt::Int(2) => DebuginfoLevel::Full,
278            StringOrInt::Int(n) => {
279                let other = serde::de::Unexpected::Signed(n);
280                return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
281            }
282            StringOrInt::String(s) => {
283                let other = serde::de::Unexpected::Str(&s);
284                return Err(D::Error::invalid_value(
285                    other,
286                    &"expected none, line-tables-only, limited, or full",
287                ));
288            }
289        })
290    }
291}
292
293/// Suitable for passing to `-C debuginfo`
294impl Display for DebuginfoLevel {
295    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296        use DebuginfoLevel::*;
297        f.write_str(match self {
298            None => "0",
299            LineDirectivesOnly => "line-directives-only",
300            LineTablesOnly => "line-tables-only",
301            Limited => "1",
302            Full => "2",
303        })
304    }
305}
306
307#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
308#[serde(untagged)]
309pub enum StringOrBool {
310    String(String),
311    Bool(bool),
312}
313
314impl Default for StringOrBool {
315    fn default() -> StringOrBool {
316        StringOrBool::Bool(false)
317    }
318}
319
320impl StringOrBool {
321    pub fn is_string_or_true(&self) -> bool {
322        matches!(self, Self::String(_) | Self::Bool(true))
323    }
324}
325
326#[derive(Deserialize)]
327#[serde(untagged)]
328pub enum StringOrInt {
329    String(String),
330    Int(i64),
331}
332
333#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
334pub enum LlvmLibunwind {
335    #[default]
336    No,
337    InTree,
338    System,
339}
340
341impl FromStr for LlvmLibunwind {
342    type Err = String;
343
344    fn from_str(value: &str) -> Result<Self, Self::Err> {
345        match value {
346            "no" => Ok(Self::No),
347            "in-tree" => Ok(Self::InTree),
348            "system" => Ok(Self::System),
349            invalid => Err(format!("Invalid value '{invalid}' for rust.llvm-libunwind config.")),
350        }
351    }
352}
353
354#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
355pub enum SplitDebuginfo {
356    Packed,
357    Unpacked,
358    #[default]
359    Off,
360}
361
362impl std::str::FromStr for SplitDebuginfo {
363    type Err = ();
364
365    fn from_str(s: &str) -> Result<Self, Self::Err> {
366        match s {
367            "packed" => Ok(SplitDebuginfo::Packed),
368            "unpacked" => Ok(SplitDebuginfo::Unpacked),
369            "off" => Ok(SplitDebuginfo::Off),
370            _ => Err(()),
371        }
372    }
373}
374
375/// Describes how to handle conflicts in merging two `TomlConfig`
376#[derive(Copy, Clone, Debug)]
377pub enum ReplaceOpt {
378    /// Silently ignore a duplicated value
379    IgnoreDuplicate,
380    /// Override the current value, even if it's `Some`
381    Override,
382    /// Exit with an error on duplicate values
383    ErrorOnDuplicate,
384}
385
386#[derive(Clone, Default)]
387pub enum DryRun {
388    /// This isn't a dry run.
389    #[default]
390    Disabled,
391    /// This is a dry run enabled by bootstrap itself, so it can verify that no work is done.
392    SelfCheck,
393    /// This is a dry run enabled by the `--dry-run` flag.
394    UserSelected,
395}
396
397/// LTO mode used for compiling rustc itself.
398#[derive(Default, Clone, PartialEq, Debug)]
399pub enum RustcLto {
400    Off,
401    #[default]
402    ThinLocal,
403    Thin,
404    Fat,
405}
406
407impl std::str::FromStr for RustcLto {
408    type Err = String;
409
410    fn from_str(s: &str) -> Result<Self, Self::Err> {
411        match s {
412            "thin-local" => Ok(RustcLto::ThinLocal),
413            "thin" => Ok(RustcLto::Thin),
414            "fat" => Ok(RustcLto::Fat),
415            "off" => Ok(RustcLto::Off),
416            _ => Err(format!("Invalid value for rustc LTO: {s}")),
417        }
418    }
419}
420
421/// Determines how will GCC be provided.
422#[derive(Default, Clone)]
423pub enum GccCiMode {
424    /// Build GCC from the local `src/gcc` submodule.
425    BuildLocally,
426    /// Try to download GCC from CI.
427    /// If it is not available on CI, it will be built locally instead.
428    #[default]
429    DownloadFromCi,
430}
431
432pub fn threads_from_config(v: u32) -> u32 {
433    match v {
434        0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
435        n => n,
436    }
437}