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