Skip to main content

bootstrap/core/config/
macros.rs

1//! This module defines two macros:
2//!
3//! - `define_config!`: A declarative macro used instead of `#[derive(Deserialize)]` to reduce
4//!   compile time and binary size, especially for the bootstrap binary.
5//!
6//! - `check_ci_llvm!`: A compile-time assertion macro that ensures certain settings are
7//!   not enabled when `download-ci-llvm` is active.
8//!
9//! A declarative macro is used here in place of a procedural derive macro to minimize
10//! the compile time of the bootstrap process.
11//!
12
13// We are using a decl macro instead of a derive proc macro here to reduce the compile time of bootstrap.
14macro_rules! define_config {
15    (
16        $(#[$attr:meta])*
17            struct $name:ident {
18            $(
19                $(#[$field_attr:meta])*
20                $field:ident: Option<$field_ty:ty> = $field_key:literal,
21            )*
22        }
23    ) => {
24        $(#[$attr])*
25        pub struct $name {
26            $(
27                $(#[$field_attr])*
28                pub $field: Option<$field_ty>,
29            )*
30        }
31
32        impl crate::core::config::Merge for $name {
33            fn merge(
34                &mut self,
35                _parent_config_path: Option<std::path::PathBuf>,
36                _included_extensions: &mut std::collections::HashSet<std::path::PathBuf>,
37                other: Self,
38                replace: crate::core::config::ReplaceOpt
39            ) {
40                use crate::core::config::ReplaceOpt;
41                $(
42                    match replace {
43                        ReplaceOpt::IgnoreDuplicate => {
44                            if self.$field.is_none() {
45                                self.$field = other.$field;
46                            }
47                        },
48                        ReplaceOpt::Override => {
49                            if other.$field.is_some() {
50                                self.$field = other.$field;
51                            }
52                        }
53                        ReplaceOpt::ErrorOnDuplicate => {
54                            if other.$field.is_some() {
55                                if self.$field.is_some() {
56                                    if cfg!(test) {
57                                        panic!("overriding existing option")
58                                    } else {
59                                        eprintln!("overriding existing option: `{}`", stringify!($field));
60                                        $crate::utils::helpers::exit_process(2);
61                                    }
62                                } else {
63                                    self.$field = other.$field;
64                                }
65                            }
66                        }
67                    }
68                )*
69            }
70        }
71
72        // The following is a trimmed version of what serde_derive generates. All parts not relevant
73        // for toml deserialization have been removed. This reduces the binary size and improves
74        // compile time of bootstrap.
75        impl<'de> serde::Deserialize<'de> for $name {
76            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77            where
78                D: serde::Deserializer<'de>,
79            {
80                struct Field;
81                impl<'de> serde::de::Visitor<'de> for Field {
82                    type Value = $name;
83                    fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84                        f.write_str(concat!("struct ", stringify!($name)))
85                    }
86
87                    #[inline]
88                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
89                    where
90                        A: serde::de::MapAccess<'de>,
91                    {
92                        $(let mut $field: Option<$field_ty> = None;)*
93                        while let Some(key) =
94                            match serde::de::MapAccess::next_key::<String>(&mut map) {
95                                Ok(val) => val,
96                                Err(err) => {
97                                    return Err(err);
98                                }
99                            }
100                        {
101                            match &*key {
102                                $($field_key => {
103                                    if $field.is_some() {
104                                        return Err(<A::Error as serde::de::Error>::duplicate_field(
105                                            $field_key,
106                                        ));
107                                    }
108                                    $field = match serde::de::MapAccess::next_value::<$field_ty>(
109                                        &mut map,
110                                    ) {
111                                        Ok(val) => Some(val),
112                                        Err(err) => {
113                                            return Err(err);
114                                        }
115                                    };
116                                })*
117                                key => {
118                                    return Err(serde::de::Error::unknown_field(key, FIELDS));
119                                }
120                            }
121                        }
122                        Ok($name { $($field),* })
123                    }
124                }
125                const FIELDS: &'static [&'static str] = &[
126                    $($field_key,)*
127                ];
128                serde::Deserializer::deserialize_struct(
129                    deserializer,
130                    stringify!($name),
131                    FIELDS,
132                    Field,
133                )
134            }
135        }
136    }
137}
138
139macro_rules! check_ci_llvm {
140    ($name:expr) => {
141        assert!(
142            $name.is_none(),
143            "setting {} is incompatible with download-ci-llvm.",
144            stringify!($name).replace("_", "-")
145        );
146    };
147}
148
149pub(crate) use check_ci_llvm;
150pub(crate) use define_config;