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