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