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