1use std::cell::{Cell, RefCell};
7use std::collections::{BTreeSet, HashMap, HashSet};
8use std::fmt::{self, Display};
9use std::hash::Hash;
10use std::io::IsTerminal;
11use std::path::{Path, PathBuf, absolute};
12use std::process::Command;
13use std::str::FromStr;
14use std::sync::{Arc, Mutex, OnceLock};
15use std::{cmp, env, fs};
16
17use build_helper::ci::CiEnv;
18use build_helper::exit;
19use build_helper::git::{GitConfig, PathFreshness, check_path_modifications, output_result};
20use serde::{Deserialize, Deserializer};
21use serde_derive::Deserialize;
22#[cfg(feature = "tracing")]
23use tracing::{instrument, span};
24
25use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
26use crate::core::build_steps::llvm;
27use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS;
28pub use crate::core::config::flags::Subcommand;
29use crate::core::config::flags::{Color, Flags, Warnings};
30use crate::core::download::is_download_ci_available;
31use crate::utils::cache::{INTERNER, Interned};
32use crate::utils::channel::{self, GitInfo};
33use crate::utils::helpers::{self, exe, output, t};
34
35#[rustfmt::skip] pub(crate) const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[
48 ":!src/tools",
49 ":!src/librustdoc",
50 ":!src/rustdoc-json-types",
51 ":!tests",
52 ":!triagebot.toml",
53];
54
55macro_rules! check_ci_llvm {
56 ($name:expr) => {
57 assert!(
58 $name.is_none(),
59 "setting {} is incompatible with download-ci-llvm.",
60 stringify!($name).replace("_", "-")
61 );
62 };
63}
64
65pub(crate) const BUILDER_CONFIG_FILENAME: &str = "builder-config";
72
73#[derive(Clone, Default)]
74pub enum DryRun {
75 #[default]
77 Disabled,
78 SelfCheck,
80 UserSelected,
82}
83
84#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
85pub enum DebuginfoLevel {
86 #[default]
87 None,
88 LineDirectivesOnly,
89 LineTablesOnly,
90 Limited,
91 Full,
92}
93
94impl<'de> Deserialize<'de> for DebuginfoLevel {
97 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98 where
99 D: Deserializer<'de>,
100 {
101 use serde::de::Error;
102
103 Ok(match Deserialize::deserialize(deserializer)? {
104 StringOrInt::String(s) if s == "none" => DebuginfoLevel::None,
105 StringOrInt::Int(0) => DebuginfoLevel::None,
106 StringOrInt::String(s) if s == "line-directives-only" => {
107 DebuginfoLevel::LineDirectivesOnly
108 }
109 StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly,
110 StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited,
111 StringOrInt::Int(1) => DebuginfoLevel::Limited,
112 StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full,
113 StringOrInt::Int(2) => DebuginfoLevel::Full,
114 StringOrInt::Int(n) => {
115 let other = serde::de::Unexpected::Signed(n);
116 return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
117 }
118 StringOrInt::String(s) => {
119 let other = serde::de::Unexpected::Str(&s);
120 return Err(D::Error::invalid_value(
121 other,
122 &"expected none, line-tables-only, limited, or full",
123 ));
124 }
125 })
126 }
127}
128
129impl Display for DebuginfoLevel {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 use DebuginfoLevel::*;
133 f.write_str(match self {
134 None => "0",
135 LineDirectivesOnly => "line-directives-only",
136 LineTablesOnly => "line-tables-only",
137 Limited => "1",
138 Full => "2",
139 })
140 }
141}
142
143#[derive(Copy, Clone, Default, Debug, PartialEq)]
155pub enum LldMode {
156 #[default]
158 Unused,
159 SelfContained,
161 External,
165}
166
167impl LldMode {
168 pub fn is_used(&self) -> bool {
169 match self {
170 LldMode::SelfContained | LldMode::External => true,
171 LldMode::Unused => false,
172 }
173 }
174}
175
176#[derive(Default, Clone)]
178pub enum GccCiMode {
179 #[default]
181 BuildLocally,
182 DownloadFromCi,
185}
186
187#[derive(Default, Clone)]
196pub struct Config {
197 pub change_id: Option<ChangeId>,
198 pub bypass_bootstrap_lock: bool,
199 pub ccache: Option<String>,
200 pub ninja_in_file: bool,
202 pub verbose: usize,
203 pub submodules: Option<bool>,
204 pub compiler_docs: bool,
205 pub library_docs_private_items: bool,
206 pub docs_minification: bool,
207 pub docs: bool,
208 pub locked_deps: bool,
209 pub vendor: bool,
210 pub target_config: HashMap<TargetSelection, Target>,
211 pub full_bootstrap: bool,
212 pub bootstrap_cache_path: Option<PathBuf>,
213 pub extended: bool,
214 pub tools: Option<HashSet<String>>,
215 pub sanitizers: bool,
216 pub profiler: bool,
217 pub omit_git_hash: bool,
218 pub skip: Vec<PathBuf>,
219 pub include_default_paths: bool,
220 pub rustc_error_format: Option<String>,
221 pub json_output: bool,
222 pub test_compare_mode: bool,
223 pub color: Color,
224 pub patch_binaries_for_nix: Option<bool>,
225 pub stage0_metadata: build_helper::stage0_parser::Stage0,
226 pub android_ndk: Option<PathBuf>,
227 pub optimized_compiler_builtins: bool,
229
230 pub stdout_is_tty: bool,
231 pub stderr_is_tty: bool,
232
233 pub on_fail: Option<String>,
234 pub explicit_stage_from_cli: bool,
235 pub explicit_stage_from_config: bool,
236 pub stage: u32,
237 pub keep_stage: Vec<u32>,
238 pub keep_stage_std: Vec<u32>,
239 pub src: PathBuf,
240 pub config: Option<PathBuf>,
242 pub jobs: Option<u32>,
243 pub cmd: Subcommand,
244 pub incremental: bool,
245 pub dry_run: DryRun,
246 pub dump_bootstrap_shims: bool,
247 pub free_args: Vec<String>,
250
251 #[cfg(not(test))]
253 download_rustc_commit: Option<String>,
254 #[cfg(test)]
255 pub download_rustc_commit: Option<String>,
256
257 pub deny_warnings: bool,
258 pub backtrace_on_ice: bool,
259
260 pub llvm_assertions: bool,
262 pub llvm_tests: bool,
263 pub llvm_enzyme: bool,
264 pub llvm_offload: bool,
265 pub llvm_plugins: bool,
266 pub llvm_optimize: bool,
267 pub llvm_thin_lto: bool,
268 pub llvm_release_debuginfo: bool,
269 pub llvm_static_stdcpp: bool,
270 pub llvm_libzstd: bool,
271 #[cfg(not(test))]
273 llvm_link_shared: Cell<Option<bool>>,
274 #[cfg(test)]
275 pub llvm_link_shared: Cell<Option<bool>>,
276 pub llvm_clang_cl: Option<String>,
277 pub llvm_targets: Option<String>,
278 pub llvm_experimental_targets: Option<String>,
279 pub llvm_link_jobs: Option<u32>,
280 pub llvm_version_suffix: Option<String>,
281 pub llvm_use_linker: Option<String>,
282 pub llvm_allow_old_toolchain: bool,
283 pub llvm_polly: bool,
284 pub llvm_clang: bool,
285 pub llvm_enable_warnings: bool,
286 pub llvm_from_ci: bool,
287 pub llvm_build_config: HashMap<String, String>,
288
289 pub lld_mode: LldMode,
290 pub lld_enabled: bool,
291 pub llvm_tools_enabled: bool,
292 pub llvm_bitcode_linker_enabled: bool,
293
294 pub llvm_cflags: Option<String>,
295 pub llvm_cxxflags: Option<String>,
296 pub llvm_ldflags: Option<String>,
297 pub llvm_use_libcxx: bool,
298
299 pub gcc_ci_mode: GccCiMode,
301
302 pub rust_optimize: RustOptimize,
304 pub rust_codegen_units: Option<u32>,
305 pub rust_codegen_units_std: Option<u32>,
306
307 pub rustc_debug_assertions: bool,
308 pub std_debug_assertions: bool,
309 pub tools_debug_assertions: bool,
310
311 pub rust_overflow_checks: bool,
312 pub rust_overflow_checks_std: bool,
313 pub rust_debug_logging: bool,
314 pub rust_debuginfo_level_rustc: DebuginfoLevel,
315 pub rust_debuginfo_level_std: DebuginfoLevel,
316 pub rust_debuginfo_level_tools: DebuginfoLevel,
317 pub rust_debuginfo_level_tests: DebuginfoLevel,
318 pub rust_rpath: bool,
319 pub rust_strip: bool,
320 pub rust_frame_pointers: bool,
321 pub rust_stack_protector: Option<String>,
322 pub rustc_default_linker: Option<String>,
323 pub rust_optimize_tests: bool,
324 pub rust_dist_src: bool,
325 pub rust_codegen_backends: Vec<String>,
326 pub rust_verify_llvm_ir: bool,
327 pub rust_thin_lto_import_instr_limit: Option<u32>,
328 pub rust_randomize_layout: bool,
329 pub rust_remap_debuginfo: bool,
330 pub rust_new_symbol_mangling: Option<bool>,
331 pub rust_profile_use: Option<String>,
332 pub rust_profile_generate: Option<String>,
333 pub rust_lto: RustcLto,
334 pub rust_validate_mir_opts: Option<u32>,
335 pub rust_std_features: BTreeSet<String>,
336 pub llvm_profile_use: Option<String>,
337 pub llvm_profile_generate: bool,
338 pub llvm_libunwind_default: Option<LlvmLibunwind>,
339 pub enable_bolt_settings: bool,
340
341 pub reproducible_artifacts: Vec<String>,
342
343 pub build: TargetSelection,
344 pub hosts: Vec<TargetSelection>,
345 pub targets: Vec<TargetSelection>,
346 pub local_rebuild: bool,
347 #[cfg(not(test))]
348 jemalloc: bool,
349 #[cfg(test)]
350 pub jemalloc: bool,
351 pub control_flow_guard: bool,
352 pub ehcont_guard: bool,
353
354 pub dist_sign_folder: Option<PathBuf>,
356 pub dist_upload_addr: Option<String>,
357 pub dist_compression_formats: Option<Vec<String>>,
358 pub dist_compression_profile: String,
359 pub dist_include_mingw_linker: bool,
360 pub dist_vendor: bool,
361
362 pub backtrace: bool, pub low_priority: bool,
367 pub channel: String,
368 pub description: Option<String>,
369 pub verbose_tests: bool,
370 pub save_toolstates: Option<PathBuf>,
371 pub print_step_timings: bool,
372 pub print_step_rusage: bool,
373
374 pub musl_root: Option<PathBuf>,
376 pub prefix: Option<PathBuf>,
377 pub sysconfdir: Option<PathBuf>,
378 pub datadir: Option<PathBuf>,
379 pub docdir: Option<PathBuf>,
380 pub bindir: PathBuf,
381 pub libdir: Option<PathBuf>,
382 pub mandir: Option<PathBuf>,
383 pub codegen_tests: bool,
384 pub nodejs: Option<PathBuf>,
385 pub npm: Option<PathBuf>,
386 pub gdb: Option<PathBuf>,
387 pub lldb: Option<PathBuf>,
388 pub python: Option<PathBuf>,
389 pub reuse: Option<PathBuf>,
390 pub cargo_native_static: bool,
391 pub configure_args: Vec<String>,
392 pub out: PathBuf,
393 pub rust_info: channel::GitInfo,
394
395 pub cargo_info: channel::GitInfo,
396 pub rust_analyzer_info: channel::GitInfo,
397 pub clippy_info: channel::GitInfo,
398 pub miri_info: channel::GitInfo,
399 pub rustfmt_info: channel::GitInfo,
400 pub enzyme_info: channel::GitInfo,
401 pub in_tree_llvm_info: channel::GitInfo,
402 pub in_tree_gcc_info: channel::GitInfo,
403
404 pub initial_cargo: PathBuf,
406 pub initial_rustc: PathBuf,
407 pub initial_cargo_clippy: Option<PathBuf>,
408 pub initial_sysroot: PathBuf,
409
410 #[cfg(not(test))]
411 initial_rustfmt: RefCell<RustfmtState>,
412 #[cfg(test)]
413 pub initial_rustfmt: RefCell<RustfmtState>,
414
415 pub paths: Vec<PathBuf>,
418
419 pub compiletest_diff_tool: Option<String>,
421
422 pub compiletest_use_stage0_libtest: bool,
424
425 pub is_running_on_ci: bool,
426
427 pub path_modification_cache: Arc<Mutex<HashMap<Vec<&'static str>, PathFreshness>>>,
429}
430
431#[derive(Clone, Debug, Default)]
432pub enum RustfmtState {
433 SystemToolchain(PathBuf),
434 Downloaded(PathBuf),
435 Unavailable,
436 #[default]
437 LazyEvaluated,
438}
439
440#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
441pub enum LlvmLibunwind {
442 #[default]
443 No,
444 InTree,
445 System,
446}
447
448impl FromStr for LlvmLibunwind {
449 type Err = String;
450
451 fn from_str(value: &str) -> Result<Self, Self::Err> {
452 match value {
453 "no" => Ok(Self::No),
454 "in-tree" => Ok(Self::InTree),
455 "system" => Ok(Self::System),
456 invalid => Err(format!("Invalid value '{invalid}' for rust.llvm-libunwind config.")),
457 }
458 }
459}
460
461#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
462pub enum SplitDebuginfo {
463 Packed,
464 Unpacked,
465 #[default]
466 Off,
467}
468
469impl std::str::FromStr for SplitDebuginfo {
470 type Err = ();
471
472 fn from_str(s: &str) -> Result<Self, Self::Err> {
473 match s {
474 "packed" => Ok(SplitDebuginfo::Packed),
475 "unpacked" => Ok(SplitDebuginfo::Unpacked),
476 "off" => Ok(SplitDebuginfo::Off),
477 _ => Err(()),
478 }
479 }
480}
481
482impl SplitDebuginfo {
483 fn default_for_platform(target: TargetSelection) -> Self {
486 if target.contains("apple") {
487 SplitDebuginfo::Unpacked
488 } else if target.is_windows() {
489 SplitDebuginfo::Packed
490 } else {
491 SplitDebuginfo::Off
492 }
493 }
494}
495
496#[derive(Default, Clone, PartialEq, Debug)]
498pub enum RustcLto {
499 Off,
500 #[default]
501 ThinLocal,
502 Thin,
503 Fat,
504}
505
506impl std::str::FromStr for RustcLto {
507 type Err = String;
508
509 fn from_str(s: &str) -> Result<Self, Self::Err> {
510 match s {
511 "thin-local" => Ok(RustcLto::ThinLocal),
512 "thin" => Ok(RustcLto::Thin),
513 "fat" => Ok(RustcLto::Fat),
514 "off" => Ok(RustcLto::Off),
515 _ => Err(format!("Invalid value for rustc LTO: {s}")),
516 }
517 }
518}
519
520#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
521pub struct TargetSelection {
524 pub triple: Interned<String>,
525 file: Option<Interned<String>>,
526 synthetic: bool,
527}
528
529#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
531pub struct TargetSelectionList(Vec<TargetSelection>);
532
533pub fn target_selection_list(s: &str) -> Result<TargetSelectionList, String> {
534 Ok(TargetSelectionList(
535 s.split(',').filter(|s| !s.is_empty()).map(TargetSelection::from_user).collect(),
536 ))
537}
538
539impl TargetSelection {
540 pub fn from_user(selection: &str) -> Self {
541 let path = Path::new(selection);
542
543 let (triple, file) = if path.exists() {
544 let triple = path
545 .file_stem()
546 .expect("Target specification file has no file stem")
547 .to_str()
548 .expect("Target specification file stem is not UTF-8");
549
550 (triple, Some(selection))
551 } else {
552 (selection, None)
553 };
554
555 let triple = INTERNER.intern_str(triple);
556 let file = file.map(|f| INTERNER.intern_str(f));
557
558 Self { triple, file, synthetic: false }
559 }
560
561 pub fn create_synthetic(triple: &str, file: &str) -> Self {
562 Self {
563 triple: INTERNER.intern_str(triple),
564 file: Some(INTERNER.intern_str(file)),
565 synthetic: true,
566 }
567 }
568
569 pub fn rustc_target_arg(&self) -> &str {
570 self.file.as_ref().unwrap_or(&self.triple)
571 }
572
573 pub fn contains(&self, needle: &str) -> bool {
574 self.triple.contains(needle)
575 }
576
577 pub fn starts_with(&self, needle: &str) -> bool {
578 self.triple.starts_with(needle)
579 }
580
581 pub fn ends_with(&self, needle: &str) -> bool {
582 self.triple.ends_with(needle)
583 }
584
585 pub fn is_synthetic(&self) -> bool {
587 self.synthetic
588 }
589
590 pub fn is_msvc(&self) -> bool {
591 self.contains("msvc")
592 }
593
594 pub fn is_windows(&self) -> bool {
595 self.contains("windows")
596 }
597
598 pub fn is_windows_gnu(&self) -> bool {
599 self.ends_with("windows-gnu")
600 }
601
602 pub fn is_cygwin(&self) -> bool {
603 self.is_windows() &&
604 env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin"))
606 }
607
608 pub fn needs_crt_begin_end(&self) -> bool {
609 self.contains("musl") && !self.contains("unikraft")
610 }
611
612 pub fn filepath(&self) -> Option<&Path> {
614 self.file.as_ref().map(Path::new)
615 }
616}
617
618impl fmt::Display for TargetSelection {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 write!(f, "{}", self.triple)?;
621 if let Some(file) = self.file {
622 write!(f, "({file})")?;
623 }
624 Ok(())
625 }
626}
627
628impl fmt::Debug for TargetSelection {
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 write!(f, "{self}")
631 }
632}
633
634impl PartialEq<&str> for TargetSelection {
635 fn eq(&self, other: &&str) -> bool {
636 self.triple == *other
637 }
638}
639
640impl AsRef<Path> for TargetSelection {
643 fn as_ref(&self) -> &Path {
644 self.triple.as_ref()
645 }
646}
647
648#[derive(Debug, Default, Clone, PartialEq, Eq)]
650pub struct Target {
651 pub llvm_config: Option<PathBuf>,
653 pub llvm_has_rust_patches: Option<bool>,
654 pub llvm_filecheck: Option<PathBuf>,
656 pub llvm_libunwind: Option<LlvmLibunwind>,
657 pub cc: Option<PathBuf>,
658 pub cxx: Option<PathBuf>,
659 pub ar: Option<PathBuf>,
660 pub ranlib: Option<PathBuf>,
661 pub default_linker: Option<PathBuf>,
662 pub linker: Option<PathBuf>,
663 pub split_debuginfo: Option<SplitDebuginfo>,
664 pub sanitizers: Option<bool>,
665 pub profiler: Option<StringOrBool>,
666 pub rpath: Option<bool>,
667 pub crt_static: Option<bool>,
668 pub musl_root: Option<PathBuf>,
669 pub musl_libdir: Option<PathBuf>,
670 pub wasi_root: Option<PathBuf>,
671 pub qemu_rootfs: Option<PathBuf>,
672 pub runner: Option<String>,
673 pub no_std: bool,
674 pub codegen_backends: Option<Vec<String>>,
675 pub optimized_compiler_builtins: Option<bool>,
676 pub jemalloc: Option<bool>,
677}
678
679impl Target {
680 pub fn from_triple(triple: &str) -> Self {
681 let mut target: Self = Default::default();
682 if triple.contains("-none") || triple.contains("nvptx") || triple.contains("switch") {
683 target.no_std = true;
684 }
685 if triple.contains("emscripten") {
686 target.runner = Some("node".into());
687 }
688 target
689 }
690}
691#[derive(Deserialize, Default)]
697#[serde(deny_unknown_fields, rename_all = "kebab-case")]
698pub(crate) struct TomlConfig {
699 #[serde(flatten)]
700 change_id: ChangeIdWrapper,
701 build: Option<Build>,
702 install: Option<Install>,
703 llvm: Option<Llvm>,
704 gcc: Option<Gcc>,
705 rust: Option<Rust>,
706 target: Option<HashMap<String, TomlTarget>>,
707 dist: Option<Dist>,
708 profile: Option<String>,
709 include: Option<Vec<PathBuf>>,
710}
711
712#[derive(Clone, Debug, PartialEq)]
714pub enum ChangeId {
715 Ignore,
716 Id(usize),
717}
718
719#[derive(Deserialize, Default)]
724pub(crate) struct ChangeIdWrapper {
725 #[serde(alias = "change-id", default, deserialize_with = "deserialize_change_id")]
726 pub(crate) inner: Option<ChangeId>,
727}
728
729fn deserialize_change_id<'de, D: Deserializer<'de>>(
730 deserializer: D,
731) -> Result<Option<ChangeId>, D::Error> {
732 let value = toml::Value::deserialize(deserializer)?;
733 Ok(match value {
734 toml::Value::String(s) if s == "ignore" => Some(ChangeId::Ignore),
735 toml::Value::Integer(i) => Some(ChangeId::Id(i as usize)),
736 _ => {
737 return Err(serde::de::Error::custom(
738 "expected \"ignore\" or an integer for change-id",
739 ));
740 }
741 })
742}
743
744#[derive(Copy, Clone, Debug)]
746enum ReplaceOpt {
747 IgnoreDuplicate,
749 Override,
751 ErrorOnDuplicate,
753}
754
755trait Merge {
756 fn merge(
757 &mut self,
758 parent_config_path: Option<PathBuf>,
759 included_extensions: &mut HashSet<PathBuf>,
760 other: Self,
761 replace: ReplaceOpt,
762 );
763}
764
765impl Merge for TomlConfig {
766 fn merge(
767 &mut self,
768 parent_config_path: Option<PathBuf>,
769 included_extensions: &mut HashSet<PathBuf>,
770 TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include }: Self,
771 replace: ReplaceOpt,
772 ) {
773 fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) {
774 if let Some(new) = y {
775 if let Some(original) = x {
776 original.merge(None, &mut Default::default(), new, replace);
777 } else {
778 *x = Some(new);
779 }
780 }
781 }
782
783 self.change_id.inner.merge(None, &mut Default::default(), change_id.inner, replace);
784 self.profile.merge(None, &mut Default::default(), profile, replace);
785
786 do_merge(&mut self.build, build, replace);
787 do_merge(&mut self.install, install, replace);
788 do_merge(&mut self.llvm, llvm, replace);
789 do_merge(&mut self.gcc, gcc, replace);
790 do_merge(&mut self.rust, rust, replace);
791 do_merge(&mut self.dist, dist, replace);
792
793 match (self.target.as_mut(), target) {
794 (_, None) => {}
795 (None, Some(target)) => self.target = Some(target),
796 (Some(original_target), Some(new_target)) => {
797 for (triple, new) in new_target {
798 if let Some(original) = original_target.get_mut(&triple) {
799 original.merge(None, &mut Default::default(), new, replace);
800 } else {
801 original_target.insert(triple, new);
802 }
803 }
804 }
805 }
806
807 let parent_dir = parent_config_path
808 .as_ref()
809 .and_then(|p| p.parent().map(ToOwned::to_owned))
810 .unwrap_or_default();
811
812 for include_path in include.clone().unwrap_or_default().iter().rev() {
815 let include_path = parent_dir.join(include_path);
816 let include_path = include_path.canonicalize().unwrap_or_else(|e| {
817 eprintln!("ERROR: Failed to canonicalize '{}' path: {e}", include_path.display());
818 exit!(2);
819 });
820
821 let included_toml = Config::get_toml_inner(&include_path).unwrap_or_else(|e| {
822 eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
823 exit!(2);
824 });
825
826 assert!(
827 included_extensions.insert(include_path.clone()),
828 "Cyclic inclusion detected: '{}' is being included again before its previous inclusion was fully processed.",
829 include_path.display()
830 );
831
832 self.merge(
833 Some(include_path.clone()),
834 included_extensions,
835 included_toml,
836 ReplaceOpt::IgnoreDuplicate,
839 );
840
841 included_extensions.remove(&include_path);
842 }
843 }
844}
845
846macro_rules! define_config {
848 ($(#[$attr:meta])* struct $name:ident {
849 $($field:ident: Option<$field_ty:ty> = $field_key:literal,)*
850 }) => {
851 $(#[$attr])*
852 struct $name {
853 $($field: Option<$field_ty>,)*
854 }
855
856 impl Merge for $name {
857 fn merge(
858 &mut self,
859 _parent_config_path: Option<PathBuf>,
860 _included_extensions: &mut HashSet<PathBuf>,
861 other: Self,
862 replace: ReplaceOpt
863 ) {
864 $(
865 match replace {
866 ReplaceOpt::IgnoreDuplicate => {
867 if self.$field.is_none() {
868 self.$field = other.$field;
869 }
870 },
871 ReplaceOpt::Override => {
872 if other.$field.is_some() {
873 self.$field = other.$field;
874 }
875 }
876 ReplaceOpt::ErrorOnDuplicate => {
877 if other.$field.is_some() {
878 if self.$field.is_some() {
879 if cfg!(test) {
880 panic!("overriding existing option")
881 } else {
882 eprintln!("overriding existing option: `{}`", stringify!($field));
883 exit!(2);
884 }
885 } else {
886 self.$field = other.$field;
887 }
888 }
889 }
890 }
891 )*
892 }
893 }
894
895 impl<'de> Deserialize<'de> for $name {
899 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
900 where
901 D: Deserializer<'de>,
902 {
903 struct Field;
904 impl<'de> serde::de::Visitor<'de> for Field {
905 type Value = $name;
906 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
907 f.write_str(concat!("struct ", stringify!($name)))
908 }
909
910 #[inline]
911 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
912 where
913 A: serde::de::MapAccess<'de>,
914 {
915 $(let mut $field: Option<$field_ty> = None;)*
916 while let Some(key) =
917 match serde::de::MapAccess::next_key::<String>(&mut map) {
918 Ok(val) => val,
919 Err(err) => {
920 return Err(err);
921 }
922 }
923 {
924 match &*key {
925 $($field_key => {
926 if $field.is_some() {
927 return Err(<A::Error as serde::de::Error>::duplicate_field(
928 $field_key,
929 ));
930 }
931 $field = match serde::de::MapAccess::next_value::<$field_ty>(
932 &mut map,
933 ) {
934 Ok(val) => Some(val),
935 Err(err) => {
936 return Err(err);
937 }
938 };
939 })*
940 key => {
941 return Err(serde::de::Error::unknown_field(key, FIELDS));
942 }
943 }
944 }
945 Ok($name { $($field),* })
946 }
947 }
948 const FIELDS: &'static [&'static str] = &[
949 $($field_key,)*
950 ];
951 Deserializer::deserialize_struct(
952 deserializer,
953 stringify!($name),
954 FIELDS,
955 Field,
956 )
957 }
958 }
959 }
960}
961
962impl<T> Merge for Option<T> {
963 fn merge(
964 &mut self,
965 _parent_config_path: Option<PathBuf>,
966 _included_extensions: &mut HashSet<PathBuf>,
967 other: Self,
968 replace: ReplaceOpt,
969 ) {
970 match replace {
971 ReplaceOpt::IgnoreDuplicate => {
972 if self.is_none() {
973 *self = other;
974 }
975 }
976 ReplaceOpt::Override => {
977 if other.is_some() {
978 *self = other;
979 }
980 }
981 ReplaceOpt::ErrorOnDuplicate => {
982 if other.is_some() {
983 if self.is_some() {
984 if cfg!(test) {
985 panic!("overriding existing option")
986 } else {
987 eprintln!("overriding existing option");
988 exit!(2);
989 }
990 } else {
991 *self = other;
992 }
993 }
994 }
995 }
996 }
997}
998
999define_config! {
1000 #[derive(Default)]
1002 struct Build {
1003 build: Option<String> = "build",
1004 description: Option<String> = "description",
1005 host: Option<Vec<String>> = "host",
1006 target: Option<Vec<String>> = "target",
1007 build_dir: Option<String> = "build-dir",
1008 cargo: Option<PathBuf> = "cargo",
1009 rustc: Option<PathBuf> = "rustc",
1010 rustfmt: Option<PathBuf> = "rustfmt",
1011 cargo_clippy: Option<PathBuf> = "cargo-clippy",
1012 docs: Option<bool> = "docs",
1013 compiler_docs: Option<bool> = "compiler-docs",
1014 library_docs_private_items: Option<bool> = "library-docs-private-items",
1015 docs_minification: Option<bool> = "docs-minification",
1016 submodules: Option<bool> = "submodules",
1017 gdb: Option<String> = "gdb",
1018 lldb: Option<String> = "lldb",
1019 nodejs: Option<String> = "nodejs",
1020 npm: Option<String> = "npm",
1021 python: Option<String> = "python",
1022 reuse: Option<String> = "reuse",
1023 locked_deps: Option<bool> = "locked-deps",
1024 vendor: Option<bool> = "vendor",
1025 full_bootstrap: Option<bool> = "full-bootstrap",
1026 bootstrap_cache_path: Option<PathBuf> = "bootstrap-cache-path",
1027 extended: Option<bool> = "extended",
1028 tools: Option<HashSet<String>> = "tools",
1029 verbose: Option<usize> = "verbose",
1030 sanitizers: Option<bool> = "sanitizers",
1031 profiler: Option<bool> = "profiler",
1032 cargo_native_static: Option<bool> = "cargo-native-static",
1033 low_priority: Option<bool> = "low-priority",
1034 configure_args: Option<Vec<String>> = "configure-args",
1035 local_rebuild: Option<bool> = "local-rebuild",
1036 print_step_timings: Option<bool> = "print-step-timings",
1037 print_step_rusage: Option<bool> = "print-step-rusage",
1038 check_stage: Option<u32> = "check-stage",
1039 doc_stage: Option<u32> = "doc-stage",
1040 build_stage: Option<u32> = "build-stage",
1041 test_stage: Option<u32> = "test-stage",
1042 install_stage: Option<u32> = "install-stage",
1043 dist_stage: Option<u32> = "dist-stage",
1044 bench_stage: Option<u32> = "bench-stage",
1045 patch_binaries_for_nix: Option<bool> = "patch-binaries-for-nix",
1046 metrics: Option<bool> = "metrics",
1048 android_ndk: Option<PathBuf> = "android-ndk",
1049 optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
1050 jobs: Option<u32> = "jobs",
1051 compiletest_diff_tool: Option<String> = "compiletest-diff-tool",
1052 compiletest_use_stage0_libtest: Option<bool> = "compiletest-use-stage0-libtest",
1053 ccache: Option<StringOrBool> = "ccache",
1054 exclude: Option<Vec<PathBuf>> = "exclude",
1055 }
1056}
1057
1058define_config! {
1059 struct Install {
1061 prefix: Option<String> = "prefix",
1062 sysconfdir: Option<String> = "sysconfdir",
1063 docdir: Option<String> = "docdir",
1064 bindir: Option<String> = "bindir",
1065 libdir: Option<String> = "libdir",
1066 mandir: Option<String> = "mandir",
1067 datadir: Option<String> = "datadir",
1068 }
1069}
1070
1071define_config! {
1072 struct Llvm {
1074 optimize: Option<bool> = "optimize",
1075 thin_lto: Option<bool> = "thin-lto",
1076 release_debuginfo: Option<bool> = "release-debuginfo",
1077 assertions: Option<bool> = "assertions",
1078 tests: Option<bool> = "tests",
1079 enzyme: Option<bool> = "enzyme",
1080 plugins: Option<bool> = "plugins",
1081 ccache: Option<StringOrBool> = "ccache",
1083 static_libstdcpp: Option<bool> = "static-libstdcpp",
1084 libzstd: Option<bool> = "libzstd",
1085 ninja: Option<bool> = "ninja",
1086 targets: Option<String> = "targets",
1087 experimental_targets: Option<String> = "experimental-targets",
1088 link_jobs: Option<u32> = "link-jobs",
1089 link_shared: Option<bool> = "link-shared",
1090 version_suffix: Option<String> = "version-suffix",
1091 clang_cl: Option<String> = "clang-cl",
1092 cflags: Option<String> = "cflags",
1093 cxxflags: Option<String> = "cxxflags",
1094 ldflags: Option<String> = "ldflags",
1095 use_libcxx: Option<bool> = "use-libcxx",
1096 use_linker: Option<String> = "use-linker",
1097 allow_old_toolchain: Option<bool> = "allow-old-toolchain",
1098 offload: Option<bool> = "offload",
1099 polly: Option<bool> = "polly",
1100 clang: Option<bool> = "clang",
1101 enable_warnings: Option<bool> = "enable-warnings",
1102 download_ci_llvm: Option<StringOrBool> = "download-ci-llvm",
1103 build_config: Option<HashMap<String, String>> = "build-config",
1104 }
1105}
1106
1107define_config! {
1108 struct Gcc {
1110 download_ci_gcc: Option<bool> = "download-ci-gcc",
1111 }
1112}
1113
1114define_config! {
1115 struct Dist {
1116 sign_folder: Option<String> = "sign-folder",
1117 upload_addr: Option<String> = "upload-addr",
1118 src_tarball: Option<bool> = "src-tarball",
1119 compression_formats: Option<Vec<String>> = "compression-formats",
1120 compression_profile: Option<String> = "compression-profile",
1121 include_mingw_linker: Option<bool> = "include-mingw-linker",
1122 vendor: Option<bool> = "vendor",
1123 }
1124}
1125
1126#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
1127#[serde(untagged)]
1128pub enum StringOrBool {
1129 String(String),
1130 Bool(bool),
1131}
1132
1133impl Default for StringOrBool {
1134 fn default() -> StringOrBool {
1135 StringOrBool::Bool(false)
1136 }
1137}
1138
1139impl StringOrBool {
1140 fn is_string_or_true(&self) -> bool {
1141 matches!(self, Self::String(_) | Self::Bool(true))
1142 }
1143}
1144
1145#[derive(Clone, Debug, PartialEq, Eq)]
1146pub enum RustOptimize {
1147 String(String),
1148 Int(u8),
1149 Bool(bool),
1150}
1151
1152impl Default for RustOptimize {
1153 fn default() -> RustOptimize {
1154 RustOptimize::Bool(false)
1155 }
1156}
1157
1158impl<'de> Deserialize<'de> for RustOptimize {
1159 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1160 where
1161 D: Deserializer<'de>,
1162 {
1163 deserializer.deserialize_any(OptimizeVisitor)
1164 }
1165}
1166
1167struct OptimizeVisitor;
1168
1169impl serde::de::Visitor<'_> for OptimizeVisitor {
1170 type Value = RustOptimize;
1171
1172 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1173 formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#)
1174 }
1175
1176 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
1177 where
1178 E: serde::de::Error,
1179 {
1180 if matches!(value, "s" | "z") {
1181 Ok(RustOptimize::String(value.to_string()))
1182 } else {
1183 Err(serde::de::Error::custom(format_optimize_error_msg(value)))
1184 }
1185 }
1186
1187 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1188 where
1189 E: serde::de::Error,
1190 {
1191 if matches!(value, 0..=3) {
1192 Ok(RustOptimize::Int(value as u8))
1193 } else {
1194 Err(serde::de::Error::custom(format_optimize_error_msg(value)))
1195 }
1196 }
1197
1198 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
1199 where
1200 E: serde::de::Error,
1201 {
1202 Ok(RustOptimize::Bool(value))
1203 }
1204}
1205
1206fn format_optimize_error_msg(v: impl std::fmt::Display) -> String {
1207 format!(
1208 r#"unrecognized option for rust optimize: "{v}", expected one of 0, 1, 2, 3, "s", "z", true, false"#
1209 )
1210}
1211
1212impl RustOptimize {
1213 pub(crate) fn is_release(&self) -> bool {
1214 match &self {
1215 RustOptimize::Bool(true) | RustOptimize::String(_) => true,
1216 RustOptimize::Int(i) => *i > 0,
1217 RustOptimize::Bool(false) => false,
1218 }
1219 }
1220
1221 pub(crate) fn get_opt_level(&self) -> Option<String> {
1222 match &self {
1223 RustOptimize::String(s) => Some(s.clone()),
1224 RustOptimize::Int(i) => Some(i.to_string()),
1225 RustOptimize::Bool(_) => None,
1226 }
1227 }
1228}
1229
1230#[derive(Deserialize)]
1231#[serde(untagged)]
1232enum StringOrInt {
1233 String(String),
1234 Int(i64),
1235}
1236
1237impl<'de> Deserialize<'de> for LldMode {
1238 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1239 where
1240 D: Deserializer<'de>,
1241 {
1242 struct LldModeVisitor;
1243
1244 impl serde::de::Visitor<'_> for LldModeVisitor {
1245 type Value = LldMode;
1246
1247 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1248 formatter.write_str("one of true, 'self-contained' or 'external'")
1249 }
1250
1251 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
1252 where
1253 E: serde::de::Error,
1254 {
1255 Ok(if v { LldMode::External } else { LldMode::Unused })
1256 }
1257
1258 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
1259 where
1260 E: serde::de::Error,
1261 {
1262 match v {
1263 "external" => Ok(LldMode::External),
1264 "self-contained" => Ok(LldMode::SelfContained),
1265 _ => Err(E::custom(format!("unknown mode {v}"))),
1266 }
1267 }
1268 }
1269
1270 deserializer.deserialize_any(LldModeVisitor)
1271 }
1272}
1273
1274define_config! {
1275 struct Rust {
1277 optimize: Option<RustOptimize> = "optimize",
1278 debug: Option<bool> = "debug",
1279 codegen_units: Option<u32> = "codegen-units",
1280 codegen_units_std: Option<u32> = "codegen-units-std",
1281 rustc_debug_assertions: Option<bool> = "debug-assertions",
1282 randomize_layout: Option<bool> = "randomize-layout",
1283 std_debug_assertions: Option<bool> = "debug-assertions-std",
1284 tools_debug_assertions: Option<bool> = "debug-assertions-tools",
1285 overflow_checks: Option<bool> = "overflow-checks",
1286 overflow_checks_std: Option<bool> = "overflow-checks-std",
1287 debug_logging: Option<bool> = "debug-logging",
1288 debuginfo_level: Option<DebuginfoLevel> = "debuginfo-level",
1289 debuginfo_level_rustc: Option<DebuginfoLevel> = "debuginfo-level-rustc",
1290 debuginfo_level_std: Option<DebuginfoLevel> = "debuginfo-level-std",
1291 debuginfo_level_tools: Option<DebuginfoLevel> = "debuginfo-level-tools",
1292 debuginfo_level_tests: Option<DebuginfoLevel> = "debuginfo-level-tests",
1293 backtrace: Option<bool> = "backtrace",
1294 incremental: Option<bool> = "incremental",
1295 default_linker: Option<String> = "default-linker",
1296 channel: Option<String> = "channel",
1297 description: Option<String> = "description",
1299 musl_root: Option<String> = "musl-root",
1300 rpath: Option<bool> = "rpath",
1301 strip: Option<bool> = "strip",
1302 frame_pointers: Option<bool> = "frame-pointers",
1303 stack_protector: Option<String> = "stack-protector",
1304 verbose_tests: Option<bool> = "verbose-tests",
1305 optimize_tests: Option<bool> = "optimize-tests",
1306 codegen_tests: Option<bool> = "codegen-tests",
1307 omit_git_hash: Option<bool> = "omit-git-hash",
1308 dist_src: Option<bool> = "dist-src",
1309 save_toolstates: Option<String> = "save-toolstates",
1310 codegen_backends: Option<Vec<String>> = "codegen-backends",
1311 llvm_bitcode_linker: Option<bool> = "llvm-bitcode-linker",
1312 lld: Option<bool> = "lld",
1313 lld_mode: Option<LldMode> = "use-lld",
1314 llvm_tools: Option<bool> = "llvm-tools",
1315 deny_warnings: Option<bool> = "deny-warnings",
1316 backtrace_on_ice: Option<bool> = "backtrace-on-ice",
1317 verify_llvm_ir: Option<bool> = "verify-llvm-ir",
1318 thin_lto_import_instr_limit: Option<u32> = "thin-lto-import-instr-limit",
1319 remap_debuginfo: Option<bool> = "remap-debuginfo",
1320 jemalloc: Option<bool> = "jemalloc",
1321 test_compare_mode: Option<bool> = "test-compare-mode",
1322 llvm_libunwind: Option<String> = "llvm-libunwind",
1323 control_flow_guard: Option<bool> = "control-flow-guard",
1324 ehcont_guard: Option<bool> = "ehcont-guard",
1325 new_symbol_mangling: Option<bool> = "new-symbol-mangling",
1326 profile_generate: Option<String> = "profile-generate",
1327 profile_use: Option<String> = "profile-use",
1328 download_rustc: Option<StringOrBool> = "download-rustc",
1330 lto: Option<String> = "lto",
1331 validate_mir_opts: Option<u32> = "validate-mir-opts",
1332 std_features: Option<BTreeSet<String>> = "std-features",
1333 }
1334}
1335
1336define_config! {
1337 struct TomlTarget {
1339 cc: Option<String> = "cc",
1340 cxx: Option<String> = "cxx",
1341 ar: Option<String> = "ar",
1342 ranlib: Option<String> = "ranlib",
1343 default_linker: Option<PathBuf> = "default-linker",
1344 linker: Option<String> = "linker",
1345 split_debuginfo: Option<String> = "split-debuginfo",
1346 llvm_config: Option<String> = "llvm-config",
1347 llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches",
1348 llvm_filecheck: Option<String> = "llvm-filecheck",
1349 llvm_libunwind: Option<String> = "llvm-libunwind",
1350 sanitizers: Option<bool> = "sanitizers",
1351 profiler: Option<StringOrBool> = "profiler",
1352 rpath: Option<bool> = "rpath",
1353 crt_static: Option<bool> = "crt-static",
1354 musl_root: Option<String> = "musl-root",
1355 musl_libdir: Option<String> = "musl-libdir",
1356 wasi_root: Option<String> = "wasi-root",
1357 qemu_rootfs: Option<String> = "qemu-rootfs",
1358 no_std: Option<bool> = "no-std",
1359 codegen_backends: Option<Vec<String>> = "codegen-backends",
1360 runner: Option<String> = "runner",
1361 optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
1362 jemalloc: Option<bool> = "jemalloc",
1363 }
1364}
1365
1366impl Config {
1367 #[cfg_attr(
1368 feature = "tracing",
1369 instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::default_opts")
1370 )]
1371 pub fn default_opts() -> Config {
1372 #[cfg(feature = "tracing")]
1373 span!(target: "CONFIG_HANDLING", tracing::Level::TRACE, "constructing default config");
1374
1375 Config {
1376 bypass_bootstrap_lock: false,
1377 llvm_optimize: true,
1378 ninja_in_file: true,
1379 llvm_static_stdcpp: false,
1380 llvm_libzstd: false,
1381 backtrace: true,
1382 rust_optimize: RustOptimize::Bool(true),
1383 rust_optimize_tests: true,
1384 rust_randomize_layout: false,
1385 submodules: None,
1386 docs: true,
1387 docs_minification: true,
1388 rust_rpath: true,
1389 rust_strip: false,
1390 channel: "dev".to_string(),
1391 codegen_tests: true,
1392 rust_dist_src: true,
1393 rust_codegen_backends: vec!["llvm".to_owned()],
1394 deny_warnings: true,
1395 bindir: "bin".into(),
1396 dist_include_mingw_linker: true,
1397 dist_compression_profile: "fast".into(),
1398
1399 stdout_is_tty: std::io::stdout().is_terminal(),
1400 stderr_is_tty: std::io::stderr().is_terminal(),
1401
1402 build: TargetSelection::from_user(env!("BUILD_TRIPLE")),
1404
1405 src: {
1406 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1407 manifest_dir.parent().unwrap().parent().unwrap().to_owned()
1409 },
1410 out: PathBuf::from("build"),
1411
1412 llvm_tools_enabled: true,
1415
1416 ..Default::default()
1417 }
1418 }
1419
1420 pub(crate) fn get_builder_toml(&self, build_name: &str) -> Result<TomlConfig, toml::de::Error> {
1421 if self.dry_run() {
1422 return Ok(TomlConfig::default());
1423 }
1424
1425 let builder_config_path =
1426 self.out.join(self.build.triple).join(build_name).join(BUILDER_CONFIG_FILENAME);
1427 Self::get_toml(&builder_config_path)
1428 }
1429
1430 pub(crate) fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
1431 #[cfg(test)]
1432 return Ok(TomlConfig::default());
1433
1434 #[cfg(not(test))]
1435 Self::get_toml_inner(file)
1436 }
1437
1438 fn get_toml_inner(file: &Path) -> Result<TomlConfig, toml::de::Error> {
1439 let contents =
1440 t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
1441 toml::from_str(&contents)
1444 .and_then(|table: toml::Value| TomlConfig::deserialize(table))
1445 .inspect_err(|_| {
1446 if let Ok(ChangeIdWrapper { inner: Some(ChangeId::Id(id)) }) =
1447 toml::from_str::<toml::Value>(&contents)
1448 .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table))
1449 {
1450 let changes = crate::find_recent_config_change_ids(id);
1451 if !changes.is_empty() {
1452 println!(
1453 "WARNING: There have been changes to x.py since you last updated:\n{}",
1454 crate::human_readable_changes(changes)
1455 );
1456 }
1457 }
1458 })
1459 }
1460
1461 #[cfg_attr(
1462 feature = "tracing",
1463 instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::parse", skip_all)
1464 )]
1465 pub fn parse(flags: Flags) -> Config {
1466 Self::parse_inner(flags, Self::get_toml)
1467 }
1468
1469 #[cfg_attr(
1470 feature = "tracing",
1471 instrument(
1472 target = "CONFIG_HANDLING",
1473 level = "trace",
1474 name = "Config::parse_inner",
1475 skip_all
1476 )
1477 )]
1478 pub(crate) fn parse_inner(
1479 mut flags: Flags,
1480 get_toml: impl Fn(&Path) -> Result<TomlConfig, toml::de::Error>,
1481 ) -> Config {
1482 let mut config = Config::default_opts();
1483
1484 config.paths = std::mem::take(&mut flags.paths);
1486
1487 #[cfg(feature = "tracing")]
1488 span!(
1489 target: "CONFIG_HANDLING",
1490 tracing::Level::TRACE,
1491 "collecting paths and path exclusions",
1492 "flags.paths" = ?flags.paths,
1493 "flags.skip" = ?flags.skip,
1494 "flags.exclude" = ?flags.exclude
1495 );
1496
1497 #[cfg(feature = "tracing")]
1498 span!(
1499 target: "CONFIG_HANDLING",
1500 tracing::Level::TRACE,
1501 "normalizing and combining `flag.skip`/`flag.exclude` paths",
1502 "config.skip" = ?config.skip,
1503 );
1504
1505 config.include_default_paths = flags.include_default_paths;
1506 config.rustc_error_format = flags.rustc_error_format;
1507 config.json_output = flags.json_output;
1508 config.on_fail = flags.on_fail;
1509 config.cmd = flags.cmd;
1510 config.incremental = flags.incremental;
1511 config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled };
1512 config.dump_bootstrap_shims = flags.dump_bootstrap_shims;
1513 config.keep_stage = flags.keep_stage;
1514 config.keep_stage_std = flags.keep_stage_std;
1515 config.color = flags.color;
1516 config.free_args = std::mem::take(&mut flags.free_args);
1517 config.llvm_profile_use = flags.llvm_profile_use;
1518 config.llvm_profile_generate = flags.llvm_profile_generate;
1519 config.enable_bolt_settings = flags.enable_bolt_settings;
1520 config.bypass_bootstrap_lock = flags.bypass_bootstrap_lock;
1521 config.is_running_on_ci = flags.ci.unwrap_or(CiEnv::is_ci());
1522
1523 if let Some(src) = flags.src {
1526 config.src = src
1527 } else {
1528 let mut cmd = helpers::git(None);
1531 cmd.arg("rev-parse").arg("--show-cdup");
1539 let output = cmd
1541 .as_command_mut()
1542 .stderr(std::process::Stdio::null())
1543 .output()
1544 .ok()
1545 .and_then(|output| if output.status.success() { Some(output) } else { None });
1546 if let Some(output) = output {
1547 let git_root_relative = String::from_utf8(output.stdout).unwrap();
1548 let git_root = env::current_dir()
1551 .unwrap()
1552 .join(PathBuf::from(git_root_relative.trim()))
1553 .canonicalize()
1554 .unwrap();
1555 let s = git_root.to_str().unwrap();
1556
1557 let git_root = match s.strip_prefix("\\\\?\\") {
1559 Some(p) => PathBuf::from(p),
1560 None => git_root,
1561 };
1562 if git_root.join("src").join("stage0").exists() {
1569 config.src = git_root;
1570 }
1571 } else {
1572 }
1575 }
1576
1577 if cfg!(test) {
1578 config.out = Path::new(
1580 &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"),
1581 )
1582 .parent()
1583 .unwrap()
1584 .to_path_buf();
1585 }
1586
1587 config.stage0_metadata = build_helper::stage0_parser::parse_stage0_file();
1588
1589 let toml_path = flags
1597 .config
1598 .clone()
1599 .or_else(|| env::var_os("RUST_BOOTSTRAP_CONFIG").map(PathBuf::from));
1600 let using_default_path = toml_path.is_none();
1601 let mut toml_path = toml_path.unwrap_or_else(|| PathBuf::from("bootstrap.toml"));
1602
1603 if using_default_path && !toml_path.exists() {
1604 toml_path = config.src.join(PathBuf::from("bootstrap.toml"));
1605 if !toml_path.exists() {
1606 toml_path = PathBuf::from("config.toml");
1607 if !toml_path.exists() {
1608 toml_path = config.src.join(PathBuf::from("config.toml"));
1609 }
1610 }
1611 }
1612
1613 let mut toml = if !using_default_path || toml_path.exists() {
1616 config.config = Some(if cfg!(not(test)) {
1617 toml_path = toml_path.canonicalize().unwrap();
1618 toml_path.clone()
1619 } else {
1620 toml_path.clone()
1621 });
1622 get_toml(&toml_path).unwrap_or_else(|e| {
1623 eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
1624 exit!(2);
1625 })
1626 } else {
1627 config.config = None;
1628 TomlConfig::default()
1629 };
1630
1631 if cfg!(test) {
1632 let build = toml.build.get_or_insert_with(Default::default);
1638 build.rustc = build.rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into()));
1639 build.cargo = build.cargo.take().or(std::env::var_os("CARGO").map(|p| p.into()));
1640 }
1641
1642 if GitInfo::new(false, &config.src).is_from_tarball() && toml.profile.is_none() {
1643 toml.profile = Some("dist".into());
1644 }
1645
1646 for include_path in toml.include.clone().unwrap_or_default().iter().rev() {
1652 let include_path = toml_path.parent().unwrap().join(include_path);
1653
1654 let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
1655 eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
1656 exit!(2);
1657 });
1658 toml.merge(
1659 Some(include_path),
1660 &mut Default::default(),
1661 included_toml,
1662 ReplaceOpt::IgnoreDuplicate,
1663 );
1664 }
1665
1666 if let Some(include) = &toml.profile {
1667 let profile_aliases = HashMap::from([("user", "dist")]);
1671 let include = match profile_aliases.get(include.as_str()) {
1672 Some(alias) => alias,
1673 None => include.as_str(),
1674 };
1675 let mut include_path = config.src.clone();
1676 include_path.push("src");
1677 include_path.push("bootstrap");
1678 include_path.push("defaults");
1679 include_path.push(format!("bootstrap.{include}.toml"));
1680 let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
1681 eprintln!(
1682 "ERROR: Failed to parse default config profile at '{}': {e}",
1683 include_path.display()
1684 );
1685 exit!(2);
1686 });
1687 toml.merge(
1688 Some(include_path),
1689 &mut Default::default(),
1690 included_toml,
1691 ReplaceOpt::IgnoreDuplicate,
1692 );
1693 }
1694
1695 let mut override_toml = TomlConfig::default();
1696 for option in flags.set.iter() {
1697 fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
1698 toml::from_str(option).and_then(|table: toml::Value| TomlConfig::deserialize(table))
1699 }
1700
1701 let mut err = match get_table(option) {
1702 Ok(v) => {
1703 override_toml.merge(
1704 None,
1705 &mut Default::default(),
1706 v,
1707 ReplaceOpt::ErrorOnDuplicate,
1708 );
1709 continue;
1710 }
1711 Err(e) => e,
1712 };
1713 if let Some((key, value)) = option.split_once('=') {
1716 if !value.contains('"') {
1717 match get_table(&format!(r#"{key}="{value}""#)) {
1718 Ok(v) => {
1719 override_toml.merge(
1720 None,
1721 &mut Default::default(),
1722 v,
1723 ReplaceOpt::ErrorOnDuplicate,
1724 );
1725 continue;
1726 }
1727 Err(e) => err = e,
1728 }
1729 }
1730 }
1731 eprintln!("failed to parse override `{option}`: `{err}");
1732 exit!(2)
1733 }
1734 toml.merge(None, &mut Default::default(), override_toml, ReplaceOpt::Override);
1735
1736 config.change_id = toml.change_id.inner;
1737
1738 let Build {
1739 mut description,
1740 build,
1741 host,
1742 target,
1743 build_dir,
1744 cargo,
1745 rustc,
1746 rustfmt,
1747 cargo_clippy,
1748 docs,
1749 compiler_docs,
1750 library_docs_private_items,
1751 docs_minification,
1752 submodules,
1753 gdb,
1754 lldb,
1755 nodejs,
1756 npm,
1757 python,
1758 reuse,
1759 locked_deps,
1760 vendor,
1761 full_bootstrap,
1762 bootstrap_cache_path,
1763 extended,
1764 tools,
1765 verbose,
1766 sanitizers,
1767 profiler,
1768 cargo_native_static,
1769 low_priority,
1770 configure_args,
1771 local_rebuild,
1772 print_step_timings,
1773 print_step_rusage,
1774 check_stage,
1775 doc_stage,
1776 build_stage,
1777 test_stage,
1778 install_stage,
1779 dist_stage,
1780 bench_stage,
1781 patch_binaries_for_nix,
1782 metrics: _,
1784 android_ndk,
1785 optimized_compiler_builtins,
1786 jobs,
1787 compiletest_diff_tool,
1788 compiletest_use_stage0_libtest,
1789 mut ccache,
1790 exclude,
1791 } = toml.build.unwrap_or_default();
1792
1793 let mut paths: Vec<PathBuf> = flags.skip.into_iter().chain(flags.exclude).collect();
1794
1795 if let Some(exclude) = exclude {
1796 paths.extend(exclude);
1797 }
1798
1799 config.skip = paths
1800 .into_iter()
1801 .map(|p| {
1802 if cfg!(windows) {
1806 PathBuf::from(p.to_str().unwrap().replace('/', "\\"))
1807 } else {
1808 p
1809 }
1810 })
1811 .collect();
1812
1813 config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0))));
1814
1815 if let Some(file_build) = build {
1816 config.build = TargetSelection::from_user(&file_build);
1817 };
1818
1819 set(&mut config.out, flags.build_dir.or_else(|| build_dir.map(PathBuf::from)));
1820 if !config.out.is_absolute() {
1823 config.out = absolute(&config.out).expect("can't make empty path absolute");
1825 }
1826
1827 if cargo_clippy.is_some() && rustc.is_none() {
1828 println!(
1829 "WARNING: Using `build.cargo-clippy` without `build.rustc` usually fails due to toolchain conflict."
1830 );
1831 }
1832
1833 config.initial_rustc = if let Some(rustc) = rustc {
1834 if !flags.skip_stage0_validation {
1835 config.check_stage0_version(&rustc, "rustc");
1836 }
1837 rustc
1838 } else {
1839 config.download_beta_toolchain();
1840 config
1841 .out
1842 .join(config.build)
1843 .join("stage0")
1844 .join("bin")
1845 .join(exe("rustc", config.build))
1846 };
1847
1848 config.initial_sysroot = config.initial_rustc.ancestors().nth(2).unwrap().into();
1849
1850 config.initial_cargo_clippy = cargo_clippy;
1851
1852 config.initial_cargo = if let Some(cargo) = cargo {
1853 if !flags.skip_stage0_validation {
1854 config.check_stage0_version(&cargo, "cargo");
1855 }
1856 cargo
1857 } else {
1858 config.download_beta_toolchain();
1859 config.initial_sysroot.join("bin").join(exe("cargo", config.build))
1860 };
1861
1862 if config.dry_run() {
1864 let dir = config.out.join("tmp-dry-run");
1865 t!(fs::create_dir_all(&dir));
1866 config.out = dir;
1867 }
1868
1869 config.hosts = if let Some(TargetSelectionList(arg_host)) = flags.host {
1870 arg_host
1871 } else if let Some(file_host) = host {
1872 file_host.iter().map(|h| TargetSelection::from_user(h)).collect()
1873 } else {
1874 vec![config.build]
1875 };
1876 config.targets = if let Some(TargetSelectionList(arg_target)) = flags.target {
1877 arg_target
1878 } else if let Some(file_target) = target {
1879 file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
1880 } else {
1881 config.hosts.clone()
1884 };
1885
1886 config.nodejs = nodejs.map(PathBuf::from);
1887 config.npm = npm.map(PathBuf::from);
1888 config.gdb = gdb.map(PathBuf::from);
1889 config.lldb = lldb.map(PathBuf::from);
1890 config.python = python.map(PathBuf::from);
1891 config.reuse = reuse.map(PathBuf::from);
1892 config.submodules = submodules;
1893 config.android_ndk = android_ndk;
1894 config.bootstrap_cache_path = bootstrap_cache_path;
1895 set(&mut config.low_priority, low_priority);
1896 set(&mut config.compiler_docs, compiler_docs);
1897 set(&mut config.library_docs_private_items, library_docs_private_items);
1898 set(&mut config.docs_minification, docs_minification);
1899 set(&mut config.docs, docs);
1900 set(&mut config.locked_deps, locked_deps);
1901 set(&mut config.full_bootstrap, full_bootstrap);
1902 set(&mut config.extended, extended);
1903 config.tools = tools;
1904 set(&mut config.verbose, verbose);
1905 set(&mut config.sanitizers, sanitizers);
1906 set(&mut config.profiler, profiler);
1907 set(&mut config.cargo_native_static, cargo_native_static);
1908 set(&mut config.configure_args, configure_args);
1909 set(&mut config.local_rebuild, local_rebuild);
1910 set(&mut config.print_step_timings, print_step_timings);
1911 set(&mut config.print_step_rusage, print_step_rusage);
1912 config.patch_binaries_for_nix = patch_binaries_for_nix;
1913
1914 config.verbose = cmp::max(config.verbose, flags.verbose as usize);
1915
1916 config.verbose_tests = config.is_verbose();
1918
1919 if let Some(install) = toml.install {
1920 let Install { prefix, sysconfdir, docdir, bindir, libdir, mandir, datadir } = install;
1921 config.prefix = prefix.map(PathBuf::from);
1922 config.sysconfdir = sysconfdir.map(PathBuf::from);
1923 config.datadir = datadir.map(PathBuf::from);
1924 config.docdir = docdir.map(PathBuf::from);
1925 set(&mut config.bindir, bindir.map(PathBuf::from));
1926 config.libdir = libdir.map(PathBuf::from);
1927 config.mandir = mandir.map(PathBuf::from);
1928 }
1929
1930 config.llvm_assertions =
1931 toml.llvm.as_ref().is_some_and(|llvm| llvm.assertions.unwrap_or(false));
1932
1933 let mut llvm_tests = None;
1936 let mut llvm_enzyme = None;
1937 let mut llvm_offload = None;
1938 let mut llvm_plugins = None;
1939 let mut debug = None;
1940 let mut rustc_debug_assertions = None;
1941 let mut std_debug_assertions = None;
1942 let mut tools_debug_assertions = None;
1943 let mut overflow_checks = None;
1944 let mut overflow_checks_std = None;
1945 let mut debug_logging = None;
1946 let mut debuginfo_level = None;
1947 let mut debuginfo_level_rustc = None;
1948 let mut debuginfo_level_std = None;
1949 let mut debuginfo_level_tools = None;
1950 let mut debuginfo_level_tests = None;
1951 let mut optimize = None;
1952 let mut lld_enabled = None;
1953 let mut std_features = None;
1954
1955 let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel")));
1956 let ci_channel = file_content.trim_end();
1957
1958 let toml_channel = toml.rust.as_ref().and_then(|r| r.channel.clone());
1959 let is_user_configured_rust_channel = match toml_channel {
1960 Some(channel) if channel == "auto-detect" => {
1961 config.channel = ci_channel.into();
1962 true
1963 }
1964 Some(channel) => {
1965 config.channel = channel;
1966 true
1967 }
1968 None => false,
1969 };
1970
1971 let default = config.channel == "dev";
1972 config.omit_git_hash = toml.rust.as_ref().and_then(|r| r.omit_git_hash).unwrap_or(default);
1973
1974 config.rust_info = GitInfo::new(config.omit_git_hash, &config.src);
1975 config.cargo_info = GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/cargo"));
1976 config.rust_analyzer_info =
1977 GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/rust-analyzer"));
1978 config.clippy_info =
1979 GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/clippy"));
1980 config.miri_info = GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/miri"));
1981 config.rustfmt_info =
1982 GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/rustfmt"));
1983 config.enzyme_info =
1984 GitInfo::new(config.omit_git_hash, &config.src.join("src/tools/enzyme"));
1985 config.in_tree_llvm_info = GitInfo::new(false, &config.src.join("src/llvm-project"));
1986 config.in_tree_gcc_info = GitInfo::new(false, &config.src.join("src/gcc"));
1987
1988 config.vendor = vendor.unwrap_or(
1989 config.rust_info.is_from_tarball()
1990 && config.src.join("vendor").exists()
1991 && config.src.join(".cargo/config.toml").exists(),
1992 );
1993
1994 if !is_user_configured_rust_channel && config.rust_info.is_from_tarball() {
1995 config.channel = ci_channel.into();
1996 }
1997
1998 if let Some(rust) = toml.rust {
1999 let Rust {
2000 optimize: optimize_toml,
2001 debug: debug_toml,
2002 codegen_units,
2003 codegen_units_std,
2004 rustc_debug_assertions: rustc_debug_assertions_toml,
2005 std_debug_assertions: std_debug_assertions_toml,
2006 tools_debug_assertions: tools_debug_assertions_toml,
2007 overflow_checks: overflow_checks_toml,
2008 overflow_checks_std: overflow_checks_std_toml,
2009 debug_logging: debug_logging_toml,
2010 debuginfo_level: debuginfo_level_toml,
2011 debuginfo_level_rustc: debuginfo_level_rustc_toml,
2012 debuginfo_level_std: debuginfo_level_std_toml,
2013 debuginfo_level_tools: debuginfo_level_tools_toml,
2014 debuginfo_level_tests: debuginfo_level_tests_toml,
2015 backtrace,
2016 incremental,
2017 randomize_layout,
2018 default_linker,
2019 channel: _, description: rust_description,
2021 musl_root,
2022 rpath,
2023 verbose_tests,
2024 optimize_tests,
2025 codegen_tests,
2026 omit_git_hash: _, dist_src,
2028 save_toolstates,
2029 codegen_backends,
2030 lld: lld_enabled_toml,
2031 llvm_tools,
2032 llvm_bitcode_linker,
2033 deny_warnings,
2034 backtrace_on_ice,
2035 verify_llvm_ir,
2036 thin_lto_import_instr_limit,
2037 remap_debuginfo,
2038 jemalloc,
2039 test_compare_mode,
2040 llvm_libunwind,
2041 control_flow_guard,
2042 ehcont_guard,
2043 new_symbol_mangling,
2044 profile_generate,
2045 profile_use,
2046 download_rustc,
2047 lto,
2048 validate_mir_opts,
2049 frame_pointers,
2050 stack_protector,
2051 strip,
2052 lld_mode,
2053 std_features: std_features_toml,
2054 } = rust;
2055
2056 let debug_assertions_requested = matches!(rustc_debug_assertions_toml, Some(true))
2067 || (matches!(debug_toml, Some(true))
2068 && !matches!(rustc_debug_assertions_toml, Some(false)));
2069
2070 if debug_assertions_requested {
2071 if let Some(ref opt) = download_rustc {
2072 if opt.is_string_or_true() {
2073 eprintln!(
2074 "WARN: currently no CI rustc builds have rustc debug assertions \
2075 enabled. Please either set `rust.debug-assertions` to `false` if you \
2076 want to use download CI rustc or set `rust.download-rustc` to `false`."
2077 );
2078 }
2079 }
2080 }
2081
2082 config.download_rustc_commit = config.download_ci_rustc_commit(
2083 download_rustc,
2084 debug_assertions_requested,
2085 config.llvm_assertions,
2086 );
2087
2088 debug = debug_toml;
2089 rustc_debug_assertions = rustc_debug_assertions_toml;
2090 std_debug_assertions = std_debug_assertions_toml;
2091 tools_debug_assertions = tools_debug_assertions_toml;
2092 overflow_checks = overflow_checks_toml;
2093 overflow_checks_std = overflow_checks_std_toml;
2094 debug_logging = debug_logging_toml;
2095 debuginfo_level = debuginfo_level_toml;
2096 debuginfo_level_rustc = debuginfo_level_rustc_toml;
2097 debuginfo_level_std = debuginfo_level_std_toml;
2098 debuginfo_level_tools = debuginfo_level_tools_toml;
2099 debuginfo_level_tests = debuginfo_level_tests_toml;
2100 lld_enabled = lld_enabled_toml;
2101 std_features = std_features_toml;
2102
2103 optimize = optimize_toml;
2104 config.rust_new_symbol_mangling = new_symbol_mangling;
2105 set(&mut config.rust_optimize_tests, optimize_tests);
2106 set(&mut config.codegen_tests, codegen_tests);
2107 set(&mut config.rust_rpath, rpath);
2108 set(&mut config.rust_strip, strip);
2109 set(&mut config.rust_frame_pointers, frame_pointers);
2110 config.rust_stack_protector = stack_protector;
2111 set(&mut config.jemalloc, jemalloc);
2112 set(&mut config.test_compare_mode, test_compare_mode);
2113 set(&mut config.backtrace, backtrace);
2114 if rust_description.is_some() {
2115 eprintln!(
2116 "Warning: rust.description is deprecated. Use build.description instead."
2117 );
2118 }
2119 description = description.or(rust_description);
2120 set(&mut config.rust_dist_src, dist_src);
2121 set(&mut config.verbose_tests, verbose_tests);
2122 if let Some(true) = incremental {
2124 config.incremental = true;
2125 }
2126 set(&mut config.lld_mode, lld_mode);
2127 set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker);
2128
2129 config.rust_randomize_layout = randomize_layout.unwrap_or_default();
2130 config.llvm_tools_enabled = llvm_tools.unwrap_or(true);
2131
2132 config.llvm_enzyme =
2133 llvm_enzyme.unwrap_or(config.channel == "dev" || config.channel == "nightly");
2134 config.rustc_default_linker = default_linker;
2135 config.musl_root = musl_root.map(PathBuf::from);
2136 config.save_toolstates = save_toolstates.map(PathBuf::from);
2137 set(
2138 &mut config.deny_warnings,
2139 match flags.warnings {
2140 Warnings::Deny => Some(true),
2141 Warnings::Warn => Some(false),
2142 Warnings::Default => deny_warnings,
2143 },
2144 );
2145 set(&mut config.backtrace_on_ice, backtrace_on_ice);
2146 set(&mut config.rust_verify_llvm_ir, verify_llvm_ir);
2147 config.rust_thin_lto_import_instr_limit = thin_lto_import_instr_limit;
2148 set(&mut config.rust_remap_debuginfo, remap_debuginfo);
2149 set(&mut config.control_flow_guard, control_flow_guard);
2150 set(&mut config.ehcont_guard, ehcont_guard);
2151 config.llvm_libunwind_default =
2152 llvm_libunwind.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));
2153
2154 if let Some(ref backends) = codegen_backends {
2155 let available_backends = ["llvm", "cranelift", "gcc"];
2156
2157 config.rust_codegen_backends = backends.iter().map(|s| {
2158 if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) {
2159 if available_backends.contains(&backend) {
2160 panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'.");
2161 } else {
2162 println!("HELP: '{s}' for 'rust.codegen-backends' might fail. \
2163 Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
2164 In this case, it would be referred to as '{backend}'.");
2165 }
2166 }
2167
2168 s.clone()
2169 }).collect();
2170 }
2171
2172 config.rust_codegen_units = codegen_units.map(threads_from_config);
2173 config.rust_codegen_units_std = codegen_units_std.map(threads_from_config);
2174 config.rust_profile_use = flags.rust_profile_use.or(profile_use);
2175 config.rust_profile_generate = flags.rust_profile_generate.or(profile_generate);
2176 config.rust_lto =
2177 lto.as_deref().map(|value| RustcLto::from_str(value).unwrap()).unwrap_or_default();
2178 config.rust_validate_mir_opts = validate_mir_opts;
2179 } else {
2180 config.rust_profile_use = flags.rust_profile_use;
2181 config.rust_profile_generate = flags.rust_profile_generate;
2182 }
2183
2184 config.reproducible_artifacts = flags.reproducible_artifact;
2185 config.description = description;
2186
2187 if let Some(commit) = &config.download_rustc_commit {
2191 if is_user_configured_rust_channel {
2192 println!(
2193 "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
2194 );
2195
2196 let channel = config
2197 .read_file_by_commit(Path::new("src/ci/channel"), commit)
2198 .trim()
2199 .to_owned();
2200
2201 config.channel = channel;
2202 }
2203 }
2204
2205 if let Some(llvm) = toml.llvm {
2206 let Llvm {
2207 optimize: optimize_toml,
2208 thin_lto,
2209 release_debuginfo,
2210 assertions: _,
2211 tests,
2212 enzyme,
2213 plugins,
2214 ccache: llvm_ccache,
2215 static_libstdcpp,
2216 libzstd,
2217 ninja,
2218 targets,
2219 experimental_targets,
2220 link_jobs,
2221 link_shared,
2222 version_suffix,
2223 clang_cl,
2224 cflags,
2225 cxxflags,
2226 ldflags,
2227 use_libcxx,
2228 use_linker,
2229 allow_old_toolchain,
2230 offload,
2231 polly,
2232 clang,
2233 enable_warnings,
2234 download_ci_llvm,
2235 build_config,
2236 } = llvm;
2237 if llvm_ccache.is_some() {
2238 eprintln!("Warning: llvm.ccache is deprecated. Use build.ccache instead.");
2239 }
2240
2241 ccache = ccache.or(llvm_ccache);
2242 set(&mut config.ninja_in_file, ninja);
2243 llvm_tests = tests;
2244 llvm_enzyme = enzyme;
2245 llvm_offload = offload;
2246 llvm_plugins = plugins;
2247 set(&mut config.llvm_optimize, optimize_toml);
2248 set(&mut config.llvm_thin_lto, thin_lto);
2249 set(&mut config.llvm_release_debuginfo, release_debuginfo);
2250 set(&mut config.llvm_static_stdcpp, static_libstdcpp);
2251 set(&mut config.llvm_libzstd, libzstd);
2252 if let Some(v) = link_shared {
2253 config.llvm_link_shared.set(Some(v));
2254 }
2255 config.llvm_targets.clone_from(&targets);
2256 config.llvm_experimental_targets.clone_from(&experimental_targets);
2257 config.llvm_link_jobs = link_jobs;
2258 config.llvm_version_suffix.clone_from(&version_suffix);
2259 config.llvm_clang_cl.clone_from(&clang_cl);
2260
2261 config.llvm_cflags.clone_from(&cflags);
2262 config.llvm_cxxflags.clone_from(&cxxflags);
2263 config.llvm_ldflags.clone_from(&ldflags);
2264 set(&mut config.llvm_use_libcxx, use_libcxx);
2265 config.llvm_use_linker.clone_from(&use_linker);
2266 config.llvm_allow_old_toolchain = allow_old_toolchain.unwrap_or(false);
2267 config.llvm_offload = offload.unwrap_or(false);
2268 config.llvm_polly = polly.unwrap_or(false);
2269 config.llvm_clang = clang.unwrap_or(false);
2270 config.llvm_enable_warnings = enable_warnings.unwrap_or(false);
2271 config.llvm_build_config = build_config.clone().unwrap_or(Default::default());
2272
2273 config.llvm_from_ci =
2274 config.parse_download_ci_llvm(download_ci_llvm, config.llvm_assertions);
2275
2276 if config.llvm_from_ci {
2277 let warn = |option: &str| {
2278 println!(
2279 "WARNING: `{option}` will only be used on `compiler/rustc_llvm` build, not for the LLVM build."
2280 );
2281 println!(
2282 "HELP: To use `{option}` for LLVM builds, set `download-ci-llvm` option to false."
2283 );
2284 };
2285
2286 if static_libstdcpp.is_some() {
2287 warn("static-libstdcpp");
2288 }
2289
2290 if link_shared.is_some() {
2291 warn("link-shared");
2292 }
2293
2294 if libzstd.is_some() {
2300 println!(
2301 "WARNING: when using `download-ci-llvm`, the local `llvm.libzstd` option, \
2302 like almost all `llvm.*` options, will be ignored and set by the LLVM CI \
2303 artifacts builder config."
2304 );
2305 println!(
2306 "HELP: To use `llvm.libzstd` for LLVM/LLD builds, set `download-ci-llvm` option to false."
2307 );
2308 }
2309 }
2310
2311 if !config.llvm_from_ci && config.llvm_thin_lto && link_shared.is_none() {
2312 config.llvm_link_shared.set(Some(true));
2316 }
2317 } else {
2318 config.llvm_from_ci = config.parse_download_ci_llvm(None, false);
2319 }
2320
2321 if let Some(gcc) = toml.gcc {
2322 config.gcc_ci_mode = match gcc.download_ci_gcc {
2323 Some(value) => match value {
2324 true => GccCiMode::DownloadFromCi,
2325 false => GccCiMode::BuildLocally,
2326 },
2327 None => GccCiMode::default(),
2328 };
2329 }
2330
2331 if let Some(t) = toml.target {
2332 for (triple, cfg) in t {
2333 let mut target = Target::from_triple(&triple);
2334
2335 if let Some(ref s) = cfg.llvm_config {
2336 if config.download_rustc_commit.is_some() && triple == *config.build.triple {
2337 panic!(
2338 "setting llvm_config for the host is incompatible with download-rustc"
2339 );
2340 }
2341 target.llvm_config = Some(config.src.join(s));
2342 }
2343 if let Some(patches) = cfg.llvm_has_rust_patches {
2344 assert!(
2345 config.submodules == Some(false) || cfg.llvm_config.is_some(),
2346 "use of `llvm-has-rust-patches` is restricted to cases where either submodules are disabled or llvm-config been provided"
2347 );
2348 target.llvm_has_rust_patches = Some(patches);
2349 }
2350 if let Some(ref s) = cfg.llvm_filecheck {
2351 target.llvm_filecheck = Some(config.src.join(s));
2352 }
2353 target.llvm_libunwind = cfg.llvm_libunwind.as_ref().map(|v| {
2354 v.parse().unwrap_or_else(|_| {
2355 panic!("failed to parse target.{triple}.llvm-libunwind")
2356 })
2357 });
2358 if let Some(s) = cfg.no_std {
2359 target.no_std = s;
2360 }
2361 target.cc = cfg.cc.map(PathBuf::from);
2362 target.cxx = cfg.cxx.map(PathBuf::from);
2363 target.ar = cfg.ar.map(PathBuf::from);
2364 target.ranlib = cfg.ranlib.map(PathBuf::from);
2365 target.linker = cfg.linker.map(PathBuf::from);
2366 target.crt_static = cfg.crt_static;
2367 target.musl_root = cfg.musl_root.map(PathBuf::from);
2368 target.musl_libdir = cfg.musl_libdir.map(PathBuf::from);
2369 target.wasi_root = cfg.wasi_root.map(PathBuf::from);
2370 target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from);
2371 target.runner = cfg.runner;
2372 target.sanitizers = cfg.sanitizers;
2373 target.profiler = cfg.profiler;
2374 target.rpath = cfg.rpath;
2375 target.optimized_compiler_builtins = cfg.optimized_compiler_builtins;
2376 target.jemalloc = cfg.jemalloc;
2377
2378 if let Some(ref backends) = cfg.codegen_backends {
2379 let available_backends = ["llvm", "cranelift", "gcc"];
2380
2381 target.codegen_backends = Some(backends.iter().map(|s| {
2382 if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) {
2383 if available_backends.contains(&backend) {
2384 panic!("Invalid value '{s}' for 'target.{triple}.codegen-backends'. Instead, please use '{backend}'.");
2385 } else {
2386 println!("HELP: '{s}' for 'target.{triple}.codegen-backends' might fail. \
2387 Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \
2388 In this case, it would be referred to as '{backend}'.");
2389 }
2390 }
2391
2392 s.clone()
2393 }).collect());
2394 }
2395
2396 target.split_debuginfo = cfg.split_debuginfo.as_ref().map(|v| {
2397 v.parse().unwrap_or_else(|_| {
2398 panic!("invalid value for target.{triple}.split-debuginfo")
2399 })
2400 });
2401
2402 config.target_config.insert(TargetSelection::from_user(&triple), target);
2403 }
2404 }
2405
2406 match ccache {
2407 Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
2408 Some(StringOrBool::Bool(true)) => {
2409 config.ccache = Some("ccache".to_string());
2410 }
2411 Some(StringOrBool::Bool(false)) | None => {}
2412 }
2413
2414 if config.llvm_from_ci {
2415 let triple = &config.build.triple;
2416 let ci_llvm_bin = config.ci_llvm_root().join("bin");
2417 let build_target = config
2418 .target_config
2419 .entry(config.build)
2420 .or_insert_with(|| Target::from_triple(triple));
2421
2422 check_ci_llvm!(build_target.llvm_config);
2423 check_ci_llvm!(build_target.llvm_filecheck);
2424 build_target.llvm_config = Some(ci_llvm_bin.join(exe("llvm-config", config.build)));
2425 build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", config.build)));
2426 }
2427
2428 if let Some(dist) = toml.dist {
2429 let Dist {
2430 sign_folder,
2431 upload_addr,
2432 src_tarball,
2433 compression_formats,
2434 compression_profile,
2435 include_mingw_linker,
2436 vendor,
2437 } = dist;
2438 config.dist_sign_folder = sign_folder.map(PathBuf::from);
2439 config.dist_upload_addr = upload_addr;
2440 config.dist_compression_formats = compression_formats;
2441 set(&mut config.dist_compression_profile, compression_profile);
2442 set(&mut config.rust_dist_src, src_tarball);
2443 set(&mut config.dist_include_mingw_linker, include_mingw_linker);
2444 config.dist_vendor = vendor.unwrap_or_else(|| {
2445 config.rust_info.is_managed_git_subrepository()
2447 || config.rust_info.is_from_tarball()
2448 });
2449 }
2450
2451 if let Some(r) = rustfmt {
2452 *config.initial_rustfmt.borrow_mut() = if r.exists() {
2453 RustfmtState::SystemToolchain(r)
2454 } else {
2455 RustfmtState::Unavailable
2456 };
2457 }
2458
2459 config.llvm_tests = llvm_tests.unwrap_or(false);
2463 config.llvm_enzyme = llvm_enzyme.unwrap_or(false);
2464 config.llvm_offload = llvm_offload.unwrap_or(false);
2465 config.llvm_plugins = llvm_plugins.unwrap_or(false);
2466 config.rust_optimize = optimize.unwrap_or(RustOptimize::Bool(true));
2467
2468 if config.build.triple == "x86_64-unknown-linux-gnu"
2482 && config.hosts == [config.build]
2483 && (config.channel == "dev" || config.channel == "nightly")
2484 {
2485 let no_llvm_config = config
2486 .target_config
2487 .get(&config.build)
2488 .is_some_and(|target_config| target_config.llvm_config.is_none());
2489 let enable_lld = config.llvm_from_ci || no_llvm_config;
2490 config.lld_enabled = lld_enabled.unwrap_or(enable_lld);
2492 } else {
2493 set(&mut config.lld_enabled, lld_enabled);
2494 }
2495
2496 if matches!(config.lld_mode, LldMode::SelfContained)
2497 && !config.lld_enabled
2498 && flags.stage.unwrap_or(0) > 0
2499 {
2500 panic!(
2501 "Trying to use self-contained lld as a linker, but LLD is not being added to the sysroot. Enable it with rust.lld = true."
2502 );
2503 }
2504
2505 if config.lld_enabled && config.is_system_llvm(config.build) {
2506 eprintln!(
2507 "Warning: LLD is enabled when using external llvm-config. LLD will not be built and copied to the sysroot."
2508 );
2509 }
2510
2511 let default_std_features = BTreeSet::from([String::from("panic-unwind")]);
2512 config.rust_std_features = std_features.unwrap_or(default_std_features);
2513
2514 let default = debug == Some(true);
2515 config.rustc_debug_assertions = rustc_debug_assertions.unwrap_or(default);
2516 config.std_debug_assertions = std_debug_assertions.unwrap_or(config.rustc_debug_assertions);
2517 config.tools_debug_assertions =
2518 tools_debug_assertions.unwrap_or(config.rustc_debug_assertions);
2519 config.rust_overflow_checks = overflow_checks.unwrap_or(default);
2520 config.rust_overflow_checks_std =
2521 overflow_checks_std.unwrap_or(config.rust_overflow_checks);
2522
2523 config.rust_debug_logging = debug_logging.unwrap_or(config.rustc_debug_assertions);
2524
2525 let with_defaults = |debuginfo_level_specific: Option<_>| {
2526 debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) {
2527 DebuginfoLevel::Limited
2528 } else {
2529 DebuginfoLevel::None
2530 })
2531 };
2532 config.rust_debuginfo_level_rustc = with_defaults(debuginfo_level_rustc);
2533 config.rust_debuginfo_level_std = with_defaults(debuginfo_level_std);
2534 config.rust_debuginfo_level_tools = with_defaults(debuginfo_level_tools);
2535 config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(DebuginfoLevel::None);
2536 config.optimized_compiler_builtins =
2537 optimized_compiler_builtins.unwrap_or(config.channel != "dev");
2538 config.compiletest_diff_tool = compiletest_diff_tool;
2539 config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true);
2540
2541 let download_rustc = config.download_rustc_commit.is_some();
2542 config.explicit_stage_from_cli = flags.stage.is_some();
2543 config.explicit_stage_from_config = test_stage.is_some()
2544 || build_stage.is_some()
2545 || doc_stage.is_some()
2546 || dist_stage.is_some()
2547 || install_stage.is_some()
2548 || check_stage.is_some()
2549 || bench_stage.is_some();
2550 config.stage = match config.cmd {
2552 Subcommand::Check { .. } => flags.stage.or(check_stage).unwrap_or(0),
2553 Subcommand::Doc { .. } => {
2555 flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 0 })
2556 }
2557 Subcommand::Build => {
2558 flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
2559 }
2560 Subcommand::Test { .. } | Subcommand::Miri { .. } => {
2561 flags.stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
2562 }
2563 Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2),
2564 Subcommand::Dist => flags.stage.or(dist_stage).unwrap_or(2),
2565 Subcommand::Install => flags.stage.or(install_stage).unwrap_or(2),
2566 Subcommand::Perf { .. } => flags.stage.unwrap_or(1),
2567 Subcommand::Clean { .. }
2570 | Subcommand::Clippy { .. }
2571 | Subcommand::Fix
2572 | Subcommand::Run { .. }
2573 | Subcommand::Setup { .. }
2574 | Subcommand::Format { .. }
2575 | Subcommand::Suggest { .. }
2576 | Subcommand::Vendor { .. } => flags.stage.unwrap_or(0),
2577 };
2578
2579 #[cfg(not(test))]
2581 if flags.stage.is_none() && config.is_running_on_ci {
2582 match config.cmd {
2583 Subcommand::Test { .. }
2584 | Subcommand::Miri { .. }
2585 | Subcommand::Doc { .. }
2586 | Subcommand::Build
2587 | Subcommand::Bench { .. }
2588 | Subcommand::Dist
2589 | Subcommand::Install => {
2590 assert_eq!(
2591 config.stage, 2,
2592 "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
2593 config.stage,
2594 );
2595 }
2596 Subcommand::Clean { .. }
2597 | Subcommand::Check { .. }
2598 | Subcommand::Clippy { .. }
2599 | Subcommand::Fix
2600 | Subcommand::Run { .. }
2601 | Subcommand::Setup { .. }
2602 | Subcommand::Format { .. }
2603 | Subcommand::Suggest { .. }
2604 | Subcommand::Vendor { .. }
2605 | Subcommand::Perf { .. } => {}
2606 }
2607 }
2608
2609 config
2610 }
2611
2612 pub fn dry_run(&self) -> bool {
2613 match self.dry_run {
2614 DryRun::Disabled => false,
2615 DryRun::SelfCheck | DryRun::UserSelected => true,
2616 }
2617 }
2618
2619 pub fn is_explicit_stage(&self) -> bool {
2620 self.explicit_stage_from_cli || self.explicit_stage_from_config
2621 }
2622
2623 #[deprecated = "use `Builder::try_run` instead where possible"]
2627 pub(crate) fn try_run(&self, cmd: &mut Command) -> Result<(), ()> {
2628 if self.dry_run() {
2629 return Ok(());
2630 }
2631 self.verbose(|| println!("running: {cmd:?}"));
2632 build_helper::util::try_run(cmd, self.is_verbose())
2633 }
2634
2635 pub(crate) fn test_args(&self) -> Vec<&str> {
2636 let mut test_args = match self.cmd {
2637 Subcommand::Test { ref test_args, .. }
2638 | Subcommand::Bench { ref test_args, .. }
2639 | Subcommand::Miri { ref test_args, .. } => {
2640 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
2641 }
2642 _ => vec![],
2643 };
2644 test_args.extend(self.free_args.iter().map(|s| s.as_str()));
2645 test_args
2646 }
2647
2648 pub(crate) fn args(&self) -> Vec<&str> {
2649 let mut args = match self.cmd {
2650 Subcommand::Run { ref args, .. } => {
2651 args.iter().flat_map(|s| s.split_whitespace()).collect()
2652 }
2653 _ => vec![],
2654 };
2655 args.extend(self.free_args.iter().map(|s| s.as_str()));
2656 args
2657 }
2658
2659 pub(crate) fn read_file_by_commit(&self, file: &Path, commit: &str) -> String {
2661 assert!(
2662 self.rust_info.is_managed_git_subrepository(),
2663 "`Config::read_file_by_commit` is not supported in non-git sources."
2664 );
2665
2666 let mut git = helpers::git(Some(&self.src));
2667 git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap()));
2668 output(git.as_command_mut())
2669 }
2670
2671 pub(crate) fn artifact_version_part(&self, commit: &str) -> String {
2674 let (channel, version) = if self.rust_info.is_managed_git_subrepository() {
2675 let channel =
2676 self.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned();
2677 let version =
2678 self.read_file_by_commit(Path::new("src/version"), commit).trim().to_owned();
2679 (channel, version)
2680 } else {
2681 let channel = fs::read_to_string(self.src.join("src/ci/channel"));
2682 let version = fs::read_to_string(self.src.join("src/version"));
2683 match (channel, version) {
2684 (Ok(channel), Ok(version)) => {
2685 (channel.trim().to_owned(), version.trim().to_owned())
2686 }
2687 (channel, version) => {
2688 let src = self.src.display();
2689 eprintln!("ERROR: failed to determine artifact channel and/or version");
2690 eprintln!(
2691 "HELP: consider using a git checkout or ensure these files are readable"
2692 );
2693 if let Err(channel) = channel {
2694 eprintln!("reading {src}/src/ci/channel failed: {channel:?}");
2695 }
2696 if let Err(version) = version {
2697 eprintln!("reading {src}/src/version failed: {version:?}");
2698 }
2699 panic!();
2700 }
2701 }
2702 };
2703
2704 match channel.as_str() {
2705 "stable" => version,
2706 "beta" => channel,
2707 "nightly" => channel,
2708 other => unreachable!("{:?} is not recognized as a valid channel", other),
2709 }
2710 }
2711
2712 pub fn bindir_relative(&self) -> &Path {
2714 let bindir = &self.bindir;
2715 if bindir.is_absolute() {
2716 if let Some(prefix) = &self.prefix {
2718 if let Ok(stripped) = bindir.strip_prefix(prefix) {
2719 return stripped;
2720 }
2721 }
2722 }
2723 bindir
2724 }
2725
2726 pub fn libdir_relative(&self) -> Option<&Path> {
2728 let libdir = self.libdir.as_ref()?;
2729 if libdir.is_relative() {
2730 Some(libdir)
2731 } else {
2732 libdir.strip_prefix(self.prefix.as_ref()?).ok()
2734 }
2735 }
2736
2737 pub(crate) fn ci_llvm_root(&self) -> PathBuf {
2739 assert!(self.llvm_from_ci);
2740 self.out.join(self.build).join("ci-llvm")
2741 }
2742
2743 pub(crate) fn ci_rustc_dir(&self) -> PathBuf {
2745 assert!(self.download_rustc());
2746 self.out.join(self.build).join("ci-rustc")
2747 }
2748
2749 pub(crate) fn llvm_link_shared(&self) -> bool {
2754 let mut opt = self.llvm_link_shared.get();
2755 if opt.is_none() && self.dry_run() {
2756 return false;
2758 }
2759
2760 let llvm_link_shared = *opt.get_or_insert_with(|| {
2761 if self.llvm_from_ci {
2762 self.maybe_download_ci_llvm();
2763 let ci_llvm = self.ci_llvm_root();
2764 let link_type = t!(
2765 std::fs::read_to_string(ci_llvm.join("link-type.txt")),
2766 format!("CI llvm missing: {}", ci_llvm.display())
2767 );
2768 link_type == "dynamic"
2769 } else {
2770 false
2773 }
2774 });
2775 self.llvm_link_shared.set(opt);
2776 llvm_link_shared
2777 }
2778
2779 pub(crate) fn download_rustc(&self) -> bool {
2781 self.download_rustc_commit().is_some()
2782 }
2783
2784 pub(crate) fn download_rustc_commit(&self) -> Option<&str> {
2785 static DOWNLOAD_RUSTC: OnceLock<Option<String>> = OnceLock::new();
2786 if self.dry_run() && DOWNLOAD_RUSTC.get().is_none() {
2787 return self.download_rustc_commit.as_deref();
2789 }
2790
2791 DOWNLOAD_RUSTC
2792 .get_or_init(|| match &self.download_rustc_commit {
2793 None => None,
2794 Some(commit) => {
2795 self.download_ci_rustc(commit);
2796
2797 if !self.llvm_from_ci {
2801 if self.is_running_on_ci {
2804 println!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled.");
2805 return None;
2806 } else {
2807 panic!("ERROR: LLVM submodule has changes, `download-rustc` can't be used.");
2808 }
2809 }
2810
2811 if let Some(config_path) = &self.config {
2812 let ci_config_toml = match self.get_builder_toml("ci-rustc") {
2813 Ok(ci_config_toml) => ci_config_toml,
2814 Err(e) if e.to_string().contains("unknown field") => {
2815 println!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled.");
2816 println!("HELP: Consider rebasing to a newer commit if available.");
2817 return None;
2818 },
2819 Err(e) => {
2820 eprintln!("ERROR: Failed to parse CI rustc bootstrap.toml: {e}");
2821 exit!(2);
2822 },
2823 };
2824
2825 let current_config_toml = Self::get_toml(config_path).unwrap();
2826
2827 let res = check_incompatible_options_for_ci_rustc(
2830 self.build,
2831 current_config_toml,
2832 ci_config_toml,
2833 );
2834
2835 let disable_ci_rustc_if_incompatible = env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE")
2838 .is_some_and(|s| s == "1" || s == "true");
2839
2840 if disable_ci_rustc_if_incompatible && res.is_err() {
2841 println!("WARNING: download-rustc is disabled with `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` env.");
2842 return None;
2843 }
2844
2845 res.unwrap();
2846 }
2847
2848 Some(commit.clone())
2849 }
2850 })
2851 .as_deref()
2852 }
2853
2854 pub(crate) fn initial_rustfmt(&self) -> Option<PathBuf> {
2855 match &mut *self.initial_rustfmt.borrow_mut() {
2856 RustfmtState::SystemToolchain(p) | RustfmtState::Downloaded(p) => Some(p.clone()),
2857 RustfmtState::Unavailable => None,
2858 r @ RustfmtState::LazyEvaluated => {
2859 if self.dry_run() {
2860 return Some(PathBuf::new());
2861 }
2862 let path = self.maybe_download_rustfmt();
2863 *r = if let Some(p) = &path {
2864 RustfmtState::Downloaded(p.clone())
2865 } else {
2866 RustfmtState::Unavailable
2867 };
2868 path
2869 }
2870 }
2871 }
2872
2873 pub fn verbose(&self, f: impl Fn()) {
2875 if self.is_verbose() {
2876 f()
2877 }
2878 }
2879
2880 pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool {
2881 self.target_config.get(&target).and_then(|t| t.sanitizers).unwrap_or(self.sanitizers)
2882 }
2883
2884 pub fn needs_sanitizer_runtime_built(&self, target: TargetSelection) -> bool {
2885 !target.is_msvc() && self.sanitizers_enabled(target)
2887 }
2888
2889 pub fn any_sanitizers_to_build(&self) -> bool {
2890 self.target_config
2891 .iter()
2892 .any(|(ts, t)| !ts.is_msvc() && t.sanitizers.unwrap_or(self.sanitizers))
2893 }
2894
2895 pub fn profiler_path(&self, target: TargetSelection) -> Option<&str> {
2896 match self.target_config.get(&target)?.profiler.as_ref()? {
2897 StringOrBool::String(s) => Some(s),
2898 StringOrBool::Bool(_) => None,
2899 }
2900 }
2901
2902 pub fn profiler_enabled(&self, target: TargetSelection) -> bool {
2903 self.target_config
2904 .get(&target)
2905 .and_then(|t| t.profiler.as_ref())
2906 .map(StringOrBool::is_string_or_true)
2907 .unwrap_or(self.profiler)
2908 }
2909
2910 pub fn any_profiler_enabled(&self) -> bool {
2911 self.target_config.values().any(|t| matches!(&t.profiler, Some(p) if p.is_string_or_true()))
2912 || self.profiler
2913 }
2914
2915 pub fn rpath_enabled(&self, target: TargetSelection) -> bool {
2916 self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath)
2917 }
2918
2919 pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool {
2920 self.target_config
2921 .get(&target)
2922 .and_then(|t| t.optimized_compiler_builtins)
2923 .unwrap_or(self.optimized_compiler_builtins)
2924 }
2925
2926 pub fn llvm_enabled(&self, target: TargetSelection) -> bool {
2927 self.codegen_backends(target).contains(&"llvm".to_owned())
2928 }
2929
2930 pub fn llvm_libunwind(&self, target: TargetSelection) -> LlvmLibunwind {
2931 self.target_config
2932 .get(&target)
2933 .and_then(|t| t.llvm_libunwind)
2934 .or(self.llvm_libunwind_default)
2935 .unwrap_or(if target.contains("fuchsia") {
2936 LlvmLibunwind::InTree
2937 } else {
2938 LlvmLibunwind::No
2939 })
2940 }
2941
2942 pub fn split_debuginfo(&self, target: TargetSelection) -> SplitDebuginfo {
2943 self.target_config
2944 .get(&target)
2945 .and_then(|t| t.split_debuginfo)
2946 .unwrap_or_else(|| SplitDebuginfo::default_for_platform(target))
2947 }
2948
2949 pub fn submodules(&self) -> bool {
2951 self.submodules.unwrap_or(self.rust_info.is_managed_git_subrepository())
2954 }
2955
2956 pub fn codegen_backends(&self, target: TargetSelection) -> &[String] {
2957 self.target_config
2958 .get(&target)
2959 .and_then(|cfg| cfg.codegen_backends.as_deref())
2960 .unwrap_or(&self.rust_codegen_backends)
2961 }
2962
2963 pub fn jemalloc(&self, target: TargetSelection) -> bool {
2964 self.target_config.get(&target).and_then(|cfg| cfg.jemalloc).unwrap_or(self.jemalloc)
2965 }
2966
2967 pub fn default_codegen_backend(&self, target: TargetSelection) -> Option<String> {
2968 self.codegen_backends(target).first().cloned()
2969 }
2970
2971 pub fn git_config(&self) -> GitConfig<'_> {
2972 GitConfig {
2973 nightly_branch: &self.stage0_metadata.config.nightly_branch,
2974 git_merge_commit_email: &self.stage0_metadata.config.git_merge_commit_email,
2975 }
2976 }
2977
2978 #[cfg_attr(
2988 feature = "tracing",
2989 instrument(
2990 level = "trace",
2991 name = "Config::update_submodule",
2992 skip_all,
2993 fields(relative_path = ?relative_path),
2994 ),
2995 )]
2996 pub(crate) fn update_submodule(&self, relative_path: &str) {
2997 if self.rust_info.is_from_tarball() || !self.submodules() {
2998 return;
2999 }
3000
3001 let absolute_path = self.src.join(relative_path);
3002
3003 if !absolute_path.exists() {
3007 t!(fs::create_dir_all(&absolute_path));
3008 }
3009
3010 if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
3013 && !helpers::dir_is_empty(&absolute_path)
3014 {
3015 return;
3016 }
3017
3018 let submodule_git = || {
3025 let mut cmd = helpers::git(Some(&absolute_path));
3026 cmd.run_always();
3027 cmd
3028 };
3029
3030 let checked_out_hash = output(submodule_git().args(["rev-parse", "HEAD"]).as_command_mut());
3032 let checked_out_hash = checked_out_hash.trim_end();
3033 let recorded = output(
3035 helpers::git(Some(&self.src))
3036 .run_always()
3037 .args(["ls-tree", "HEAD"])
3038 .arg(relative_path)
3039 .as_command_mut(),
3040 );
3041
3042 let actual_hash = recorded
3043 .split_whitespace()
3044 .nth(2)
3045 .unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
3046
3047 if actual_hash == checked_out_hash {
3048 return;
3050 }
3051
3052 println!("Updating submodule {relative_path}");
3053 self.check_run(
3054 helpers::git(Some(&self.src))
3055 .run_always()
3056 .args(["submodule", "-q", "sync"])
3057 .arg(relative_path),
3058 );
3059
3060 let update = |progress: bool| {
3062 let current_branch = output_result(
3065 helpers::git(Some(&self.src))
3066 .allow_failure()
3067 .run_always()
3068 .args(["symbolic-ref", "--short", "HEAD"])
3069 .as_command_mut(),
3070 )
3071 .map(|b| b.trim().to_owned());
3072
3073 let mut git = helpers::git(Some(&self.src)).allow_failure();
3074 git.run_always();
3075 if let Ok(branch) = current_branch {
3076 let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
3079 git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
3080 }
3081 git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
3082 if progress {
3083 git.arg("--progress");
3084 }
3085 git.arg(relative_path);
3086 git
3087 };
3088 if !self.check_run(&mut update(true)) {
3089 self.check_run(&mut update(false));
3090 }
3091
3092 let has_local_modifications = !self.check_run(submodule_git().allow_failure().args([
3095 "diff-index",
3096 "--quiet",
3097 "HEAD",
3098 ]));
3099 if has_local_modifications {
3100 self.check_run(submodule_git().args(["stash", "push"]));
3101 }
3102
3103 self.check_run(submodule_git().args(["reset", "-q", "--hard"]));
3104 self.check_run(submodule_git().args(["clean", "-qdfx"]));
3105
3106 if has_local_modifications {
3107 self.check_run(submodule_git().args(["stash", "pop"]));
3108 }
3109 }
3110
3111 #[cfg(test)]
3112 pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
3113
3114 #[cfg(not(test))]
3116 pub fn check_stage0_version(&self, program_path: &Path, component_name: &'static str) {
3117 use build_helper::util::fail;
3118
3119 if self.dry_run() {
3120 return;
3121 }
3122
3123 let stage0_output = output(Command::new(program_path).arg("--version"));
3124 let mut stage0_output = stage0_output.lines().next().unwrap().split(' ');
3125
3126 let stage0_name = stage0_output.next().unwrap();
3127 if stage0_name != component_name {
3128 fail(&format!(
3129 "Expected to find {component_name} at {} but it claims to be {stage0_name}",
3130 program_path.display()
3131 ));
3132 }
3133
3134 let stage0_version =
3135 semver::Version::parse(stage0_output.next().unwrap().split('-').next().unwrap().trim())
3136 .unwrap();
3137 let source_version = semver::Version::parse(
3138 fs::read_to_string(self.src.join("src/version")).unwrap().trim(),
3139 )
3140 .unwrap();
3141 if !(source_version == stage0_version
3142 || (source_version.major == stage0_version.major
3143 && (source_version.minor == stage0_version.minor
3144 || source_version.minor == stage0_version.minor + 1)))
3145 {
3146 let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1);
3147 fail(&format!(
3148 "Unexpected {component_name} version: {stage0_version}, we should use {prev_version}/{source_version} to build source with {source_version}"
3149 ));
3150 }
3151 }
3152
3153 fn download_ci_rustc_commit(
3155 &self,
3156 download_rustc: Option<StringOrBool>,
3157 debug_assertions_requested: bool,
3158 llvm_assertions: bool,
3159 ) -> Option<String> {
3160 if !is_download_ci_available(&self.build.triple, llvm_assertions) {
3161 return None;
3162 }
3163
3164 let if_unchanged = match download_rustc {
3166 None | Some(StringOrBool::Bool(false)) => return None,
3172 Some(StringOrBool::Bool(true)) => false,
3173 Some(StringOrBool::String(s)) if s == "if-unchanged" => {
3174 if !self.rust_info.is_managed_git_subrepository() {
3175 println!(
3176 "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources."
3177 );
3178 crate::exit!(1);
3179 }
3180
3181 true
3182 }
3183 Some(StringOrBool::String(other)) => {
3184 panic!("unrecognized option for download-rustc: {other}")
3185 }
3186 };
3187
3188 let mut allowed_paths = RUSTC_IF_UNCHANGED_ALLOWED_PATHS.to_vec();
3190
3191 if !self.is_running_on_ci {
3199 allowed_paths.push(":!library");
3200 }
3201
3202 let commit = if self.rust_info.is_managed_git_subrepository() {
3203 let freshness = self.check_path_modifications(&allowed_paths);
3206 self.verbose(|| {
3207 eprintln!("rustc freshness: {freshness:?}");
3208 });
3209 match freshness {
3210 PathFreshness::LastModifiedUpstream { upstream } => upstream,
3211 PathFreshness::HasLocalModifications { upstream } => {
3212 if if_unchanged {
3213 return None;
3214 }
3215
3216 if self.is_running_on_ci {
3217 eprintln!("CI rustc commit matches with HEAD and we are in CI.");
3218 eprintln!(
3219 "`rustc.download-ci` functionality will be skipped as artifacts are not available."
3220 );
3221 return None;
3222 }
3223
3224 upstream
3225 }
3226 PathFreshness::MissingUpstream => {
3227 eprintln!("No upstream commit found");
3228 return None;
3229 }
3230 }
3231 } else {
3232 channel::read_commit_info_file(&self.src)
3233 .map(|info| info.sha.trim().to_owned())
3234 .expect("git-commit-info is missing in the project root")
3235 };
3236
3237 if debug_assertions_requested {
3238 eprintln!(
3239 "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \
3240 rustc is not currently built with debug assertions."
3241 );
3242 return None;
3243 }
3244
3245 Some(commit)
3246 }
3247
3248 fn parse_download_ci_llvm(
3249 &self,
3250 download_ci_llvm: Option<StringOrBool>,
3251 asserts: bool,
3252 ) -> bool {
3253 let default = if self.is_running_on_ci {
3256 StringOrBool::String("if-unchanged".to_string())
3257 } else {
3258 StringOrBool::Bool(true)
3259 };
3260 let download_ci_llvm = download_ci_llvm.unwrap_or(default);
3261
3262 let if_unchanged = || {
3263 if self.rust_info.is_from_tarball() {
3264 println!("ERROR: 'if-unchanged' is only compatible with Git managed sources.");
3266 crate::exit!(1);
3267 }
3268
3269 #[cfg(not(test))]
3271 self.update_submodule("src/llvm-project");
3272
3273 let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS);
3275
3276 if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) }
3278 };
3279
3280 match download_ci_llvm {
3281 StringOrBool::Bool(b) => {
3282 if !b && self.download_rustc_commit.is_some() {
3283 panic!(
3284 "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`."
3285 );
3286 }
3287
3288 if b && self.is_running_on_ci {
3289 panic!(
3291 "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead."
3292 );
3293 }
3294
3295 b && llvm::is_ci_llvm_available_for_target(self, asserts)
3297 }
3298 StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(),
3299 StringOrBool::String(other) => {
3300 panic!("unrecognized option for download-ci-llvm: {:?}", other)
3301 }
3302 }
3303 }
3304
3305 pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool {
3307 match self.check_path_modifications(paths) {
3308 PathFreshness::LastModifiedUpstream { .. } => false,
3309 PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true,
3310 }
3311 }
3312
3313 pub fn check_path_modifications(&self, paths: &[&'static str]) -> PathFreshness {
3315 self.path_modification_cache
3321 .lock()
3322 .unwrap()
3323 .entry(paths.to_vec())
3324 .or_insert_with(|| {
3325 check_path_modifications(&self.src, &self.git_config(), paths, CiEnv::current())
3326 .unwrap()
3327 })
3328 .clone()
3329 }
3330
3331 pub fn is_host_target(&self, target: TargetSelection) -> bool {
3333 self.build == target
3334 }
3335
3336 pub fn is_system_llvm(&self, target: TargetSelection) -> bool {
3341 match self.target_config.get(&target) {
3342 Some(Target { llvm_config: Some(_), .. }) => {
3343 let ci_llvm = self.llvm_from_ci && self.is_host_target(target);
3344 !ci_llvm
3345 }
3346 Some(Target { llvm_config: None, .. }) => false,
3348 None => false,
3349 }
3350 }
3351
3352 pub fn is_rust_llvm(&self, target: TargetSelection) -> bool {
3356 match self.target_config.get(&target) {
3357 Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched,
3361 _ => !self.is_system_llvm(target),
3364 }
3365 }
3366
3367 pub fn ci_env(&self) -> CiEnv {
3368 if self.is_running_on_ci { CiEnv::GitHubActions } else { CiEnv::None }
3369 }
3370}
3371
3372#[cfg(not(test))]
3375pub(crate) fn check_incompatible_options_for_ci_llvm(
3376 current_config_toml: TomlConfig,
3377 ci_config_toml: TomlConfig,
3378) -> Result<(), String> {
3379 macro_rules! err {
3380 ($current:expr, $expected:expr) => {
3381 if let Some(current) = &$current {
3382 if Some(current) != $expected.as_ref() {
3383 return Err(format!(
3384 "ERROR: Setting `llvm.{}` is incompatible with `llvm.download-ci-llvm`. \
3385 Current value: {:?}, Expected value(s): {}{:?}",
3386 stringify!($expected).replace("_", "-"),
3387 $current,
3388 if $expected.is_some() { "None/" } else { "" },
3389 $expected,
3390 ));
3391 };
3392 };
3393 };
3394 }
3395
3396 macro_rules! warn {
3397 ($current:expr, $expected:expr) => {
3398 if let Some(current) = &$current {
3399 if Some(current) != $expected.as_ref() {
3400 println!(
3401 "WARNING: `llvm.{}` has no effect with `llvm.download-ci-llvm`. \
3402 Current value: {:?}, Expected value(s): {}{:?}",
3403 stringify!($expected).replace("_", "-"),
3404 $current,
3405 if $expected.is_some() { "None/" } else { "" },
3406 $expected,
3407 );
3408 };
3409 };
3410 };
3411 }
3412
3413 let (Some(current_llvm_config), Some(ci_llvm_config)) =
3414 (current_config_toml.llvm, ci_config_toml.llvm)
3415 else {
3416 return Ok(());
3417 };
3418
3419 let Llvm {
3420 optimize,
3421 thin_lto,
3422 release_debuginfo,
3423 assertions: _,
3424 tests: _,
3425 plugins,
3426 ccache: _,
3427 static_libstdcpp: _,
3428 libzstd,
3429 ninja: _,
3430 targets,
3431 experimental_targets,
3432 link_jobs: _,
3433 link_shared: _,
3434 version_suffix,
3435 clang_cl,
3436 cflags,
3437 cxxflags,
3438 ldflags,
3439 use_libcxx,
3440 use_linker,
3441 allow_old_toolchain,
3442 offload,
3443 polly,
3444 clang,
3445 enable_warnings,
3446 download_ci_llvm: _,
3447 build_config,
3448 enzyme,
3449 } = ci_llvm_config;
3450
3451 err!(current_llvm_config.optimize, optimize);
3452 err!(current_llvm_config.thin_lto, thin_lto);
3453 err!(current_llvm_config.release_debuginfo, release_debuginfo);
3454 err!(current_llvm_config.libzstd, libzstd);
3455 err!(current_llvm_config.targets, targets);
3456 err!(current_llvm_config.experimental_targets, experimental_targets);
3457 err!(current_llvm_config.clang_cl, clang_cl);
3458 err!(current_llvm_config.version_suffix, version_suffix);
3459 err!(current_llvm_config.cflags, cflags);
3460 err!(current_llvm_config.cxxflags, cxxflags);
3461 err!(current_llvm_config.ldflags, ldflags);
3462 err!(current_llvm_config.use_libcxx, use_libcxx);
3463 err!(current_llvm_config.use_linker, use_linker);
3464 err!(current_llvm_config.allow_old_toolchain, allow_old_toolchain);
3465 err!(current_llvm_config.offload, offload);
3466 err!(current_llvm_config.polly, polly);
3467 err!(current_llvm_config.clang, clang);
3468 err!(current_llvm_config.build_config, build_config);
3469 err!(current_llvm_config.plugins, plugins);
3470 err!(current_llvm_config.enzyme, enzyme);
3471
3472 warn!(current_llvm_config.enable_warnings, enable_warnings);
3473
3474 Ok(())
3475}
3476
3477fn check_incompatible_options_for_ci_rustc(
3480 host: TargetSelection,
3481 current_config_toml: TomlConfig,
3482 ci_config_toml: TomlConfig,
3483) -> Result<(), String> {
3484 macro_rules! err {
3485 ($current:expr, $expected:expr, $config_section:expr) => {
3486 if let Some(current) = &$current {
3487 if Some(current) != $expected.as_ref() {
3488 return Err(format!(
3489 "ERROR: Setting `{}` is incompatible with `rust.download-rustc`. \
3490 Current value: {:?}, Expected value(s): {}{:?}",
3491 format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
3492 $current,
3493 if $expected.is_some() { "None/" } else { "" },
3494 $expected,
3495 ));
3496 };
3497 };
3498 };
3499 }
3500
3501 macro_rules! warn {
3502 ($current:expr, $expected:expr, $config_section:expr) => {
3503 if let Some(current) = &$current {
3504 if Some(current) != $expected.as_ref() {
3505 println!(
3506 "WARNING: `{}` has no effect with `rust.download-rustc`. \
3507 Current value: {:?}, Expected value(s): {}{:?}",
3508 format!("{}.{}", $config_section, stringify!($expected).replace("_", "-")),
3509 $current,
3510 if $expected.is_some() { "None/" } else { "" },
3511 $expected,
3512 );
3513 };
3514 };
3515 };
3516 }
3517
3518 let current_profiler = current_config_toml.build.as_ref().and_then(|b| b.profiler);
3519 let profiler = ci_config_toml.build.as_ref().and_then(|b| b.profiler);
3520 err!(current_profiler, profiler, "build");
3521
3522 let current_optimized_compiler_builtins =
3523 current_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
3524 let optimized_compiler_builtins =
3525 ci_config_toml.build.as_ref().and_then(|b| b.optimized_compiler_builtins);
3526 err!(current_optimized_compiler_builtins, optimized_compiler_builtins, "build");
3527
3528 let host_str = host.to_string();
3531 if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str)) {
3532 if current_cfg.profiler.is_some() {
3533 let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str));
3534 let ci_cfg = ci_target_toml.ok_or(format!(
3535 "Target specific config for '{host_str}' is not present for CI-rustc"
3536 ))?;
3537
3538 let profiler = &ci_cfg.profiler;
3539 err!(current_cfg.profiler, profiler, "build");
3540
3541 let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins;
3542 err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build");
3543 }
3544 }
3545
3546 let (Some(current_rust_config), Some(ci_rust_config)) =
3547 (current_config_toml.rust, ci_config_toml.rust)
3548 else {
3549 return Ok(());
3550 };
3551
3552 let Rust {
3553 optimize,
3555 randomize_layout,
3556 debug_logging,
3557 debuginfo_level_rustc,
3558 llvm_tools,
3559 llvm_bitcode_linker,
3560 lto,
3561 stack_protector,
3562 strip,
3563 lld_mode,
3564 jemalloc,
3565 rpath,
3566 channel,
3567 description,
3568 incremental,
3569 default_linker,
3570 std_features,
3571
3572 debug: _,
3574 codegen_units: _,
3575 codegen_units_std: _,
3576 rustc_debug_assertions: _,
3577 std_debug_assertions: _,
3578 tools_debug_assertions: _,
3579 overflow_checks: _,
3580 overflow_checks_std: _,
3581 debuginfo_level: _,
3582 debuginfo_level_std: _,
3583 debuginfo_level_tools: _,
3584 debuginfo_level_tests: _,
3585 backtrace: _,
3586 musl_root: _,
3587 verbose_tests: _,
3588 optimize_tests: _,
3589 codegen_tests: _,
3590 omit_git_hash: _,
3591 dist_src: _,
3592 save_toolstates: _,
3593 codegen_backends: _,
3594 lld: _,
3595 deny_warnings: _,
3596 backtrace_on_ice: _,
3597 verify_llvm_ir: _,
3598 thin_lto_import_instr_limit: _,
3599 remap_debuginfo: _,
3600 test_compare_mode: _,
3601 llvm_libunwind: _,
3602 control_flow_guard: _,
3603 ehcont_guard: _,
3604 new_symbol_mangling: _,
3605 profile_generate: _,
3606 profile_use: _,
3607 download_rustc: _,
3608 validate_mir_opts: _,
3609 frame_pointers: _,
3610 } = ci_rust_config;
3611
3612 err!(current_rust_config.optimize, optimize, "rust");
3620 err!(current_rust_config.randomize_layout, randomize_layout, "rust");
3621 err!(current_rust_config.debug_logging, debug_logging, "rust");
3622 err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust");
3623 err!(current_rust_config.rpath, rpath, "rust");
3624 err!(current_rust_config.strip, strip, "rust");
3625 err!(current_rust_config.lld_mode, lld_mode, "rust");
3626 err!(current_rust_config.llvm_tools, llvm_tools, "rust");
3627 err!(current_rust_config.llvm_bitcode_linker, llvm_bitcode_linker, "rust");
3628 err!(current_rust_config.jemalloc, jemalloc, "rust");
3629 err!(current_rust_config.default_linker, default_linker, "rust");
3630 err!(current_rust_config.stack_protector, stack_protector, "rust");
3631 err!(current_rust_config.lto, lto, "rust");
3632 err!(current_rust_config.std_features, std_features, "rust");
3633
3634 warn!(current_rust_config.channel, channel, "rust");
3635 warn!(current_rust_config.description, description, "rust");
3636 warn!(current_rust_config.incremental, incremental, "rust");
3637
3638 Ok(())
3639}
3640
3641fn set<T>(field: &mut T, val: Option<T>) {
3642 if let Some(v) = val {
3643 *field = v;
3644 }
3645}
3646
3647fn threads_from_config(v: u32) -> u32 {
3648 match v {
3649 0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
3650 n => n,
3651 }
3652}