bootstrap/core/config/toml/
llvm.rs

1//! This module defines the `Llvm` struct, which represents the `[llvm]` table
2//! in the `bootstrap.toml` configuration file.
3
4use serde::{Deserialize, Deserializer};
5
6use crate::core::config::toml::{Merge, ReplaceOpt, TomlConfig};
7use crate::core::config::{StringOrBool, set};
8use crate::{Config, HashMap, HashSet, PathBuf, define_config, exit};
9
10define_config! {
11    /// TOML representation of how the LLVM build is configured.
12    struct Llvm {
13        optimize: Option<bool> = "optimize",
14        thin_lto: Option<bool> = "thin-lto",
15        release_debuginfo: Option<bool> = "release-debuginfo",
16        assertions: Option<bool> = "assertions",
17        tests: Option<bool> = "tests",
18        enzyme: Option<bool> = "enzyme",
19        plugins: Option<bool> = "plugins",
20        static_libstdcpp: Option<bool> = "static-libstdcpp",
21        libzstd: Option<bool> = "libzstd",
22        ninja: Option<bool> = "ninja",
23        targets: Option<String> = "targets",
24        experimental_targets: Option<String> = "experimental-targets",
25        link_jobs: Option<u32> = "link-jobs",
26        link_shared: Option<bool> = "link-shared",
27        version_suffix: Option<String> = "version-suffix",
28        clang_cl: Option<String> = "clang-cl",
29        cflags: Option<String> = "cflags",
30        cxxflags: Option<String> = "cxxflags",
31        ldflags: Option<String> = "ldflags",
32        use_libcxx: Option<bool> = "use-libcxx",
33        use_linker: Option<String> = "use-linker",
34        allow_old_toolchain: Option<bool> = "allow-old-toolchain",
35        offload: Option<bool> = "offload",
36        polly: Option<bool> = "polly",
37        clang: Option<bool> = "clang",
38        enable_warnings: Option<bool> = "enable-warnings",
39        download_ci_llvm: Option<StringOrBool> = "download-ci-llvm",
40        build_config: Option<HashMap<String, String>> = "build-config",
41    }
42}
43
44/// Compares the current `Llvm` options against those in the CI LLVM builder and detects any incompatible options.
45/// It does this by destructuring the `Llvm` instance to make sure every `Llvm` field is covered and not missing.
46#[cfg(not(test))]
47pub fn check_incompatible_options_for_ci_llvm(
48    current_config_toml: TomlConfig,
49    ci_config_toml: TomlConfig,
50) -> Result<(), String> {
51    macro_rules! err {
52        ($current:expr, $expected:expr) => {
53            if let Some(current) = &$current {
54                if Some(current) != $expected.as_ref() {
55                    return Err(format!(
56                        "ERROR: Setting `llvm.{}` is incompatible with `llvm.download-ci-llvm`. \
57                        Current value: {:?}, Expected value(s): {}{:?}",
58                        stringify!($expected).replace("_", "-"),
59                        $current,
60                        if $expected.is_some() { "None/" } else { "" },
61                        $expected,
62                    ));
63                };
64            };
65        };
66    }
67
68    macro_rules! warn {
69        ($current:expr, $expected:expr) => {
70            if let Some(current) = &$current {
71                if Some(current) != $expected.as_ref() {
72                    println!(
73                        "WARNING: `llvm.{}` has no effect with `llvm.download-ci-llvm`. \
74                        Current value: {:?}, Expected value(s): {}{:?}",
75                        stringify!($expected).replace("_", "-"),
76                        $current,
77                        if $expected.is_some() { "None/" } else { "" },
78                        $expected,
79                    );
80                };
81            };
82        };
83    }
84
85    let (Some(current_llvm_config), Some(ci_llvm_config)) =
86        (current_config_toml.llvm, ci_config_toml.llvm)
87    else {
88        return Ok(());
89    };
90
91    let Llvm {
92        optimize,
93        thin_lto,
94        release_debuginfo,
95        assertions: _,
96        tests: _,
97        plugins,
98        static_libstdcpp: _,
99        libzstd,
100        ninja: _,
101        targets,
102        experimental_targets,
103        link_jobs: _,
104        link_shared: _,
105        version_suffix,
106        clang_cl,
107        cflags,
108        cxxflags,
109        ldflags,
110        use_libcxx,
111        use_linker,
112        allow_old_toolchain,
113        offload,
114        polly,
115        clang,
116        enable_warnings,
117        download_ci_llvm: _,
118        build_config,
119        enzyme,
120    } = ci_llvm_config;
121
122    err!(current_llvm_config.optimize, optimize);
123    err!(current_llvm_config.thin_lto, thin_lto);
124    err!(current_llvm_config.release_debuginfo, release_debuginfo);
125    err!(current_llvm_config.libzstd, libzstd);
126    err!(current_llvm_config.targets, targets);
127    err!(current_llvm_config.experimental_targets, experimental_targets);
128    err!(current_llvm_config.clang_cl, clang_cl);
129    err!(current_llvm_config.version_suffix, version_suffix);
130    err!(current_llvm_config.cflags, cflags);
131    err!(current_llvm_config.cxxflags, cxxflags);
132    err!(current_llvm_config.ldflags, ldflags);
133    err!(current_llvm_config.use_libcxx, use_libcxx);
134    err!(current_llvm_config.use_linker, use_linker);
135    err!(current_llvm_config.allow_old_toolchain, allow_old_toolchain);
136    err!(current_llvm_config.offload, offload);
137    err!(current_llvm_config.polly, polly);
138    err!(current_llvm_config.clang, clang);
139    err!(current_llvm_config.build_config, build_config);
140    err!(current_llvm_config.plugins, plugins);
141    err!(current_llvm_config.enzyme, enzyme);
142
143    warn!(current_llvm_config.enable_warnings, enable_warnings);
144
145    Ok(())
146}
147
148impl Config {
149    pub fn apply_llvm_config(&mut self, toml_llvm: Option<Llvm>) {
150        let mut llvm_tests = None;
151        let mut llvm_enzyme = None;
152        let mut llvm_offload = None;
153        let mut llvm_plugins = None;
154
155        if let Some(llvm) = toml_llvm {
156            let Llvm {
157                optimize: optimize_toml,
158                thin_lto,
159                release_debuginfo,
160                assertions: _,
161                tests,
162                enzyme,
163                plugins,
164                static_libstdcpp,
165                libzstd,
166                ninja,
167                targets,
168                experimental_targets,
169                link_jobs,
170                link_shared,
171                version_suffix,
172                clang_cl,
173                cflags,
174                cxxflags,
175                ldflags,
176                use_libcxx,
177                use_linker,
178                allow_old_toolchain,
179                offload,
180                polly,
181                clang,
182                enable_warnings,
183                download_ci_llvm,
184                build_config,
185            } = llvm;
186
187            set(&mut self.ninja_in_file, ninja);
188            llvm_tests = tests;
189            llvm_enzyme = enzyme;
190            llvm_offload = offload;
191            llvm_plugins = plugins;
192            set(&mut self.llvm_optimize, optimize_toml);
193            set(&mut self.llvm_thin_lto, thin_lto);
194            set(&mut self.llvm_release_debuginfo, release_debuginfo);
195            set(&mut self.llvm_static_stdcpp, static_libstdcpp);
196            set(&mut self.llvm_libzstd, libzstd);
197            if let Some(v) = link_shared {
198                self.llvm_link_shared.set(Some(v));
199            }
200            self.llvm_targets.clone_from(&targets);
201            self.llvm_experimental_targets.clone_from(&experimental_targets);
202            self.llvm_link_jobs = link_jobs;
203            self.llvm_version_suffix.clone_from(&version_suffix);
204            self.llvm_clang_cl.clone_from(&clang_cl);
205
206            self.llvm_cflags.clone_from(&cflags);
207            self.llvm_cxxflags.clone_from(&cxxflags);
208            self.llvm_ldflags.clone_from(&ldflags);
209            set(&mut self.llvm_use_libcxx, use_libcxx);
210            self.llvm_use_linker.clone_from(&use_linker);
211            self.llvm_allow_old_toolchain = allow_old_toolchain.unwrap_or(false);
212            self.llvm_offload = offload.unwrap_or(false);
213            self.llvm_polly = polly.unwrap_or(false);
214            self.llvm_clang = clang.unwrap_or(false);
215            self.llvm_enable_warnings = enable_warnings.unwrap_or(false);
216            self.llvm_build_config = build_config.clone().unwrap_or(Default::default());
217
218            self.llvm_from_ci = self.parse_download_ci_llvm(download_ci_llvm, self.llvm_assertions);
219
220            if self.llvm_from_ci {
221                let warn = |option: &str| {
222                    println!(
223                        "WARNING: `{option}` will only be used on `compiler/rustc_llvm` build, not for the LLVM build."
224                    );
225                    println!(
226                        "HELP: To use `{option}` for LLVM builds, set `download-ci-llvm` option to false."
227                    );
228                };
229
230                if static_libstdcpp.is_some() {
231                    warn("static-libstdcpp");
232                }
233
234                if link_shared.is_some() {
235                    warn("link-shared");
236                }
237
238                // FIXME(#129153): instead of all the ad-hoc `download-ci-llvm` checks that follow,
239                // use the `builder-config` present in tarballs since #128822 to compare the local
240                // config to the ones used to build the LLVM artifacts on CI, and only notify users
241                // if they've chosen a different value.
242
243                if libzstd.is_some() {
244                    println!(
245                        "WARNING: when using `download-ci-llvm`, the local `llvm.libzstd` option, \
246                        like almost all `llvm.*` options, will be ignored and set by the LLVM CI \
247                        artifacts builder config."
248                    );
249                    println!(
250                        "HELP: To use `llvm.libzstd` for LLVM/LLD builds, set `download-ci-llvm` option to false."
251                    );
252                }
253            }
254
255            if !self.llvm_from_ci && self.llvm_thin_lto && link_shared.is_none() {
256                // If we're building with ThinLTO on, by default we want to link
257                // to LLVM shared, to avoid re-doing ThinLTO (which happens in
258                // the link step) with each stage.
259                self.llvm_link_shared.set(Some(true));
260            }
261        } else {
262            self.llvm_from_ci = self.parse_download_ci_llvm(None, false);
263        }
264
265        self.llvm_tests = llvm_tests.unwrap_or(false);
266        self.llvm_enzyme = llvm_enzyme.unwrap_or(false);
267        self.llvm_offload = llvm_offload.unwrap_or(false);
268        self.llvm_plugins = llvm_plugins.unwrap_or(false);
269    }
270}