1use std::cell::Cell;
18use std::collections::{BTreeSet, HashMap, HashSet};
19use std::io::IsTerminal;
20use std::path::{Path, PathBuf, absolute};
21use std::str::FromStr;
22use std::sync::{Arc, Mutex};
23use std::{cmp, env, fs};
24
25use build_helper::ci::CiEnv;
26use build_helper::exit;
27use build_helper::git::{GitConfig, PathFreshness, check_path_modifications};
28use serde::Deserialize;
29#[cfg(feature = "tracing")]
30use tracing::{instrument, span};
31
32use crate::core::build_steps::llvm;
33use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS;
34pub use crate::core::config::flags::Subcommand;
35use crate::core::config::flags::{Color, Flags};
36use crate::core::config::target_selection::TargetSelectionList;
37use crate::core::config::toml::TomlConfig;
38use crate::core::config::toml::build::{Build, Tool};
39use crate::core::config::toml::change_id::ChangeId;
40use crate::core::config::toml::rust::{
41 LldMode, RustOptimize, check_incompatible_options_for_ci_rustc,
42};
43use crate::core::config::toml::target::Target;
44use crate::core::config::{
45 DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo,
46 StringOrBool, set, threads_from_config,
47};
48use crate::core::download::{
49 DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt,
50};
51use crate::utils::channel;
52use crate::utils::exec::{ExecutionContext, command};
53use crate::utils::helpers::{exe, get_host_target};
54use crate::{CodegenBackendKind, GitInfo, OnceLock, TargetSelection, check_ci_llvm, helpers, t};
55
56#[rustfmt::skip] pub const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[
69 ":!library",
70 ":!src/tools",
71 ":!src/librustdoc",
72 ":!src/rustdoc-json-types",
73 ":!tests",
74 ":!triagebot.toml",
75];
76
77#[derive(Default, Clone)]
86pub struct Config {
87 pub change_id: Option<ChangeId>,
88 pub bypass_bootstrap_lock: bool,
89 pub ccache: Option<String>,
90 pub ninja_in_file: bool,
92 pub verbose: usize,
93 pub submodules: Option<bool>,
94 pub compiler_docs: bool,
95 pub library_docs_private_items: bool,
96 pub docs_minification: bool,
97 pub docs: bool,
98 pub locked_deps: bool,
99 pub vendor: bool,
100 pub target_config: HashMap<TargetSelection, Target>,
101 pub full_bootstrap: bool,
102 pub bootstrap_cache_path: Option<PathBuf>,
103 pub extended: bool,
104 pub tools: Option<HashSet<String>>,
105 pub tool: HashMap<String, Tool>,
108 pub sanitizers: bool,
109 pub profiler: bool,
110 pub omit_git_hash: bool,
111 pub skip: Vec<PathBuf>,
112 pub include_default_paths: bool,
113 pub rustc_error_format: Option<String>,
114 pub json_output: bool,
115 pub compile_time_deps: bool,
116 pub test_compare_mode: bool,
117 pub color: Color,
118 pub patch_binaries_for_nix: Option<bool>,
119 pub stage0_metadata: build_helper::stage0_parser::Stage0,
120 pub android_ndk: Option<PathBuf>,
121 pub optimized_compiler_builtins: bool,
123
124 pub stdout_is_tty: bool,
125 pub stderr_is_tty: bool,
126
127 pub on_fail: Option<String>,
128 pub explicit_stage_from_cli: bool,
129 pub explicit_stage_from_config: bool,
130 pub stage: u32,
131 pub keep_stage: Vec<u32>,
132 pub keep_stage_std: Vec<u32>,
133 pub src: PathBuf,
134 pub config: Option<PathBuf>,
136 pub jobs: Option<u32>,
137 pub cmd: Subcommand,
138 pub incremental: bool,
139 pub dump_bootstrap_shims: bool,
140 pub free_args: Vec<String>,
143
144 pub download_rustc_commit: Option<String>,
146
147 pub deny_warnings: bool,
148 pub backtrace_on_ice: bool,
149
150 pub llvm_assertions: bool,
152 pub llvm_tests: bool,
153 pub llvm_enzyme: bool,
154 pub llvm_offload: bool,
155 pub llvm_plugins: bool,
156 pub llvm_optimize: bool,
157 pub llvm_thin_lto: bool,
158 pub llvm_release_debuginfo: bool,
159 pub llvm_static_stdcpp: bool,
160 pub llvm_libzstd: bool,
161 pub llvm_link_shared: Cell<Option<bool>>,
162 pub llvm_clang_cl: Option<String>,
163 pub llvm_targets: Option<String>,
164 pub llvm_experimental_targets: Option<String>,
165 pub llvm_link_jobs: Option<u32>,
166 pub llvm_version_suffix: Option<String>,
167 pub llvm_use_linker: Option<String>,
168 pub llvm_allow_old_toolchain: bool,
169 pub llvm_polly: bool,
170 pub llvm_clang: bool,
171 pub llvm_enable_warnings: bool,
172 pub llvm_from_ci: bool,
173 pub llvm_build_config: HashMap<String, String>,
174
175 pub lld_mode: LldMode,
176 pub lld_enabled: bool,
177 pub llvm_tools_enabled: bool,
178 pub llvm_bitcode_linker_enabled: bool,
179
180 pub llvm_cflags: Option<String>,
181 pub llvm_cxxflags: Option<String>,
182 pub llvm_ldflags: Option<String>,
183 pub llvm_use_libcxx: bool,
184
185 pub gcc_ci_mode: GccCiMode,
187
188 pub rust_optimize: RustOptimize,
190 pub rust_codegen_units: Option<u32>,
191 pub rust_codegen_units_std: Option<u32>,
192
193 pub rustc_debug_assertions: bool,
194 pub std_debug_assertions: bool,
195 pub tools_debug_assertions: bool,
196
197 pub rust_overflow_checks: bool,
198 pub rust_overflow_checks_std: bool,
199 pub rust_debug_logging: bool,
200 pub rust_debuginfo_level_rustc: DebuginfoLevel,
201 pub rust_debuginfo_level_std: DebuginfoLevel,
202 pub rust_debuginfo_level_tools: DebuginfoLevel,
203 pub rust_debuginfo_level_tests: DebuginfoLevel,
204 pub rust_rpath: bool,
205 pub rust_strip: bool,
206 pub rust_frame_pointers: bool,
207 pub rust_stack_protector: Option<String>,
208 pub rustc_default_linker: Option<String>,
209 pub rust_optimize_tests: bool,
210 pub rust_dist_src: bool,
211 pub rust_codegen_backends: Vec<CodegenBackendKind>,
212 pub rust_verify_llvm_ir: bool,
213 pub rust_thin_lto_import_instr_limit: Option<u32>,
214 pub rust_randomize_layout: bool,
215 pub rust_remap_debuginfo: bool,
216 pub rust_new_symbol_mangling: Option<bool>,
217 pub rust_profile_use: Option<String>,
218 pub rust_profile_generate: Option<String>,
219 pub rust_lto: RustcLto,
220 pub rust_validate_mir_opts: Option<u32>,
221 pub rust_std_features: BTreeSet<String>,
222 pub llvm_profile_use: Option<String>,
223 pub llvm_profile_generate: bool,
224 pub llvm_libunwind_default: Option<LlvmLibunwind>,
225 pub enable_bolt_settings: bool,
226
227 pub reproducible_artifacts: Vec<String>,
228
229 pub host_target: TargetSelection,
230 pub hosts: Vec<TargetSelection>,
231 pub targets: Vec<TargetSelection>,
232 pub local_rebuild: bool,
233 pub jemalloc: bool,
234 pub control_flow_guard: bool,
235 pub ehcont_guard: bool,
236
237 pub dist_sign_folder: Option<PathBuf>,
239 pub dist_upload_addr: Option<String>,
240 pub dist_compression_formats: Option<Vec<String>>,
241 pub dist_compression_profile: String,
242 pub dist_include_mingw_linker: bool,
243 pub dist_vendor: bool,
244
245 pub backtrace: bool, pub low_priority: bool,
250 pub channel: String,
251 pub description: Option<String>,
252 pub verbose_tests: bool,
253 pub save_toolstates: Option<PathBuf>,
254 pub print_step_timings: bool,
255 pub print_step_rusage: bool,
256
257 pub musl_root: Option<PathBuf>,
259 pub prefix: Option<PathBuf>,
260 pub sysconfdir: Option<PathBuf>,
261 pub datadir: Option<PathBuf>,
262 pub docdir: Option<PathBuf>,
263 pub bindir: PathBuf,
264 pub libdir: Option<PathBuf>,
265 pub mandir: Option<PathBuf>,
266 pub codegen_tests: bool,
267 pub nodejs: Option<PathBuf>,
268 pub npm: Option<PathBuf>,
269 pub gdb: Option<PathBuf>,
270 pub lldb: Option<PathBuf>,
271 pub python: Option<PathBuf>,
272 pub reuse: Option<PathBuf>,
273 pub cargo_native_static: bool,
274 pub configure_args: Vec<String>,
275 pub out: PathBuf,
276 pub rust_info: channel::GitInfo,
277
278 pub cargo_info: channel::GitInfo,
279 pub rust_analyzer_info: channel::GitInfo,
280 pub clippy_info: channel::GitInfo,
281 pub miri_info: channel::GitInfo,
282 pub rustfmt_info: channel::GitInfo,
283 pub enzyme_info: channel::GitInfo,
284 pub in_tree_llvm_info: channel::GitInfo,
285 pub in_tree_gcc_info: channel::GitInfo,
286
287 pub initial_cargo: PathBuf,
289 pub initial_rustc: PathBuf,
290 pub initial_cargo_clippy: Option<PathBuf>,
291 pub initial_sysroot: PathBuf,
292 pub initial_rustfmt: Option<PathBuf>,
293
294 pub paths: Vec<PathBuf>,
297
298 pub compiletest_diff_tool: Option<String>,
300
301 pub compiletest_allow_stage0: bool,
307
308 pub compiletest_use_stage0_libtest: bool,
310
311 pub tidy_extra_checks: Option<String>,
313 pub is_running_on_ci: bool,
314
315 pub path_modification_cache: Arc<Mutex<HashMap<Vec<&'static str>, PathFreshness>>>,
317
318 pub skip_std_check_if_no_download_rustc: bool,
322
323 pub exec_ctx: ExecutionContext,
324}
325
326impl Config {
327 #[cfg_attr(
328 feature = "tracing",
329 instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::default_opts")
330 )]
331 pub fn default_opts() -> Config {
332 #[cfg(feature = "tracing")]
333 span!(target: "CONFIG_HANDLING", tracing::Level::TRACE, "constructing default config");
334
335 Config {
336 bypass_bootstrap_lock: false,
337 llvm_optimize: true,
338 ninja_in_file: true,
339 llvm_static_stdcpp: false,
340 llvm_libzstd: false,
341 backtrace: true,
342 rust_optimize: RustOptimize::Bool(true),
343 rust_optimize_tests: true,
344 rust_randomize_layout: false,
345 submodules: None,
346 docs: true,
347 docs_minification: true,
348 rust_rpath: true,
349 rust_strip: false,
350 channel: "dev".to_string(),
351 codegen_tests: true,
352 rust_dist_src: true,
353 rust_codegen_backends: vec![CodegenBackendKind::Llvm],
354 deny_warnings: true,
355 bindir: "bin".into(),
356 dist_include_mingw_linker: true,
357 dist_compression_profile: "fast".into(),
358
359 stdout_is_tty: std::io::stdout().is_terminal(),
360 stderr_is_tty: std::io::stderr().is_terminal(),
361
362 host_target: get_host_target(),
364
365 src: {
366 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
367 manifest_dir.parent().unwrap().parent().unwrap().to_owned()
369 },
370 out: PathBuf::from("build"),
371
372 llvm_tools_enabled: true,
375
376 ..Default::default()
377 }
378 }
379
380 pub fn set_dry_run(&mut self, dry_run: DryRun) {
381 self.exec_ctx.set_dry_run(dry_run);
382 }
383
384 pub fn get_dry_run(&self) -> &DryRun {
385 self.exec_ctx.get_dry_run()
386 }
387
388 #[cfg_attr(
389 feature = "tracing",
390 instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::parse", skip_all)
391 )]
392 pub fn parse(flags: Flags) -> Config {
393 Self::parse_inner(flags, Self::get_toml)
394 }
395
396 #[cfg_attr(
397 feature = "tracing",
398 instrument(
399 target = "CONFIG_HANDLING",
400 level = "trace",
401 name = "Config::parse_inner",
402 skip_all
403 )
404 )]
405 pub(crate) fn parse_inner(
406 flags: Flags,
407 get_toml: impl Fn(&Path) -> Result<TomlConfig, toml::de::Error>,
408 ) -> Config {
409 let Flags {
413 cmd: flags_cmd,
414 verbose: flags_verbose,
415 incremental: flags_incremental,
416 config: flags_config,
417 build_dir: flags_build_dir,
418 build: flags_build,
419 host: flags_host,
420 target: flags_target,
421 exclude: flags_exclude,
422 skip: flags_skip,
423 include_default_paths: flags_include_default_paths,
424 rustc_error_format: flags_rustc_error_format,
425 on_fail: flags_on_fail,
426 dry_run: flags_dry_run,
427 dump_bootstrap_shims: flags_dump_bootstrap_shims,
428 stage: flags_stage,
429 keep_stage: flags_keep_stage,
430 keep_stage_std: flags_keep_stage_std,
431 src: flags_src,
432 jobs: flags_jobs,
433 warnings: flags_warnings,
434 json_output: flags_json_output,
435 compile_time_deps: flags_compile_time_deps,
436 color: flags_color,
437 bypass_bootstrap_lock: flags_bypass_bootstrap_lock,
438 rust_profile_generate: flags_rust_profile_generate,
439 rust_profile_use: flags_rust_profile_use,
440 llvm_profile_use: flags_llvm_profile_use,
441 llvm_profile_generate: flags_llvm_profile_generate,
442 enable_bolt_settings: flags_enable_bolt_settings,
443 skip_stage0_validation: flags_skip_stage0_validation,
444 reproducible_artifact: flags_reproducible_artifact,
445 paths: mut flags_paths,
446 set: flags_set,
447 free_args: mut flags_free_args,
448 ci: flags_ci,
449 skip_std_check_if_no_download_rustc: flags_skip_std_check_if_no_download_rustc,
450 } = flags;
451
452 let mut config = Config::default_opts();
453 let mut exec_ctx = ExecutionContext::new();
454 exec_ctx.set_verbose(flags_verbose);
455 exec_ctx.set_fail_fast(flags_cmd.fail_fast());
456
457 config.exec_ctx = exec_ctx;
458
459 config.paths = std::mem::take(&mut flags_paths);
461
462 #[cfg(feature = "tracing")]
463 span!(
464 target: "CONFIG_HANDLING",
465 tracing::Level::TRACE,
466 "collecting paths and path exclusions",
467 "flags.paths" = ?flags_paths,
468 "flags.skip" = ?flags_skip,
469 "flags.exclude" = ?flags_exclude
470 );
471
472 #[cfg(feature = "tracing")]
473 span!(
474 target: "CONFIG_HANDLING",
475 tracing::Level::TRACE,
476 "normalizing and combining `flag.skip`/`flag.exclude` paths",
477 "config.skip" = ?config.skip,
478 );
479
480 config.include_default_paths = flags_include_default_paths;
481 config.rustc_error_format = flags_rustc_error_format;
482 config.json_output = flags_json_output;
483 config.compile_time_deps = flags_compile_time_deps;
484 config.on_fail = flags_on_fail;
485 config.cmd = flags_cmd;
486 config.incremental = flags_incremental;
487 config.set_dry_run(if flags_dry_run { DryRun::UserSelected } else { DryRun::Disabled });
488 config.dump_bootstrap_shims = flags_dump_bootstrap_shims;
489 config.keep_stage = flags_keep_stage;
490 config.keep_stage_std = flags_keep_stage_std;
491 config.color = flags_color;
492 config.free_args = std::mem::take(&mut flags_free_args);
493 config.llvm_profile_use = flags_llvm_profile_use;
494 config.llvm_profile_generate = flags_llvm_profile_generate;
495 config.enable_bolt_settings = flags_enable_bolt_settings;
496 config.bypass_bootstrap_lock = flags_bypass_bootstrap_lock;
497 config.is_running_on_ci = flags_ci.unwrap_or(CiEnv::is_ci());
498 config.skip_std_check_if_no_download_rustc = flags_skip_std_check_if_no_download_rustc;
499
500 if let Some(src) = flags_src {
503 config.src = src
504 } else {
505 let mut cmd = helpers::git(None);
508 cmd.arg("rev-parse").arg("--show-cdup");
516 let output = cmd.allow_failure().run_capture_stdout(&config);
518 if output.is_success() {
519 let git_root_relative = output.stdout();
520 let git_root = env::current_dir()
523 .unwrap()
524 .join(PathBuf::from(git_root_relative.trim()))
525 .canonicalize()
526 .unwrap();
527 let s = git_root.to_str().unwrap();
528
529 let git_root = match s.strip_prefix("\\\\?\\") {
531 Some(p) => PathBuf::from(p),
532 None => git_root,
533 };
534 if git_root.join("src").join("stage0").exists() {
541 config.src = git_root;
542 }
543 } else {
544 }
547 }
548
549 if cfg!(test) {
550 config.out = Path::new(
552 &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"),
553 )
554 .parent()
555 .unwrap()
556 .to_path_buf();
557 }
558
559 config.stage0_metadata = build_helper::stage0_parser::parse_stage0_file();
560
561 let toml_path = flags_config
569 .clone()
570 .or_else(|| env::var_os("RUST_BOOTSTRAP_CONFIG").map(PathBuf::from));
571 let using_default_path = toml_path.is_none();
572 let mut toml_path = toml_path.unwrap_or_else(|| PathBuf::from("bootstrap.toml"));
573
574 if using_default_path && !toml_path.exists() {
575 toml_path = config.src.join(PathBuf::from("bootstrap.toml"));
576 if !toml_path.exists() {
577 toml_path = PathBuf::from("config.toml");
578 if !toml_path.exists() {
579 toml_path = config.src.join(PathBuf::from("config.toml"));
580 }
581 }
582 }
583
584 let mut toml = if !using_default_path || toml_path.exists() {
587 config.config = Some(if cfg!(not(test)) {
588 toml_path = toml_path.canonicalize().unwrap();
589 toml_path.clone()
590 } else {
591 toml_path.clone()
592 });
593 get_toml(&toml_path).unwrap_or_else(|e| {
594 eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
595 exit!(2);
596 })
597 } else {
598 config.config = None;
599 TomlConfig::default()
600 };
601
602 if cfg!(test) {
603 let build = toml.build.get_or_insert_with(Default::default);
609 build.rustc = build.rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into()));
610 build.cargo = build.cargo.take().or(std::env::var_os("CARGO").map(|p| p.into()));
611 }
612
613 if config.git_info(false, &config.src).is_from_tarball() && toml.profile.is_none() {
614 toml.profile = Some("dist".into());
615 }
616
617 for include_path in toml.include.clone().unwrap_or_default().iter().rev() {
623 let include_path = toml_path.parent().unwrap().join(include_path);
624
625 let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
626 eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
627 exit!(2);
628 });
629 toml.merge(
630 Some(include_path),
631 &mut Default::default(),
632 included_toml,
633 ReplaceOpt::IgnoreDuplicate,
634 );
635 }
636
637 if let Some(include) = &toml.profile {
638 let profile_aliases = HashMap::from([("user", "dist")]);
642 let include = match profile_aliases.get(include.as_str()) {
643 Some(alias) => alias,
644 None => include.as_str(),
645 };
646 let mut include_path = config.src.clone();
647 include_path.push("src");
648 include_path.push("bootstrap");
649 include_path.push("defaults");
650 include_path.push(format!("bootstrap.{include}.toml"));
651 let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
652 eprintln!(
653 "ERROR: Failed to parse default config profile at '{}': {e}",
654 include_path.display()
655 );
656 exit!(2);
657 });
658 toml.merge(
659 Some(include_path),
660 &mut Default::default(),
661 included_toml,
662 ReplaceOpt::IgnoreDuplicate,
663 );
664 }
665
666 let mut override_toml = TomlConfig::default();
667 for option in flags_set.iter() {
668 fn get_table(option: &str) -> Result<TomlConfig, toml::de::Error> {
669 toml::from_str(option).and_then(|table: toml::Value| TomlConfig::deserialize(table))
670 }
671
672 let mut err = match get_table(option) {
673 Ok(v) => {
674 override_toml.merge(
675 None,
676 &mut Default::default(),
677 v,
678 ReplaceOpt::ErrorOnDuplicate,
679 );
680 continue;
681 }
682 Err(e) => e,
683 };
684 if let Some((key, value)) = option.split_once('=')
687 && !value.contains('"')
688 {
689 match get_table(&format!(r#"{key}="{value}""#)) {
690 Ok(v) => {
691 override_toml.merge(
692 None,
693 &mut Default::default(),
694 v,
695 ReplaceOpt::ErrorOnDuplicate,
696 );
697 continue;
698 }
699 Err(e) => err = e,
700 }
701 }
702 eprintln!("failed to parse override `{option}`: `{err}");
703 exit!(2)
704 }
705 toml.merge(None, &mut Default::default(), override_toml, ReplaceOpt::Override);
706
707 config.change_id = toml.change_id.inner;
708
709 let Build {
710 description,
711 build,
712 host,
713 target,
714 build_dir,
715 cargo,
716 rustc,
717 rustfmt,
718 cargo_clippy,
719 docs,
720 compiler_docs,
721 library_docs_private_items,
722 docs_minification,
723 submodules,
724 gdb,
725 lldb,
726 nodejs,
727 npm,
728 python,
729 reuse,
730 locked_deps,
731 vendor,
732 full_bootstrap,
733 bootstrap_cache_path,
734 extended,
735 tools,
736 tool,
737 verbose,
738 sanitizers,
739 profiler,
740 cargo_native_static,
741 low_priority,
742 configure_args,
743 local_rebuild,
744 print_step_timings,
745 print_step_rusage,
746 check_stage,
747 doc_stage,
748 build_stage,
749 test_stage,
750 install_stage,
751 dist_stage,
752 bench_stage,
753 patch_binaries_for_nix,
754 metrics: _,
756 android_ndk,
757 optimized_compiler_builtins,
758 jobs,
759 compiletest_diff_tool,
760 compiletest_allow_stage0,
761 compiletest_use_stage0_libtest,
762 tidy_extra_checks,
763 ccache,
764 exclude,
765 } = toml.build.unwrap_or_default();
766
767 let mut paths: Vec<PathBuf> = flags_skip.into_iter().chain(flags_exclude).collect();
768
769 if let Some(exclude) = exclude {
770 paths.extend(exclude);
771 }
772
773 config.skip = paths
774 .into_iter()
775 .map(|p| {
776 if cfg!(windows) {
780 PathBuf::from(p.to_str().unwrap().replace('/', "\\"))
781 } else {
782 p
783 }
784 })
785 .collect();
786
787 config.jobs = Some(threads_from_config(flags_jobs.unwrap_or(jobs.unwrap_or(0))));
788
789 if let Some(flags_build) = flags_build {
790 config.host_target = TargetSelection::from_user(&flags_build);
791 } else if let Some(file_build) = build {
792 config.host_target = TargetSelection::from_user(&file_build);
793 };
794
795 set(&mut config.out, flags_build_dir.or_else(|| build_dir.map(PathBuf::from)));
796 if !config.out.is_absolute() {
799 config.out = absolute(&config.out).expect("can't make empty path absolute");
801 }
802
803 if cargo_clippy.is_some() && rustc.is_none() {
804 println!(
805 "WARNING: Using `build.cargo-clippy` without `build.rustc` usually fails due to toolchain conflict."
806 );
807 }
808
809 config.patch_binaries_for_nix = patch_binaries_for_nix;
810 config.bootstrap_cache_path = bootstrap_cache_path;
811 config.llvm_assertions =
812 toml.llvm.as_ref().is_some_and(|llvm| llvm.assertions.unwrap_or(false));
813
814 config.initial_rustc = if let Some(rustc) = rustc {
815 if !flags_skip_stage0_validation {
816 config.check_stage0_version(&rustc, "rustc");
817 }
818 rustc
819 } else {
820 let dwn_ctx = DownloadContext::from(&config);
821 download_beta_toolchain(dwn_ctx);
822 config
823 .out
824 .join(config.host_target)
825 .join("stage0")
826 .join("bin")
827 .join(exe("rustc", config.host_target))
828 };
829
830 config.initial_sysroot = t!(PathBuf::from_str(
831 command(&config.initial_rustc)
832 .args(["--print", "sysroot"])
833 .run_in_dry_run()
834 .run_capture_stdout(&config)
835 .stdout()
836 .trim()
837 ));
838
839 config.initial_cargo_clippy = cargo_clippy;
840
841 config.initial_cargo = if let Some(cargo) = cargo {
842 if !flags_skip_stage0_validation {
843 config.check_stage0_version(&cargo, "cargo");
844 }
845 cargo
846 } else {
847 let dwn_ctx = DownloadContext::from(&config);
848 download_beta_toolchain(dwn_ctx);
849 config.initial_sysroot.join("bin").join(exe("cargo", config.host_target))
850 };
851
852 if config.dry_run() {
854 let dir = config.out.join("tmp-dry-run");
855 t!(fs::create_dir_all(&dir));
856 config.out = dir;
857 }
858
859 config.hosts = if let Some(TargetSelectionList(arg_host)) = flags_host {
860 arg_host
861 } else if let Some(file_host) = host {
862 file_host.iter().map(|h| TargetSelection::from_user(h)).collect()
863 } else {
864 vec![config.host_target]
865 };
866 config.targets = if let Some(TargetSelectionList(arg_target)) = flags_target {
867 arg_target
868 } else if let Some(file_target) = target {
869 file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
870 } else {
871 config.hosts.clone()
874 };
875
876 config.nodejs = nodejs.map(PathBuf::from);
877 config.npm = npm.map(PathBuf::from);
878 config.gdb = gdb.map(PathBuf::from);
879 config.lldb = lldb.map(PathBuf::from);
880 config.python = python.map(PathBuf::from);
881 config.reuse = reuse.map(PathBuf::from);
882 config.submodules = submodules;
883 config.android_ndk = android_ndk;
884 set(&mut config.low_priority, low_priority);
885 set(&mut config.compiler_docs, compiler_docs);
886 set(&mut config.library_docs_private_items, library_docs_private_items);
887 set(&mut config.docs_minification, docs_minification);
888 set(&mut config.docs, docs);
889 set(&mut config.locked_deps, locked_deps);
890 set(&mut config.full_bootstrap, full_bootstrap);
891 set(&mut config.extended, extended);
892 config.tools = tools;
893 set(&mut config.tool, tool);
894 set(&mut config.verbose, verbose);
895 set(&mut config.sanitizers, sanitizers);
896 set(&mut config.profiler, profiler);
897 set(&mut config.cargo_native_static, cargo_native_static);
898 set(&mut config.configure_args, configure_args);
899 set(&mut config.local_rebuild, local_rebuild);
900 set(&mut config.print_step_timings, print_step_timings);
901 set(&mut config.print_step_rusage, print_step_rusage);
902
903 config.verbose = cmp::max(config.verbose, flags_verbose as usize);
904
905 config.verbose_tests = config.is_verbose();
907
908 config.apply_install_config(toml.install);
909
910 let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel")));
911 let ci_channel = file_content.trim_end();
912
913 let toml_channel = toml.rust.as_ref().and_then(|r| r.channel.clone());
914 let is_user_configured_rust_channel = match toml_channel {
915 Some(channel) if channel == "auto-detect" => {
916 config.channel = ci_channel.into();
917 true
918 }
919 Some(channel) => {
920 config.channel = channel;
921 true
922 }
923 None => false,
924 };
925
926 let default = config.channel == "dev";
927 config.omit_git_hash = toml.rust.as_ref().and_then(|r| r.omit_git_hash).unwrap_or(default);
928
929 config.rust_info = config.git_info(config.omit_git_hash, &config.src);
930 config.cargo_info =
931 config.git_info(config.omit_git_hash, &config.src.join("src/tools/cargo"));
932 config.rust_analyzer_info =
933 config.git_info(config.omit_git_hash, &config.src.join("src/tools/rust-analyzer"));
934 config.clippy_info =
935 config.git_info(config.omit_git_hash, &config.src.join("src/tools/clippy"));
936 config.miri_info =
937 config.git_info(config.omit_git_hash, &config.src.join("src/tools/miri"));
938 config.rustfmt_info =
939 config.git_info(config.omit_git_hash, &config.src.join("src/tools/rustfmt"));
940 config.enzyme_info =
941 config.git_info(config.omit_git_hash, &config.src.join("src/tools/enzyme"));
942 config.in_tree_llvm_info = config.git_info(false, &config.src.join("src/llvm-project"));
943 config.in_tree_gcc_info = config.git_info(false, &config.src.join("src/gcc"));
944
945 config.vendor = vendor.unwrap_or(
946 config.rust_info.is_from_tarball()
947 && config.src.join("vendor").exists()
948 && config.src.join(".cargo/config.toml").exists(),
949 );
950
951 if !is_user_configured_rust_channel && config.rust_info.is_from_tarball() {
952 config.channel = ci_channel.into();
953 }
954
955 config.rust_profile_use = flags_rust_profile_use;
956 config.rust_profile_generate = flags_rust_profile_generate;
957
958 config.apply_target_config(toml.target);
959 config.apply_rust_config(toml.rust, flags_warnings);
960
961 config.reproducible_artifacts = flags_reproducible_artifact;
962 config.description = description;
963
964 if let Some(commit) = &config.download_rustc_commit
968 && is_user_configured_rust_channel
969 {
970 println!(
971 "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
972 );
973
974 let channel =
975 config.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned();
976
977 config.channel = channel;
978 }
979
980 config.apply_llvm_config(toml.llvm);
981
982 config.apply_gcc_config(toml.gcc);
983
984 match ccache {
985 Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()),
986 Some(StringOrBool::Bool(true)) => {
987 config.ccache = Some("ccache".to_string());
988 }
989 Some(StringOrBool::Bool(false)) | None => {}
990 }
991
992 if config.llvm_from_ci {
993 let triple = &config.host_target.triple;
994 let ci_llvm_bin = config.ci_llvm_root().join("bin");
995 let build_target = config
996 .target_config
997 .entry(config.host_target)
998 .or_insert_with(|| Target::from_triple(triple));
999
1000 check_ci_llvm!(build_target.llvm_config);
1001 check_ci_llvm!(build_target.llvm_filecheck);
1002 build_target.llvm_config =
1003 Some(ci_llvm_bin.join(exe("llvm-config", config.host_target)));
1004 build_target.llvm_filecheck =
1005 Some(ci_llvm_bin.join(exe("FileCheck", config.host_target)));
1006 }
1007
1008 config.apply_dist_config(toml.dist);
1009
1010 config.initial_rustfmt = if let Some(r) = rustfmt {
1011 Some(r)
1012 } else {
1013 let dwn_ctx = DownloadContext::from(&config);
1014 maybe_download_rustfmt(dwn_ctx)
1015 };
1016
1017 if matches!(config.lld_mode, LldMode::SelfContained)
1018 && !config.lld_enabled
1019 && flags_stage.unwrap_or(0) > 0
1020 {
1021 panic!(
1022 "Trying to use self-contained lld as a linker, but LLD is not being added to the sysroot. Enable it with rust.lld = true."
1023 );
1024 }
1025
1026 if config.lld_enabled && config.is_system_llvm(config.host_target) {
1027 panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config.");
1028 }
1029
1030 config.optimized_compiler_builtins =
1031 optimized_compiler_builtins.unwrap_or(config.channel != "dev");
1032
1033 config.compiletest_diff_tool = compiletest_diff_tool;
1034
1035 config.compiletest_allow_stage0 = compiletest_allow_stage0.unwrap_or(false);
1036 config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true);
1037
1038 config.tidy_extra_checks = tidy_extra_checks;
1039
1040 let download_rustc = config.download_rustc_commit.is_some();
1041 config.explicit_stage_from_cli = flags_stage.is_some();
1042 config.explicit_stage_from_config = test_stage.is_some()
1043 || build_stage.is_some()
1044 || doc_stage.is_some()
1045 || dist_stage.is_some()
1046 || install_stage.is_some()
1047 || check_stage.is_some()
1048 || bench_stage.is_some();
1049
1050 config.stage = match config.cmd {
1051 Subcommand::Check { .. } => flags_stage.or(check_stage).unwrap_or(1),
1052 Subcommand::Clippy { .. } | Subcommand::Fix => flags_stage.or(check_stage).unwrap_or(1),
1053 Subcommand::Doc { .. } => {
1055 flags_stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 1 })
1056 }
1057 Subcommand::Build => {
1058 flags_stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
1059 }
1060 Subcommand::Test { .. } | Subcommand::Miri { .. } => {
1061 flags_stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 })
1062 }
1063 Subcommand::Bench { .. } => flags_stage.or(bench_stage).unwrap_or(2),
1064 Subcommand::Dist => flags_stage.or(dist_stage).unwrap_or(2),
1065 Subcommand::Install => flags_stage.or(install_stage).unwrap_or(2),
1066 Subcommand::Perf { .. } => flags_stage.unwrap_or(1),
1067 Subcommand::Clean { .. }
1070 | Subcommand::Run { .. }
1071 | Subcommand::Setup { .. }
1072 | Subcommand::Format { .. }
1073 | Subcommand::Vendor { .. } => flags_stage.unwrap_or(0),
1074 };
1075
1076 match (config.stage, &config.cmd) {
1078 (0, Subcommand::Build) => {
1079 eprintln!("WARNING: cannot build anything on stage 0. Use at least stage 1.");
1080 exit!(1);
1081 }
1082 (0, Subcommand::Check { .. }) => {
1083 eprintln!("WARNING: cannot check anything on stage 0. Use at least stage 1.");
1084 exit!(1);
1085 }
1086 _ => {}
1087 }
1088
1089 if config.compile_time_deps && !matches!(config.cmd, Subcommand::Check { .. }) {
1090 eprintln!(
1091 "WARNING: Can't use --compile-time-deps with any subcommand other than check."
1092 );
1093 exit!(1);
1094 }
1095
1096 #[cfg(not(test))]
1098 if flags_stage.is_none() && config.is_running_on_ci {
1099 match config.cmd {
1100 Subcommand::Test { .. }
1101 | Subcommand::Miri { .. }
1102 | Subcommand::Doc { .. }
1103 | Subcommand::Build
1104 | Subcommand::Bench { .. }
1105 | Subcommand::Dist
1106 | Subcommand::Install => {
1107 assert_eq!(
1108 config.stage, 2,
1109 "x.py should be run with `--stage 2` on CI, but was run with `--stage {}`",
1110 config.stage,
1111 );
1112 }
1113 Subcommand::Clean { .. }
1114 | Subcommand::Check { .. }
1115 | Subcommand::Clippy { .. }
1116 | Subcommand::Fix
1117 | Subcommand::Run { .. }
1118 | Subcommand::Setup { .. }
1119 | Subcommand::Format { .. }
1120 | Subcommand::Vendor { .. }
1121 | Subcommand::Perf { .. } => {}
1122 }
1123 }
1124
1125 config
1126 }
1127
1128 pub fn dry_run(&self) -> bool {
1129 self.exec_ctx.dry_run()
1130 }
1131
1132 pub fn is_explicit_stage(&self) -> bool {
1133 self.explicit_stage_from_cli || self.explicit_stage_from_config
1134 }
1135
1136 pub(crate) fn test_args(&self) -> Vec<&str> {
1137 let mut test_args = match self.cmd {
1138 Subcommand::Test { ref test_args, .. }
1139 | Subcommand::Bench { ref test_args, .. }
1140 | Subcommand::Miri { ref test_args, .. } => {
1141 test_args.iter().flat_map(|s| s.split_whitespace()).collect()
1142 }
1143 _ => vec![],
1144 };
1145 test_args.extend(self.free_args.iter().map(|s| s.as_str()));
1146 test_args
1147 }
1148
1149 pub(crate) fn args(&self) -> Vec<&str> {
1150 let mut args = match self.cmd {
1151 Subcommand::Run { ref args, .. } => {
1152 args.iter().flat_map(|s| s.split_whitespace()).collect()
1153 }
1154 _ => vec![],
1155 };
1156 args.extend(self.free_args.iter().map(|s| s.as_str()));
1157 args
1158 }
1159
1160 pub(crate) fn read_file_by_commit(&self, file: &Path, commit: &str) -> String {
1162 assert!(
1163 self.rust_info.is_managed_git_subrepository(),
1164 "`Config::read_file_by_commit` is not supported in non-git sources."
1165 );
1166
1167 let mut git = helpers::git(Some(&self.src));
1168 git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap()));
1169 git.run_capture_stdout(self).stdout()
1170 }
1171
1172 pub(crate) fn artifact_version_part(&self, commit: &str) -> String {
1175 let (channel, version) = if self.rust_info.is_managed_git_subrepository() {
1176 let channel =
1177 self.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned();
1178 let version =
1179 self.read_file_by_commit(Path::new("src/version"), commit).trim().to_owned();
1180 (channel, version)
1181 } else {
1182 let channel = fs::read_to_string(self.src.join("src/ci/channel"));
1183 let version = fs::read_to_string(self.src.join("src/version"));
1184 match (channel, version) {
1185 (Ok(channel), Ok(version)) => {
1186 (channel.trim().to_owned(), version.trim().to_owned())
1187 }
1188 (channel, version) => {
1189 let src = self.src.display();
1190 eprintln!("ERROR: failed to determine artifact channel and/or version");
1191 eprintln!(
1192 "HELP: consider using a git checkout or ensure these files are readable"
1193 );
1194 if let Err(channel) = channel {
1195 eprintln!("reading {src}/src/ci/channel failed: {channel:?}");
1196 }
1197 if let Err(version) = version {
1198 eprintln!("reading {src}/src/version failed: {version:?}");
1199 }
1200 panic!();
1201 }
1202 }
1203 };
1204
1205 match channel.as_str() {
1206 "stable" => version,
1207 "beta" => channel,
1208 "nightly" => channel,
1209 other => unreachable!("{:?} is not recognized as a valid channel", other),
1210 }
1211 }
1212
1213 pub fn bindir_relative(&self) -> &Path {
1215 let bindir = &self.bindir;
1216 if bindir.is_absolute() {
1217 if let Some(prefix) = &self.prefix
1219 && let Ok(stripped) = bindir.strip_prefix(prefix)
1220 {
1221 return stripped;
1222 }
1223 }
1224 bindir
1225 }
1226
1227 pub fn libdir_relative(&self) -> Option<&Path> {
1229 let libdir = self.libdir.as_ref()?;
1230 if libdir.is_relative() {
1231 Some(libdir)
1232 } else {
1233 libdir.strip_prefix(self.prefix.as_ref()?).ok()
1235 }
1236 }
1237
1238 pub(crate) fn ci_llvm_root(&self) -> PathBuf {
1240 assert!(self.llvm_from_ci);
1241 self.out.join(self.host_target).join("ci-llvm")
1242 }
1243
1244 pub(crate) fn ci_rustc_dir(&self) -> PathBuf {
1246 assert!(self.download_rustc());
1247 self.out.join(self.host_target).join("ci-rustc")
1248 }
1249
1250 pub(crate) fn llvm_link_shared(&self) -> bool {
1255 let mut opt = self.llvm_link_shared.get();
1256 if opt.is_none() && self.dry_run() {
1257 return false;
1259 }
1260
1261 let llvm_link_shared = *opt.get_or_insert_with(|| {
1262 if self.llvm_from_ci {
1263 self.maybe_download_ci_llvm();
1264 let ci_llvm = self.ci_llvm_root();
1265 let link_type = t!(
1266 std::fs::read_to_string(ci_llvm.join("link-type.txt")),
1267 format!("CI llvm missing: {}", ci_llvm.display())
1268 );
1269 link_type == "dynamic"
1270 } else {
1271 false
1274 }
1275 });
1276 self.llvm_link_shared.set(opt);
1277 llvm_link_shared
1278 }
1279
1280 pub(crate) fn download_rustc(&self) -> bool {
1282 self.download_rustc_commit().is_some()
1283 }
1284
1285 pub(crate) fn download_rustc_commit(&self) -> Option<&str> {
1286 static DOWNLOAD_RUSTC: OnceLock<Option<String>> = OnceLock::new();
1287 if self.dry_run() && DOWNLOAD_RUSTC.get().is_none() {
1288 return self.download_rustc_commit.as_deref();
1290 }
1291
1292 DOWNLOAD_RUSTC
1293 .get_or_init(|| match &self.download_rustc_commit {
1294 None => None,
1295 Some(commit) => {
1296 self.download_ci_rustc(commit);
1297
1298 if !self.llvm_from_ci {
1302 if self.is_running_on_ci {
1305 println!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled.");
1306 return None;
1307 } else {
1308 panic!("ERROR: LLVM submodule has changes, `download-rustc` can't be used.");
1309 }
1310 }
1311
1312 if let Some(config_path) = &self.config {
1313 let ci_config_toml = match self.get_builder_toml("ci-rustc") {
1314 Ok(ci_config_toml) => ci_config_toml,
1315 Err(e) if e.to_string().contains("unknown field") => {
1316 println!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled.");
1317 println!("HELP: Consider rebasing to a newer commit if available.");
1318 return None;
1319 },
1320 Err(e) => {
1321 eprintln!("ERROR: Failed to parse CI rustc bootstrap.toml: {e}");
1322 exit!(2);
1323 },
1324 };
1325
1326 let current_config_toml = Self::get_toml(config_path).unwrap();
1327
1328 let res = check_incompatible_options_for_ci_rustc(
1331 self.host_target,
1332 current_config_toml,
1333 ci_config_toml,
1334 );
1335
1336 let disable_ci_rustc_if_incompatible = env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE")
1339 .is_some_and(|s| s == "1" || s == "true");
1340
1341 if disable_ci_rustc_if_incompatible && res.is_err() {
1342 println!("WARNING: download-rustc is disabled with `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` env.");
1343 return None;
1344 }
1345
1346 res.unwrap();
1347 }
1348
1349 Some(commit.clone())
1350 }
1351 })
1352 .as_deref()
1353 }
1354
1355 pub fn verbose(&self, f: impl Fn()) {
1357 self.exec_ctx.verbose(f);
1358 }
1359
1360 pub fn any_sanitizers_to_build(&self) -> bool {
1361 self.target_config
1362 .iter()
1363 .any(|(ts, t)| !ts.is_msvc() && t.sanitizers.unwrap_or(self.sanitizers))
1364 }
1365
1366 pub fn any_profiler_enabled(&self) -> bool {
1367 self.target_config.values().any(|t| matches!(&t.profiler, Some(p) if p.is_string_or_true()))
1368 || self.profiler
1369 }
1370
1371 pub fn submodules(&self) -> bool {
1373 self.submodules.unwrap_or(self.rust_info.is_managed_git_subrepository())
1376 }
1377
1378 pub fn git_config(&self) -> GitConfig<'_> {
1379 GitConfig {
1380 nightly_branch: &self.stage0_metadata.config.nightly_branch,
1381 git_merge_commit_email: &self.stage0_metadata.config.git_merge_commit_email,
1382 }
1383 }
1384
1385 #[cfg_attr(
1395 feature = "tracing",
1396 instrument(
1397 level = "trace",
1398 name = "Config::update_submodule",
1399 skip_all,
1400 fields(relative_path = ?relative_path),
1401 ),
1402 )]
1403 pub(crate) fn update_submodule(&self, relative_path: &str) {
1404 if self.rust_info.is_from_tarball() || !self.submodules() {
1405 return;
1406 }
1407
1408 let absolute_path = self.src.join(relative_path);
1409
1410 if !absolute_path.exists() {
1414 t!(fs::create_dir_all(&absolute_path));
1415 }
1416
1417 if !self.git_info(false, &absolute_path).is_managed_git_subrepository()
1420 && !helpers::dir_is_empty(&absolute_path)
1421 {
1422 return;
1423 }
1424
1425 let submodule_git = || {
1432 let mut cmd = helpers::git(Some(&absolute_path));
1433 cmd.run_in_dry_run();
1434 cmd
1435 };
1436
1437 let checked_out_hash =
1439 submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(self).stdout();
1440 let checked_out_hash = checked_out_hash.trim_end();
1441 let recorded = helpers::git(Some(&self.src))
1443 .run_in_dry_run()
1444 .args(["ls-tree", "HEAD"])
1445 .arg(relative_path)
1446 .run_capture_stdout(self)
1447 .stdout();
1448
1449 let actual_hash = recorded
1450 .split_whitespace()
1451 .nth(2)
1452 .unwrap_or_else(|| panic!("unexpected output `{recorded}`"));
1453
1454 if actual_hash == checked_out_hash {
1455 return;
1457 }
1458
1459 println!("Updating submodule {relative_path}");
1460
1461 helpers::git(Some(&self.src))
1462 .allow_failure()
1463 .run_in_dry_run()
1464 .args(["submodule", "-q", "sync"])
1465 .arg(relative_path)
1466 .run(self);
1467
1468 let update = |progress: bool| {
1470 let current_branch = helpers::git(Some(&self.src))
1473 .allow_failure()
1474 .run_in_dry_run()
1475 .args(["symbolic-ref", "--short", "HEAD"])
1476 .run_capture(self);
1477
1478 let mut git = helpers::git(Some(&self.src)).allow_failure();
1479 git.run_in_dry_run();
1480 if current_branch.is_success() {
1481 let branch = current_branch.stdout();
1484 let branch = branch.trim();
1485 let branch = branch.strip_prefix("heads/").unwrap_or(branch);
1486 git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
1487 }
1488 git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
1489 if progress {
1490 git.arg("--progress");
1491 }
1492 git.arg(relative_path);
1493 git
1494 };
1495 if !update(true).allow_failure().run(self) {
1496 update(false).allow_failure().run(self);
1497 }
1498
1499 let has_local_modifications =
1502 !submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(self);
1503 if has_local_modifications {
1504 submodule_git().allow_failure().args(["stash", "push"]).run(self);
1505 }
1506
1507 submodule_git().allow_failure().args(["reset", "-q", "--hard"]).run(self);
1508 submodule_git().allow_failure().args(["clean", "-qdfx"]).run(self);
1509
1510 if has_local_modifications {
1511 submodule_git().allow_failure().args(["stash", "pop"]).run(self);
1512 }
1513 }
1514
1515 #[cfg(test)]
1516 pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
1517
1518 #[cfg(not(test))]
1520 pub fn check_stage0_version(&self, program_path: &Path, component_name: &'static str) {
1521 use build_helper::util::fail;
1522
1523 if self.dry_run() {
1524 return;
1525 }
1526
1527 let stage0_output =
1528 command(program_path).arg("--version").run_capture_stdout(self).stdout();
1529 let mut stage0_output = stage0_output.lines().next().unwrap().split(' ');
1530
1531 let stage0_name = stage0_output.next().unwrap();
1532 if stage0_name != component_name {
1533 fail(&format!(
1534 "Expected to find {component_name} at {} but it claims to be {stage0_name}",
1535 program_path.display()
1536 ));
1537 }
1538
1539 let stage0_version =
1540 semver::Version::parse(stage0_output.next().unwrap().split('-').next().unwrap().trim())
1541 .unwrap();
1542 let source_version = semver::Version::parse(
1543 fs::read_to_string(self.src.join("src/version")).unwrap().trim(),
1544 )
1545 .unwrap();
1546 if !(source_version == stage0_version
1547 || (source_version.major == stage0_version.major
1548 && (source_version.minor == stage0_version.minor
1549 || source_version.minor == stage0_version.minor + 1)))
1550 {
1551 let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1);
1552 fail(&format!(
1553 "Unexpected {component_name} version: {stage0_version}, we should use {prev_version}/{source_version} to build source with {source_version}"
1554 ));
1555 }
1556 }
1557
1558 pub fn download_ci_rustc_commit(
1560 &self,
1561 download_rustc: Option<StringOrBool>,
1562 debug_assertions_requested: bool,
1563 llvm_assertions: bool,
1564 ) -> Option<String> {
1565 if !is_download_ci_available(&self.host_target.triple, llvm_assertions) {
1566 return None;
1567 }
1568
1569 let if_unchanged = match download_rustc {
1571 None | Some(StringOrBool::Bool(false)) => return None,
1577 Some(StringOrBool::Bool(true)) => false,
1578 Some(StringOrBool::String(s)) if s == "if-unchanged" => {
1579 if !self.rust_info.is_managed_git_subrepository() {
1580 println!(
1581 "ERROR: `download-rustc=if-unchanged` is only compatible with Git managed sources."
1582 );
1583 crate::exit!(1);
1584 }
1585
1586 true
1587 }
1588 Some(StringOrBool::String(other)) => {
1589 panic!("unrecognized option for download-rustc: {other}")
1590 }
1591 };
1592
1593 let commit = if self.rust_info.is_managed_git_subrepository() {
1594 let freshness = self.check_path_modifications(RUSTC_IF_UNCHANGED_ALLOWED_PATHS);
1597 self.verbose(|| {
1598 eprintln!("rustc freshness: {freshness:?}");
1599 });
1600 match freshness {
1601 PathFreshness::LastModifiedUpstream { upstream } => upstream,
1602 PathFreshness::HasLocalModifications { upstream } => {
1603 if if_unchanged {
1604 return None;
1605 }
1606
1607 if self.is_running_on_ci {
1608 eprintln!("CI rustc commit matches with HEAD and we are in CI.");
1609 eprintln!(
1610 "`rustc.download-ci` functionality will be skipped as artifacts are not available."
1611 );
1612 return None;
1613 }
1614
1615 upstream
1616 }
1617 PathFreshness::MissingUpstream => {
1618 eprintln!("No upstream commit found");
1619 return None;
1620 }
1621 }
1622 } else {
1623 channel::read_commit_info_file(&self.src)
1624 .map(|info| info.sha.trim().to_owned())
1625 .expect("git-commit-info is missing in the project root")
1626 };
1627
1628 if debug_assertions_requested {
1629 eprintln!(
1630 "WARN: `rust.debug-assertions = true` will prevent downloading CI rustc as alt CI \
1631 rustc is not currently built with debug assertions."
1632 );
1633 return None;
1634 }
1635
1636 Some(commit)
1637 }
1638
1639 pub fn parse_download_ci_llvm(
1640 &self,
1641 download_ci_llvm: Option<StringOrBool>,
1642 asserts: bool,
1643 ) -> bool {
1644 let default = if self.is_running_on_ci {
1647 StringOrBool::String("if-unchanged".to_string())
1648 } else {
1649 StringOrBool::Bool(true)
1650 };
1651 let download_ci_llvm = download_ci_llvm.unwrap_or(default);
1652
1653 let if_unchanged = || {
1654 if self.rust_info.is_from_tarball() {
1655 println!("ERROR: 'if-unchanged' is only compatible with Git managed sources.");
1657 crate::exit!(1);
1658 }
1659
1660 #[cfg(not(test))]
1662 self.update_submodule("src/llvm-project");
1663
1664 let has_changes = self.has_changes_from_upstream(LLVM_INVALIDATION_PATHS);
1666
1667 if has_changes { false } else { llvm::is_ci_llvm_available_for_target(self, asserts) }
1669 };
1670
1671 match download_ci_llvm {
1672 StringOrBool::Bool(b) => {
1673 if !b && self.download_rustc_commit.is_some() {
1674 panic!(
1675 "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`."
1676 );
1677 }
1678
1679 if b && self.is_running_on_ci {
1680 panic!(
1682 "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead."
1683 );
1684 }
1685
1686 b && llvm::is_ci_llvm_available_for_target(self, asserts)
1688 }
1689 StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(),
1690 StringOrBool::String(other) => {
1691 panic!("unrecognized option for download-ci-llvm: {other:?}")
1692 }
1693 }
1694 }
1695
1696 pub fn has_changes_from_upstream(&self, paths: &[&'static str]) -> bool {
1698 match self.check_path_modifications(paths) {
1699 PathFreshness::LastModifiedUpstream { .. } => false,
1700 PathFreshness::HasLocalModifications { .. } | PathFreshness::MissingUpstream => true,
1701 }
1702 }
1703
1704 pub fn check_path_modifications(&self, paths: &[&'static str]) -> PathFreshness {
1706 self.path_modification_cache
1712 .lock()
1713 .unwrap()
1714 .entry(paths.to_vec())
1715 .or_insert_with(|| {
1716 check_path_modifications(&self.src, &self.git_config(), paths, CiEnv::current())
1717 .unwrap()
1718 })
1719 .clone()
1720 }
1721
1722 pub fn ci_env(&self) -> CiEnv {
1723 if self.is_running_on_ci { CiEnv::GitHubActions } else { CiEnv::None }
1724 }
1725
1726 pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool {
1727 self.target_config.get(&target).and_then(|t| t.sanitizers).unwrap_or(self.sanitizers)
1728 }
1729
1730 pub fn needs_sanitizer_runtime_built(&self, target: TargetSelection) -> bool {
1731 !target.is_msvc() && self.sanitizers_enabled(target)
1733 }
1734
1735 pub fn profiler_path(&self, target: TargetSelection) -> Option<&str> {
1736 match self.target_config.get(&target)?.profiler.as_ref()? {
1737 StringOrBool::String(s) => Some(s),
1738 StringOrBool::Bool(_) => None,
1739 }
1740 }
1741
1742 pub fn profiler_enabled(&self, target: TargetSelection) -> bool {
1743 self.target_config
1744 .get(&target)
1745 .and_then(|t| t.profiler.as_ref())
1746 .map(StringOrBool::is_string_or_true)
1747 .unwrap_or(self.profiler)
1748 }
1749
1750 pub fn codegen_backends(&self, target: TargetSelection) -> &[CodegenBackendKind] {
1751 self.target_config
1752 .get(&target)
1753 .and_then(|cfg| cfg.codegen_backends.as_deref())
1754 .unwrap_or(&self.rust_codegen_backends)
1755 }
1756
1757 pub fn jemalloc(&self, target: TargetSelection) -> bool {
1758 self.target_config.get(&target).and_then(|cfg| cfg.jemalloc).unwrap_or(self.jemalloc)
1759 }
1760
1761 pub fn default_codegen_backend(&self, target: TargetSelection) -> Option<CodegenBackendKind> {
1762 self.codegen_backends(target).first().cloned()
1763 }
1764
1765 pub fn rpath_enabled(&self, target: TargetSelection) -> bool {
1766 self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath)
1767 }
1768
1769 pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool {
1770 self.target_config
1771 .get(&target)
1772 .and_then(|t| t.optimized_compiler_builtins)
1773 .unwrap_or(self.optimized_compiler_builtins)
1774 }
1775
1776 pub fn llvm_enabled(&self, target: TargetSelection) -> bool {
1777 self.codegen_backends(target).contains(&CodegenBackendKind::Llvm)
1778 }
1779
1780 pub fn llvm_libunwind(&self, target: TargetSelection) -> LlvmLibunwind {
1781 self.target_config
1782 .get(&target)
1783 .and_then(|t| t.llvm_libunwind)
1784 .or(self.llvm_libunwind_default)
1785 .unwrap_or(if target.contains("fuchsia") {
1786 LlvmLibunwind::InTree
1787 } else {
1788 LlvmLibunwind::No
1789 })
1790 }
1791
1792 pub fn split_debuginfo(&self, target: TargetSelection) -> SplitDebuginfo {
1793 self.target_config
1794 .get(&target)
1795 .and_then(|t| t.split_debuginfo)
1796 .unwrap_or_else(|| SplitDebuginfo::default_for_platform(target))
1797 }
1798
1799 pub fn is_host_target(&self, target: TargetSelection) -> bool {
1801 self.host_target == target
1802 }
1803
1804 pub fn is_system_llvm(&self, target: TargetSelection) -> bool {
1809 match self.target_config.get(&target) {
1810 Some(Target { llvm_config: Some(_), .. }) => {
1811 let ci_llvm = self.llvm_from_ci && self.is_host_target(target);
1812 !ci_llvm
1813 }
1814 Some(Target { llvm_config: None, .. }) => false,
1816 None => false,
1817 }
1818 }
1819
1820 pub fn is_rust_llvm(&self, target: TargetSelection) -> bool {
1824 match self.target_config.get(&target) {
1825 Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched,
1829 _ => !self.is_system_llvm(target),
1832 }
1833 }
1834
1835 pub fn exec_ctx(&self) -> &ExecutionContext {
1836 &self.exec_ctx
1837 }
1838
1839 pub fn git_info(&self, omit_git_hash: bool, dir: &Path) -> GitInfo {
1840 GitInfo::new(omit_git_hash, dir, self)
1841 }
1842}
1843
1844impl AsRef<ExecutionContext> for Config {
1845 fn as_ref(&self) -> &ExecutionContext {
1846 &self.exec_ctx
1847 }
1848}