1use 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 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 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#[derive(Copy, Clone, Default, Debug, PartialEq)]
88pub enum LldMode {
89 #[default]
91 Unused,
92 SelfContained,
94 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
231pub 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 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 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 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 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: _, musl_root,
454 rpath,
455 verbose_tests,
456 optimize_tests,
457 codegen_tests,
458 omit_git_hash: _, 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 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 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 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 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}