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