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