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