bootstrap/core/config/toml/
rust.rs

1//! This module defines the `Rust` struct, which represents the `[rust]` table
2//! in the `bootstrap.toml` configuration file.
3
4use std::str::FromStr;
5
6use serde::{Deserialize, Deserializer};
7
8use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
9use crate::core::config::toml::TomlConfig;
10use crate::core::config::{
11    DebuginfoLevel, Merge, ReplaceOpt, RustcLto, StringOrBool, set, threads_from_config,
12};
13use crate::flags::Warnings;
14use crate::{BTreeSet, Config, HashSet, PathBuf, TargetSelection, define_config, exit};
15
16define_config! {
17    /// TOML representation of how the Rust build is configured.
18    struct Rust {
19        optimize: Option<RustOptimize> = "optimize",
20        debug: Option<bool> = "debug",
21        codegen_units: Option<u32> = "codegen-units",
22        codegen_units_std: Option<u32> = "codegen-units-std",
23        rustc_debug_assertions: Option<bool> = "debug-assertions",
24        randomize_layout: Option<bool> = "randomize-layout",
25        std_debug_assertions: Option<bool> = "debug-assertions-std",
26        tools_debug_assertions: Option<bool> = "debug-assertions-tools",
27        overflow_checks: Option<bool> = "overflow-checks",
28        overflow_checks_std: Option<bool> = "overflow-checks-std",
29        debug_logging: Option<bool> = "debug-logging",
30        debuginfo_level: Option<DebuginfoLevel> = "debuginfo-level",
31        debuginfo_level_rustc: Option<DebuginfoLevel> = "debuginfo-level-rustc",
32        debuginfo_level_std: Option<DebuginfoLevel> = "debuginfo-level-std",
33        debuginfo_level_tools: Option<DebuginfoLevel> = "debuginfo-level-tools",
34        debuginfo_level_tests: Option<DebuginfoLevel> = "debuginfo-level-tests",
35        backtrace: Option<bool> = "backtrace",
36        incremental: Option<bool> = "incremental",
37        default_linker: Option<String> = "default-linker",
38        channel: Option<String> = "channel",
39        musl_root: Option<String> = "musl-root",
40        rpath: Option<bool> = "rpath",
41        strip: Option<bool> = "strip",
42        frame_pointers: Option<bool> = "frame-pointers",
43        stack_protector: Option<String> = "stack-protector",
44        verbose_tests: Option<bool> = "verbose-tests",
45        optimize_tests: Option<bool> = "optimize-tests",
46        codegen_tests: Option<bool> = "codegen-tests",
47        omit_git_hash: Option<bool> = "omit-git-hash",
48        dist_src: Option<bool> = "dist-src",
49        save_toolstates: Option<String> = "save-toolstates",
50        codegen_backends: Option<Vec<String>> = "codegen-backends",
51        llvm_bitcode_linker: Option<bool> = "llvm-bitcode-linker",
52        lld: Option<bool> = "lld",
53        lld_mode: Option<LldMode> = "use-lld",
54        llvm_tools: Option<bool> = "llvm-tools",
55        deny_warnings: Option<bool> = "deny-warnings",
56        backtrace_on_ice: Option<bool> = "backtrace-on-ice",
57        verify_llvm_ir: Option<bool> = "verify-llvm-ir",
58        thin_lto_import_instr_limit: Option<u32> = "thin-lto-import-instr-limit",
59        remap_debuginfo: Option<bool> = "remap-debuginfo",
60        jemalloc: Option<bool> = "jemalloc",
61        test_compare_mode: Option<bool> = "test-compare-mode",
62        llvm_libunwind: Option<String> = "llvm-libunwind",
63        control_flow_guard: Option<bool> = "control-flow-guard",
64        ehcont_guard: Option<bool> = "ehcont-guard",
65        new_symbol_mangling: Option<bool> = "new-symbol-mangling",
66        profile_generate: Option<String> = "profile-generate",
67        profile_use: Option<String> = "profile-use",
68        // ignored; this is set from an env var set by bootstrap.py
69        download_rustc: Option<StringOrBool> = "download-rustc",
70        lto: Option<String> = "lto",
71        validate_mir_opts: Option<u32> = "validate-mir-opts",
72        std_features: Option<BTreeSet<String>> = "std-features",
73    }
74}
75
76/// LLD in bootstrap works like this:
77/// - Self-contained lld: use `rust-lld` from the compiler's sysroot
78/// - External: use an external `lld` binary
79///
80/// It is configured depending on the target:
81/// 1) Everything except MSVC
82/// - Self-contained: `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker`
83/// - External: `-Clinker-flavor=gnu-lld-cc`
84/// 2) MSVC
85/// - Self-contained: `-Clinker=<path to rust-lld>`
86/// - External: `-Clinker=lld`
87#[derive(Copy, Clone, Default, Debug, PartialEq)]
88pub enum LldMode {
89    /// Do not use LLD
90    #[default]
91    Unused,
92    /// Use `rust-lld` from the compiler's sysroot
93    SelfContained,
94    /// Use an externally provided `lld` binary.
95    /// Note that the linker name cannot be overridden, the binary has to be named `lld` and it has
96    /// to be in $PATH.
97    External,
98}
99
100impl LldMode {
101    pub fn is_used(&self) -> bool {
102        match self {
103            LldMode::SelfContained | LldMode::External => true,
104            LldMode::Unused => false,
105        }
106    }
107}
108
109impl<'de> Deserialize<'de> for LldMode {
110    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111    where
112        D: Deserializer<'de>,
113    {
114        struct LldModeVisitor;
115
116        impl serde::de::Visitor<'_> for LldModeVisitor {
117            type Value = LldMode;
118
119            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120                formatter.write_str("one of true, 'self-contained' or 'external'")
121            }
122
123            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
124            where
125                E: serde::de::Error,
126            {
127                Ok(if v { LldMode::External } else { LldMode::Unused })
128            }
129
130            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
131            where
132                E: serde::de::Error,
133            {
134                match v {
135                    "external" => Ok(LldMode::External),
136                    "self-contained" => Ok(LldMode::SelfContained),
137                    _ => Err(E::custom(format!("unknown mode {v}"))),
138                }
139            }
140        }
141
142        deserializer.deserialize_any(LldModeVisitor)
143    }
144}
145
146#[derive(Clone, Debug, PartialEq, Eq)]
147pub enum RustOptimize {
148    String(String),
149    Int(u8),
150    Bool(bool),
151}
152
153impl Default for RustOptimize {
154    fn default() -> RustOptimize {
155        RustOptimize::Bool(false)
156    }
157}
158
159impl<'de> Deserialize<'de> for RustOptimize {
160    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161    where
162        D: Deserializer<'de>,
163    {
164        deserializer.deserialize_any(OptimizeVisitor)
165    }
166}
167
168struct OptimizeVisitor;
169
170impl serde::de::Visitor<'_> for OptimizeVisitor {
171    type Value = RustOptimize;
172
173    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#)
175    }
176
177    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
178    where
179        E: serde::de::Error,
180    {
181        if matches!(value, "s" | "z") {
182            Ok(RustOptimize::String(value.to_string()))
183        } else {
184            Err(serde::de::Error::custom(format_optimize_error_msg(value)))
185        }
186    }
187
188    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
189    where
190        E: serde::de::Error,
191    {
192        if matches!(value, 0..=3) {
193            Ok(RustOptimize::Int(value as u8))
194        } else {
195            Err(serde::de::Error::custom(format_optimize_error_msg(value)))
196        }
197    }
198
199    fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
200    where
201        E: serde::de::Error,
202    {
203        Ok(RustOptimize::Bool(value))
204    }
205}
206
207fn format_optimize_error_msg(v: impl std::fmt::Display) -> String {
208    format!(
209        r#"unrecognized option for rust optimize: "{v}", expected one of 0, 1, 2, 3, "s", "z", true, false"#
210    )
211}
212
213impl RustOptimize {
214    pub(crate) fn is_release(&self) -> bool {
215        match &self {
216            RustOptimize::Bool(true) | RustOptimize::String(_) => true,
217            RustOptimize::Int(i) => *i > 0,
218            RustOptimize::Bool(false) => false,
219        }
220    }
221
222    pub(crate) fn get_opt_level(&self) -> Option<String> {
223        match &self {
224            RustOptimize::String(s) => Some(s.clone()),
225            RustOptimize::Int(i) => Some(i.to_string()),
226            RustOptimize::Bool(_) => None,
227        }
228    }
229}
230
231/// Compares the current Rust options against those in the CI rustc builder and detects any incompatible options.
232/// It does this by destructuring the `Rust` instance to make sure every `Rust` field is covered and not missing.
233pub fn check_incompatible_options_for_ci_rustc(
234    host: TargetSelection,
235    current_config_toml: TomlConfig,
236    ci_config_toml: TomlConfig,
237) -> Result<(), String> {
238    macro_rules! err {
239        ($current:expr, $expected:expr, $config_section:expr) => {
240            if let Some(current) = &$current {
241                if Some(current) != $expected.as_ref() {
242                    return Err(format!(
243                        "ERROR: Setting `{}` is incompatible with `rust.download-rustc`. \
244                        Current value: {:?}, Expected value(s): {}{:?}",
245                        format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
246                        $current,
247                        if $expected.is_some() { "None/" } else { "" },
248                        $expected,
249                    ));
250                };
251            };
252        };
253    }
254
255    macro_rules! warn {
256        ($current:expr, $expected:expr, $config_section:expr) => {
257            if let Some(current) = &$current {
258                if Some(current) != $expected.as_ref() {
259                    println!(
260                        "WARNING: `{}` has no effect with `rust.download-rustc`. \
261                        Current value: {:?}, Expected value(s): {}{:?}",
262                        format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
263                        $current,
264                        if $expected.is_some() { "None/" } else { "" },
265                        $expected,
266                    );
267                };
268            };
269        };
270    }
271
272    let current_profiler = current_config_toml.build.as_ref().and_then(|b| b.profiler);
273    let profiler = ci_config_toml.build.as_ref().and_then(|b| b.profiler);
274    err!(current_profiler, profiler, "build");
275
276    let current_optimized_compiler_builtins =
277        current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
278    let optimized_compiler_builtins =
279        ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
280    err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build");
281
282    // We always build the in-tree compiler on cross targets, so we only care
283    // about the host target here.
284    let host_str = host.to_string();
285    if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str))
286        && current_cfg.profiler.is_some()
287    {
288        let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str));
289        let ci_cfg = ci_target_toml.ok_or(format!(
290            "Target specific config for '{host_str}' is not present for CI-rustc"
291        ))?;
292
293        let profiler = &ci_cfg.profiler;
294        err!(current_cfg.profiler, profiler, "build");
295
296        let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins;
297        err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build");
298    }
299
300    let (Some(current_rust_config), Some(ci_rust_config)) =
301        (current_config_toml.rust, ci_config_toml.rust)
302    else {
303        return Ok(());
304    };
305
306    let Rust {
307        // Following options are the CI rustc incompatible ones.
308        optimize,
309        randomize_layout,
310        debug_logging,
311        debuginfo_level_rustc,
312        llvm_tools,
313        llvm_bitcode_linker,
314        lto,
315        stack_protector,
316        strip,
317        lld_mode,
318        jemalloc,
319        rpath,
320        channel,
321        default_linker,
322        std_features,
323
324        // Rest of the options can simply be ignored.
325        incremental: _,
326        debug: _,
327        codegen_units: _,
328        codegen_units_std: _,
329        rustc_debug_assertions: _,
330        std_debug_assertions: _,
331        tools_debug_assertions: _,
332        overflow_checks: _,
333        overflow_checks_std: _,
334        debuginfo_level: _,
335        debuginfo_level_std: _,
336        debuginfo_level_tools: _,
337        debuginfo_level_tests: _,
338        backtrace: _,
339        musl_root: _,
340        verbose_tests: _,
341        optimize_tests: _,
342        codegen_tests: _,
343        omit_git_hash: _,
344        dist_src: _,
345        save_toolstates: _,
346        codegen_backends: _,
347        lld: _,
348        deny_warnings: _,
349        backtrace_on_ice: _,
350        verify_llvm_ir: _,
351        thin_lto_import_instr_limit: _,
352        remap_debuginfo: _,
353        test_compare_mode: _,
354        llvm_libunwind: _,
355        control_flow_guard: _,
356        ehcont_guard: _,
357        new_symbol_mangling: _,
358        profile_generate: _,
359        profile_use: _,
360        download_rustc: _,
361        validate_mir_opts: _,
362        frame_pointers: _,
363    } = ci_rust_config;
364
365    // There are two kinds of checks for CI rustc incompatible options:
366    //    1. Checking an option that may change the compiler behaviour/output.
367    //    2. Checking an option that have no effect on the compiler behaviour/output.
368    //
369    // If the option belongs to the first category, we call `err` macro for a hard error;
370    // otherwise, we just print a warning with `warn` macro.
371
372    err!(current_rust_config.optimize, optimize, "rust");
373    err!(current_rust_config.randomize_layout, randomize_layout, "rust");
374    err!(current_rust_config.debug_logging, debug_logging, "rust");
375    err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust");
376    err!(current_rust_config.rpath, rpath, "rust");
377    err!(current_rust_config.strip, strip, "rust");
378    err!(current_rust_config.lld_mode, lld_mode, "rust");
379    err!(current_rust_config.llvm_tools, llvm_tools, "rust");
380    err!(current_rust_config.llvm_bitcode_linker, llvm_bitcode_linker, "rust");
381    err!(current_rust_config.jemalloc, jemalloc, "rust");
382    err!(current_rust_config.default_linker, default_linker, "rust");
383    err!(current_rust_config.stack_protector, stack_protector, "rust");
384    err!(current_rust_config.lto, lto, "rust");
385    err!(current_rust_config.std_features, std_features, "rust");
386
387    warn!(current_rust_config.channel, channel, "rust");
388
389    Ok(())
390}
391
392pub(crate) const VALID_CODEGEN_BACKENDS: &[&str] = &["llvm", "cranelift", "gcc"];
393
394pub(crate) fn validate_codegen_backends(backends: Vec<String>, section: &str) -> Vec<String> {
395    for backend in &backends {
396        if let Some(stripped) = backend.strip_prefix(CODEGEN_BACKEND_PREFIX) {
397            panic!(
398                "Invalid value '{backend}' for '{section}.codegen-backends'. \
399                Codegen backends are defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
400                Please, use '{stripped}' instead."
401            )
402        }
403        if !VALID_CODEGEN_BACKENDS.contains(&backend.as_str()) {
404            println!(
405                "HELP: '{backend}' for '{section}.codegen-backends' might fail. \
406                List of known good values: {VALID_CODEGEN_BACKENDS:?}"
407            );
408        }
409    }
410    backends
411}
412
413impl Config {
414    pub fn apply_rust_config(&mut self, toml_rust: Option<Rust>, warnings: Warnings) {
415        let mut debug = None;
416        let mut rustc_debug_assertions = None;
417        let mut std_debug_assertions = None;
418        let mut tools_debug_assertions = None;
419        let mut overflow_checks = None;
420        let mut overflow_checks_std = None;
421        let mut debug_logging = None;
422        let mut debuginfo_level = None;
423        let mut debuginfo_level_rustc = None;
424        let mut debuginfo_level_std = None;
425        let mut debuginfo_level_tools = None;
426        let mut debuginfo_level_tests = None;
427        let mut optimize = None;
428        let mut lld_enabled = None;
429        let mut std_features = None;
430
431        if let Some(rust) = toml_rust {
432            let Rust {
433                optimize: optimize_toml,
434                debug: debug_toml,
435                codegen_units,
436                codegen_units_std,
437                rustc_debug_assertions: rustc_debug_assertions_toml,
438                std_debug_assertions: std_debug_assertions_toml,
439                tools_debug_assertions: tools_debug_assertions_toml,
440                overflow_checks: overflow_checks_toml,
441                overflow_checks_std: overflow_checks_std_toml,
442                debug_logging: debug_logging_toml,
443                debuginfo_level: debuginfo_level_toml,
444                debuginfo_level_rustc: debuginfo_level_rustc_toml,
445                debuginfo_level_std: debuginfo_level_std_toml,
446                debuginfo_level_tools: debuginfo_level_tools_toml,
447                debuginfo_level_tests: debuginfo_level_tests_toml,
448                backtrace,
449                incremental,
450                randomize_layout,
451                default_linker,
452                channel: _, // already handled above
453                musl_root,
454                rpath,
455                verbose_tests,
456                optimize_tests,
457                codegen_tests,
458                omit_git_hash: _, // already handled above
459                dist_src,
460                save_toolstates,
461                codegen_backends,
462                lld: lld_enabled_toml,
463                llvm_tools,
464                llvm_bitcode_linker,
465                deny_warnings,
466                backtrace_on_ice,
467                verify_llvm_ir,
468                thin_lto_import_instr_limit,
469                remap_debuginfo,
470                jemalloc,
471                test_compare_mode,
472                llvm_libunwind,
473                control_flow_guard,
474                ehcont_guard,
475                new_symbol_mangling,
476                profile_generate,
477                profile_use,
478                download_rustc,
479                lto,
480                validate_mir_opts,
481                frame_pointers,
482                stack_protector,
483                strip,
484                lld_mode,
485                std_features: std_features_toml,
486            } = rust;
487
488            // FIXME(#133381): alt rustc builds currently do *not* have rustc debug assertions
489            // enabled. We should not download a CI alt rustc if we need rustc to have debug
490            // assertions (e.g. for crashes test suite). This can be changed once something like
491            // [Enable debug assertions on alt
492            // builds](https://github.com/rust-lang/rust/pull/131077) lands.
493            //
494            // Note that `rust.debug = true` currently implies `rust.debug-assertions = true`!
495            //
496            // This relies also on the fact that the global default for `download-rustc` will be
497            // `false` if it's not explicitly set.
498            let debug_assertions_requested = matches!(rustc_debug_assertions_toml, Some(true))
499                || (matches!(debug_toml, Some(true))
500                    && !matches!(rustc_debug_assertions_toml, Some(false)));
501
502            if debug_assertions_requested
503                && let Some(ref opt) = download_rustc
504                && opt.is_string_or_true()
505            {
506                eprintln!(
507                    "WARN: currently no CI rustc builds have rustc debug assertions \
508                            enabled. Please either set `rust.debug-assertions` to `false` if you \
509                            want to use download CI rustc or set `rust.download-rustc` to `false`."
510                );
511            }
512
513            self.download_rustc_commit = self.download_ci_rustc_commit(
514                download_rustc,
515                debug_assertions_requested,
516                self.llvm_assertions,
517            );
518
519            debug = debug_toml;
520            rustc_debug_assertions = rustc_debug_assertions_toml;
521            std_debug_assertions = std_debug_assertions_toml;
522            tools_debug_assertions = tools_debug_assertions_toml;
523            overflow_checks = overflow_checks_toml;
524            overflow_checks_std = overflow_checks_std_toml;
525            debug_logging = debug_logging_toml;
526            debuginfo_level = debuginfo_level_toml;
527            debuginfo_level_rustc = debuginfo_level_rustc_toml;
528            debuginfo_level_std = debuginfo_level_std_toml;
529            debuginfo_level_tools = debuginfo_level_tools_toml;
530            debuginfo_level_tests = debuginfo_level_tests_toml;
531            lld_enabled = lld_enabled_toml;
532            std_features = std_features_toml;
533
534            if optimize_toml.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) {
535                eprintln!(
536                    "WARNING: setting `optimize` to `false` is known to cause errors and \
537                    should be considered unsupported. Refer to `bootstrap.example.toml` \
538                    for more details."
539                );
540            }
541
542            optimize = optimize_toml;
543            self.rust_new_symbol_mangling = new_symbol_mangling;
544            set(&mut self.rust_optimize_tests, optimize_tests);
545            set(&mut self.codegen_tests, codegen_tests);
546            set(&mut self.rust_rpath, rpath);
547            set(&mut self.rust_strip, strip);
548            set(&mut self.rust_frame_pointers, frame_pointers);
549            self.rust_stack_protector = stack_protector;
550            set(&mut self.jemalloc, jemalloc);
551            set(&mut self.test_compare_mode, test_compare_mode);
552            set(&mut self.backtrace, backtrace);
553            set(&mut self.rust_dist_src, dist_src);
554            set(&mut self.verbose_tests, verbose_tests);
555            // in the case "false" is set explicitly, do not overwrite the command line args
556            if let Some(true) = incremental {
557                self.incremental = true;
558            }
559            set(&mut self.lld_mode, lld_mode);
560            set(&mut self.llvm_bitcode_linker_enabled, llvm_bitcode_linker);
561
562            self.rust_randomize_layout = randomize_layout.unwrap_or_default();
563            self.llvm_tools_enabled = llvm_tools.unwrap_or(true);
564
565            self.llvm_enzyme = self.channel == "dev" || self.channel == "nightly";
566            self.rustc_default_linker = default_linker;
567            self.musl_root = musl_root.map(PathBuf::from);
568            self.save_toolstates = save_toolstates.map(PathBuf::from);
569            set(
570                &mut self.deny_warnings,
571                match warnings {
572                    Warnings::Deny => Some(true),
573                    Warnings::Warn => Some(false),
574                    Warnings::Default => deny_warnings,
575                },
576            );
577            set(&mut self.backtrace_on_ice, backtrace_on_ice);
578            set(&mut self.rust_verify_llvm_ir, verify_llvm_ir);
579            self.rust_thin_lto_import_instr_limit = thin_lto_import_instr_limit;
580            set(&mut self.rust_remap_debuginfo, remap_debuginfo);
581            set(&mut self.control_flow_guard, control_flow_guard);
582            set(&mut self.ehcont_guard, ehcont_guard);
583            self.llvm_libunwind_default =
584                llvm_libunwind.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
585            set(
586                &mut self.rust_codegen_backends,
587                codegen_backends.map(|backends| validate_codegen_backends(backends, "rust")),
588            );
589
590            self.rust_codegen_units = codegen_units.map(threads_from_config);
591            self.rust_codegen_units_std = codegen_units_std.map(threads_from_config);
592
593            if self.rust_profile_use.is_none() {
594                self.rust_profile_use = profile_use;
595            }
596
597            if self.rust_profile_generate.is_none() {
598                self.rust_profile_generate = profile_generate;
599            }
600
601            self.rust_lto =
602                lto.as_deref().map(|value| RustcLto::from_str(value).unwrap()).unwrap_or_default();
603            self.rust_validate_mir_opts = validate_mir_opts;
604        }
605
606        self.rust_optimize = optimize.unwrap_or(RustOptimize::Bool(true));
607
608        // We make `x86_64-unknown-linux-gnu` use the self-contained linker by default, so we will
609        // build our internal lld and use it as the default linker, by setting the `rust.lld` config
610        // to true by default:
611        // - on the `x86_64-unknown-linux-gnu` target
612        // - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that
613        //   we're also able to build the corresponding lld
614        // - or when using an external llvm that's downloaded from CI, which also contains our prebuilt
615        //   lld
616        // - otherwise, we'd be using an external llvm, and lld would not necessarily available and
617        //   thus, disabled
618        // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g.
619        //   when the config sets `rust.lld = false`
620        if self.host_target.triple == "x86_64-unknown-linux-gnu" && self.hosts == [self.host_target]
621        {
622            let no_llvm_config = self
623                .target_config
624                .get(&self.host_target)
625                .is_some_and(|target_config| target_config.llvm_config.is_none());
626            let enable_lld = self.llvm_from_ci || no_llvm_config;
627            // Prefer the config setting in case an explicit opt-out is needed.
628            self.lld_enabled = lld_enabled.unwrap_or(enable_lld);
629        } else {
630            set(&mut self.lld_enabled, lld_enabled);
631        }
632
633        let default_std_features = BTreeSet::from([String::from("panic-unwind")]);
634        self.rust_std_features = std_features.unwrap_or(default_std_features);
635
636        let default = debug == Some(true);
637        self.rustc_debug_assertions = rustc_debug_assertions.unwrap_or(default);
638        self.std_debug_assertions = std_debug_assertions.unwrap_or(self.rustc_debug_assertions);
639        self.tools_debug_assertions = tools_debug_assertions.unwrap_or(self.rustc_debug_assertions);
640        self.rust_overflow_checks = overflow_checks.unwrap_or(default);
641        self.rust_overflow_checks_std = overflow_checks_std.unwrap_or(self.rust_overflow_checks);
642
643        self.rust_debug_logging = debug_logging.unwrap_or(self.rustc_debug_assertions);
644
645        let with_defaults = |debuginfo_level_specific: Option<_>| {
646            debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) {
647                DebuginfoLevel::Limited
648            } else {
649                DebuginfoLevel::None
650            })
651        };
652        self.rust_debuginfo_level_rustc = with_defaults(debuginfo_level_rustc);
653        self.rust_debuginfo_level_std = with_defaults(debuginfo_level_std);
654        self.rust_debuginfo_level_tools = with_defaults(debuginfo_level_tools);
655        self.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(DebuginfoLevel::None);
656    }
657}