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;