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