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