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