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