1#![cfg_attr(test, allow(unused))]
19
20use std::cell::{Cell, RefCell};
21use std::collections::{BTreeSet, HashMap, HashSet};
22use std::fmt::Display;
23use std::path::{Path, PathBuf};
24use std::process::Command;
25use std::sync::OnceLock;
26use std::time::SystemTime;
27use std::{env, fs, io, str};
28
29use build_helper::ci::gha;
30use build_helper::exit;
31use cc::Tool;
32use termcolor::{ColorChoice, StandardStream, WriteColor};
33use utils::build_stamp::BuildStamp;
34use utils::channel::GitInfo;
35
36use crate::core::builder;
37use crate::core::builder::Kind;
38use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags};
39use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command};
40use crate::utils::helpers::{
41 self, dir_is_empty, exe, libdir, output, set_file_times, split_debuginfo, symlink_dir,
42};
43
44mod core;
45mod utils;
46
47pub use core::builder::PathSet;
48pub use core::config::flags::{Flags, Subcommand};
49pub use core::config::{ChangeId, Config};
50
51#[cfg(feature = "tracing")]
52use tracing::{instrument, span};
53pub use utils::change_tracker::{
54 CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes,
55};
56pub use utils::helpers::PanicTracker;
57
58use crate::core::build_steps::vendor::VENDOR_DIR;
59
60const LLVM_TOOLS: &[&str] = &[
61 "llvm-cov", "llvm-nm", "llvm-objcopy", "llvm-objdump", "llvm-profdata", "llvm-readobj", "llvm-size", "llvm-strip", "llvm-ar", "llvm-as", "llvm-dis", "llvm-link", "llc", "opt", ];
76
77const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
79
80#[expect(clippy::type_complexity)] const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
84 (None, "bootstrap", None),
85 (Some(Mode::Rustc), "llvm_enzyme", None),
86 (Some(Mode::Codegen), "llvm_enzyme", None),
87 (Some(Mode::ToolRustc), "llvm_enzyme", None),
88 (Some(Mode::ToolRustc), "rust_analyzer", None),
89 (Some(Mode::ToolStd), "rust_analyzer", None),
90 ];
94
95#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)]
101pub struct Compiler {
102 stage: u32,
103 host: TargetSelection,
104 forced_compiler: bool,
108}
109
110impl std::hash::Hash for Compiler {
111 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
112 self.stage.hash(state);
113 self.host.hash(state);
114 }
115}
116
117impl PartialEq for Compiler {
118 fn eq(&self, other: &Self) -> bool {
119 self.stage == other.stage && self.host == other.host
120 }
121}
122
123#[derive(PartialEq, Eq, Copy, Clone, Debug)]
124pub enum DocTests {
125 Yes,
127 No,
129 Only,
131}
132
133pub enum GitRepo {
134 Rustc,
135 Llvm,
136}
137
138#[derive(Clone)]
149pub struct Build {
150 config: Config,
152
153 version: String,
155
156 src: PathBuf,
158 out: PathBuf,
159 bootstrap_out: PathBuf,
160 cargo_info: GitInfo,
161 rust_analyzer_info: GitInfo,
162 clippy_info: GitInfo,
163 miri_info: GitInfo,
164 rustfmt_info: GitInfo,
165 enzyme_info: GitInfo,
166 in_tree_llvm_info: GitInfo,
167 in_tree_gcc_info: GitInfo,
168 local_rebuild: bool,
169 fail_fast: bool,
170 doc_tests: DocTests,
171 verbosity: usize,
172
173 build: TargetSelection,
175 hosts: Vec<TargetSelection>,
177 targets: Vec<TargetSelection>,
179
180 initial_rustc: PathBuf,
181 initial_rustdoc: PathBuf,
182 initial_cargo: PathBuf,
183 initial_lld: PathBuf,
184 initial_relative_libdir: PathBuf,
185 initial_sysroot: PathBuf,
186
187 cc: RefCell<HashMap<TargetSelection, cc::Tool>>,
190 cxx: RefCell<HashMap<TargetSelection, cc::Tool>>,
191 ar: RefCell<HashMap<TargetSelection, PathBuf>>,
192 ranlib: RefCell<HashMap<TargetSelection, PathBuf>>,
193 crates: HashMap<String, Crate>,
196 crate_paths: HashMap<PathBuf, String>,
197 is_sudo: bool,
198 delayed_failures: RefCell<Vec<String>>,
199 prerelease_version: Cell<Option<u32>>,
200
201 #[cfg(feature = "build-metrics")]
202 metrics: crate::utils::metrics::BuildMetrics,
203}
204
205#[derive(Debug, Clone)]
206struct Crate {
207 name: String,
208 deps: HashSet<String>,
209 path: PathBuf,
210 features: Vec<String>,
211}
212
213impl Crate {
214 fn local_path(&self, build: &Build) -> PathBuf {
215 self.path.strip_prefix(&build.config.src).unwrap().into()
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
221pub enum DependencyType {
222 Host,
224 Target,
226 TargetSelfContained,
228}
229
230#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
235pub enum Mode {
236 Std,
238
239 Rustc,
241
242 Codegen,
244
245 ToolBootstrap,
252
253 ToolStd,
257
258 ToolRustc,
263}
264
265impl Mode {
266 pub fn is_tool(&self) -> bool {
267 matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd)
268 }
269
270 pub fn must_support_dlopen(&self) -> bool {
271 matches!(self, Mode::Std | Mode::Codegen)
272 }
273}
274
275#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
276pub enum CLang {
277 C,
278 Cxx,
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
282pub enum FileType {
283 Executable,
285 NativeLibrary,
287 Script,
289 Regular,
291}
292
293impl FileType {
294 pub fn perms(self) -> u32 {
296 match self {
297 FileType::Executable | FileType::Script => 0o755,
298 FileType::Regular | FileType::NativeLibrary => 0o644,
299 }
300 }
301
302 pub fn could_have_split_debuginfo(self) -> bool {
303 match self {
304 FileType::Executable | FileType::NativeLibrary => true,
305 FileType::Script | FileType::Regular => false,
306 }
307 }
308}
309
310macro_rules! forward {
311 ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {
312 impl Build {
313 $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? {
314 self.config.$fn( $($param),* )
315 } )+
316 }
317 }
318}
319
320forward! {
321 verbose(f: impl Fn()),
322 is_verbose() -> bool,
323 create(path: &Path, s: &str),
324 remove(f: &Path),
325 tempdir() -> PathBuf,
326 llvm_link_shared() -> bool,
327 download_rustc() -> bool,
328 initial_rustfmt() -> Option<PathBuf>,
329}
330
331impl Build {
332 pub fn new(mut config: Config) -> Build {
337 let src = config.src.clone();
338 let out = config.out.clone();
339
340 #[cfg(unix)]
341 let is_sudo = match env::var_os("SUDO_USER") {
344 Some(_sudo_user) => {
345 let uid = unsafe { libc::getuid() };
350 uid == 0
351 }
352 None => false,
353 };
354 #[cfg(not(unix))]
355 let is_sudo = false;
356
357 let rust_info = config.rust_info.clone();
358 let cargo_info = config.cargo_info.clone();
359 let rust_analyzer_info = config.rust_analyzer_info.clone();
360 let clippy_info = config.clippy_info.clone();
361 let miri_info = config.miri_info.clone();
362 let rustfmt_info = config.rustfmt_info.clone();
363 let enzyme_info = config.enzyme_info.clone();
364 let in_tree_llvm_info = config.in_tree_llvm_info.clone();
365 let in_tree_gcc_info = config.in_tree_gcc_info.clone();
366
367 let initial_target_libdir_str =
368 config.initial_sysroot.join("lib/rustlib").join(config.build).join("lib");
369
370 let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap();
371 let initial_lld = initial_target_dir.join("bin").join("rust-lld");
372
373 let initial_relative_libdir = initial_target_dir
374 .ancestors()
375 .nth(2)
376 .unwrap()
377 .strip_prefix(&config.initial_sysroot)
378 .expect("Couldn’t determine initial relative libdir.")
379 .to_path_buf();
380
381 let version = std::fs::read_to_string(src.join("src").join("version"))
382 .expect("failed to read src/version");
383 let version = version.trim();
384
385 let mut bootstrap_out = std::env::current_exe()
386 .expect("could not determine path to running process")
387 .parent()
388 .unwrap()
389 .to_path_buf();
390 if bootstrap_out.ends_with("deps") {
393 bootstrap_out.pop();
394 }
395 if !bootstrap_out.join(exe("rustc", config.build)).exists() && !cfg!(test) {
396 panic!(
398 "`rustc` not found in {}, run `cargo build --bins` before `cargo run`",
399 bootstrap_out.display()
400 )
401 }
402
403 if rust_info.is_from_tarball() && config.description.is_none() {
404 config.description = Some("built from a source tarball".to_owned());
405 }
406
407 let mut build = Build {
408 initial_lld,
409 initial_relative_libdir,
410 initial_rustc: config.initial_rustc.clone(),
411 initial_rustdoc: config.initial_rustc.with_file_name(exe("rustdoc", config.build)),
412 initial_cargo: config.initial_cargo.clone(),
413 initial_sysroot: config.initial_sysroot.clone(),
414 local_rebuild: config.local_rebuild,
415 fail_fast: config.cmd.fail_fast(),
416 doc_tests: config.cmd.doc_tests(),
417 verbosity: config.verbose,
418
419 build: config.build,
420 hosts: config.hosts.clone(),
421 targets: config.targets.clone(),
422
423 config,
424 version: version.to_string(),
425 src,
426 out,
427 bootstrap_out,
428
429 cargo_info,
430 rust_analyzer_info,
431 clippy_info,
432 miri_info,
433 rustfmt_info,
434 enzyme_info,
435 in_tree_llvm_info,
436 in_tree_gcc_info,
437 cc: RefCell::new(HashMap::new()),
438 cxx: RefCell::new(HashMap::new()),
439 ar: RefCell::new(HashMap::new()),
440 ranlib: RefCell::new(HashMap::new()),
441 crates: HashMap::new(),
442 crate_paths: HashMap::new(),
443 is_sudo,
444 delayed_failures: RefCell::new(Vec::new()),
445 prerelease_version: Cell::new(None),
446
447 #[cfg(feature = "build-metrics")]
448 metrics: crate::utils::metrics::BuildMetrics::init(),
449 };
450
451 let local_version_verbose =
454 output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
455 let local_release = local_version_verbose
456 .lines()
457 .filter_map(|x| x.strip_prefix("release:"))
458 .next()
459 .unwrap()
460 .trim();
461 if local_release.split('.').take(2).eq(version.split('.').take(2)) {
462 build.verbose(|| println!("auto-detected local-rebuild {local_release}"));
463 build.local_rebuild = true;
464 }
465
466 build.verbose(|| println!("finding compilers"));
467 utils::cc_detect::find(&build);
468 if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
474 build.verbose(|| println!("running sanity check"));
475 crate::core::sanity::check(&mut build);
476
477 let rust_submodules = ["library/backtrace", "library/stdarch"];
480 for s in rust_submodules {
481 build.require_submodule(
482 s,
483 Some(
484 "The submodule is required for the standard library \
485 and the main Cargo workspace.",
486 ),
487 );
488 }
489 build.update_existing_submodules();
491
492 build.verbose(|| println!("learning about cargo"));
493 crate::core::metadata::build(&mut build);
494 }
495
496 let build_triple = build.out.join(build.build);
498 t!(fs::create_dir_all(&build_triple));
499 let host = build.out.join("host");
500 if host.is_symlink() {
501 #[cfg(windows)]
504 t!(fs::remove_dir(&host));
505 #[cfg(not(windows))]
506 t!(fs::remove_file(&host));
507 }
508 t!(
509 symlink_dir(&build.config, &build_triple, &host),
510 format!("symlink_dir({} => {}) failed", host.display(), build_triple.display())
511 );
512
513 build
514 }
515
516 #[cfg_attr(
525 feature = "tracing",
526 instrument(
527 level = "trace",
528 name = "Build::require_submodule",
529 skip_all,
530 fields(submodule = submodule),
531 ),
532 )]
533 pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) {
534 if self.rust_info().is_from_tarball() {
535 return;
536 }
537
538 if cfg!(test) && !self.config.submodules() {
541 return;
542 }
543 self.config.update_submodule(submodule);
544 let absolute_path = self.config.src.join(submodule);
545 if !absolute_path.exists() || dir_is_empty(&absolute_path) {
546 let maybe_enable = if !self.config.submodules()
547 && self.config.rust_info.is_managed_git_subrepository()
548 {
549 "\nConsider setting `build.submodules = true` or manually initializing the submodules."
550 } else {
551 ""
552 };
553 let err_hint = err_hint.map_or_else(String::new, |e| format!("\n{e}"));
554 eprintln!(
555 "submodule {submodule} does not appear to be checked out, \
556 but it is required for this step{maybe_enable}{err_hint}"
557 );
558 exit!(1);
559 }
560 }
561
562 pub fn require_and_update_all_submodules(&self) {
565 for submodule in build_helper::util::parse_gitmodules(&self.src) {
566 self.require_submodule(submodule, None);
567 }
568 }
569
570 fn update_existing_submodules(&self) {
573 if !self.config.submodules() {
576 return;
577 }
578 let output = helpers::git(Some(&self.src))
579 .args(["config", "--file"])
580 .arg(".gitmodules")
581 .args(["--get-regexp", "path"])
582 .run_capture(self)
583 .stdout();
584 std::thread::scope(|s| {
585 for line in output.lines() {
588 let submodule = line.split_once(' ').unwrap().1;
589 let config = self.config.clone();
590 s.spawn(move || {
591 Self::update_existing_submodule(&config, submodule);
592 });
593 }
594 });
595 }
596
597 pub fn update_existing_submodule(config: &Config, submodule: &str) {
599 if !config.submodules() {
601 return;
602 }
603
604 if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
605 config.update_submodule(submodule);
606 }
607 }
608
609 #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))]
611 pub fn build(&mut self) {
612 trace!("setting up job management");
613 unsafe {
614 crate::utils::job::setup(self);
615 }
616
617 trace!("downloading rustfmt early");
619 let _ = &builder::Builder::new(self).initial_rustfmt();
620
621 {
623 #[cfg(feature = "tracing")]
624 let _hardcoded_span = span!(
625 tracing::Level::DEBUG,
626 "handling hardcoded subcommands (Format, Suggest, Perf)"
627 )
628 .entered();
629
630 match &self.config.cmd {
631 Subcommand::Format { check, all } => {
632 return core::build_steps::format::format(
633 &builder::Builder::new(self),
634 *check,
635 *all,
636 &self.config.paths,
637 );
638 }
639 Subcommand::Suggest { run } => {
640 return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
641 }
642 Subcommand::Perf(args) => {
643 return core::build_steps::perf::perf(&builder::Builder::new(self), args);
644 }
645 _cmd => {
646 debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");
647 }
648 }
649
650 debug!("handling subcommand normally");
651 }
652
653 if !self.config.dry_run() {
654 #[cfg(feature = "tracing")]
655 let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered();
656
657 {
660 #[cfg(feature = "tracing")]
661 let _sanity_check_span =
662 span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered();
663 self.config.dry_run = DryRun::SelfCheck;
664 let builder = builder::Builder::new(self);
665 builder.execute_cli();
666 }
667
668 {
670 #[cfg(feature = "tracing")]
671 let _actual_run_span =
672 span!(tracing::Level::DEBUG, "(2) executing actual run").entered();
673 self.config.dry_run = DryRun::Disabled;
674 let builder = builder::Builder::new(self);
675 builder.execute_cli();
676 }
677 } else {
678 #[cfg(feature = "tracing")]
679 let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered();
680
681 let builder = builder::Builder::new(self);
682 builder.execute_cli();
683 }
684
685 #[cfg(feature = "tracing")]
686 debug!("checking for postponed test failures from `test --no-fail-fast`");
687
688 let failures = self.delayed_failures.borrow();
690 if !failures.is_empty() {
691 eprintln!("\n{} command(s) did not execute successfully:\n", failures.len());
692 for failure in failures.iter() {
693 eprintln!(" - {failure}\n");
694 }
695 exit!(1);
696 }
697
698 #[cfg(feature = "build-metrics")]
699 self.metrics.persist(self);
700 }
701
702 fn rust_info(&self) -> &GitInfo {
703 &self.config.rust_info
704 }
705
706 fn std_features(&self, target: TargetSelection) -> String {
709 let mut features: BTreeSet<&str> =
710 self.config.rust_std_features.iter().map(|s| s.as_str()).collect();
711
712 match self.config.llvm_libunwind(target) {
713 LlvmLibunwind::InTree => features.insert("llvm-libunwind"),
714 LlvmLibunwind::System => features.insert("system-llvm-libunwind"),
715 LlvmLibunwind::No => false,
716 };
717
718 if self.config.backtrace {
719 features.insert("backtrace");
720 }
721
722 if self.config.profiler_enabled(target) {
723 features.insert("profiler");
724 }
725
726 if target.contains("zkvm") {
728 features.insert("compiler-builtins-mem");
729 }
730
731 features.into_iter().collect::<Vec<_>>().join(" ")
732 }
733
734 fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {
736 let possible_features_by_crates: HashSet<_> = crates
737 .iter()
738 .flat_map(|krate| &self.crates[krate].features)
739 .map(std::ops::Deref::deref)
740 .collect();
741 let check = |feature: &str| -> bool {
742 crates.is_empty() || possible_features_by_crates.contains(feature)
743 };
744 let mut features = vec![];
745 if self.config.jemalloc(target) && check("jemalloc") {
746 features.push("jemalloc");
747 }
748 if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
749 features.push("llvm");
750 }
751 if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {
753 features.push("rustc_randomized_layouts");
754 }
755
756 if !self.config.rust_debug_logging && check("max_level_info") {
762 features.push("max_level_info");
763 }
764
765 features.join(" ")
766 }
767
768 fn cargo_dir(&self) -> &'static str {
771 if self.config.rust_optimize.is_release() { "release" } else { "debug" }
772 }
773
774 fn tools_dir(&self, compiler: Compiler) -> PathBuf {
775 let out = self.out.join(compiler.host).join(format!("stage{}-tools-bin", compiler.stage));
776 t!(fs::create_dir_all(&out));
777 out
778 }
779
780 fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
785 let suffix = match mode {
786 Mode::Std => "-std",
787 Mode::Rustc => "-rustc",
788 Mode::Codegen => "-codegen",
789 Mode::ToolBootstrap => "-bootstrap-tools",
790 Mode::ToolStd | Mode::ToolRustc => "-tools",
791 };
792 self.out.join(compiler.host).join(format!("stage{}{}", compiler.stage, suffix))
793 }
794
795 fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {
799 self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
800 }
801
802 fn llvm_out(&self, target: TargetSelection) -> PathBuf {
807 if self.config.llvm_from_ci && self.config.is_host_target(target) {
808 self.config.ci_llvm_root()
809 } else {
810 self.out.join(target).join("llvm")
811 }
812 }
813
814 fn enzyme_out(&self, target: TargetSelection) -> PathBuf {
815 self.out.join(&*target.triple).join("enzyme")
816 }
817
818 fn gcc_out(&self, target: TargetSelection) -> PathBuf {
819 self.out.join(&*target.triple).join("gcc")
820 }
821
822 fn lld_out(&self, target: TargetSelection) -> PathBuf {
823 self.out.join(target).join("lld")
824 }
825
826 fn doc_out(&self, target: TargetSelection) -> PathBuf {
828 self.out.join(target).join("doc")
829 }
830
831 fn json_doc_out(&self, target: TargetSelection) -> PathBuf {
833 self.out.join(target).join("json-doc")
834 }
835
836 fn test_out(&self, target: TargetSelection) -> PathBuf {
837 self.out.join(target).join("test")
838 }
839
840 fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
842 self.out.join(target).join("compiler-doc")
843 }
844
845 fn md_doc_out(&self, target: TargetSelection) -> PathBuf {
847 self.out.join(target).join("md-doc")
848 }
849
850 fn vendored_crates_path(&self) -> Option<PathBuf> {
852 if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None }
853 }
854
855 fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf {
857 let target_config = self.config.target_config.get(&target);
858 if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
859 s.to_path_buf()
860 } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
861 let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(self).stdout();
862 let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));
863 if filecheck.exists() {
864 filecheck
865 } else {
866 let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(self).stdout();
869 let lib_filecheck =
870 Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));
871 if lib_filecheck.exists() {
872 lib_filecheck
873 } else {
874 filecheck
878 }
879 }
880 } else {
881 let base = self.llvm_out(target).join("build");
882 let base = if !self.ninja() && target.is_msvc() {
883 if self.config.llvm_optimize {
884 if self.config.llvm_release_debuginfo {
885 base.join("RelWithDebInfo")
886 } else {
887 base.join("Release")
888 }
889 } else {
890 base.join("Debug")
891 }
892 } else {
893 base
894 };
895 base.join("bin").join(exe("FileCheck", target))
896 }
897 }
898
899 fn native_dir(&self, target: TargetSelection) -> PathBuf {
901 self.out.join(target).join("native")
902 }
903
904 fn test_helpers_out(&self, target: TargetSelection) -> PathBuf {
907 self.native_dir(target).join("rust-test-helpers")
908 }
909
910 fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) {
912 if env::var_os("RUST_TEST_THREADS").is_none() {
913 cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
914 }
915 }
916
917 fn rustc_snapshot_libdir(&self) -> PathBuf {
919 self.rustc_snapshot_sysroot().join(libdir(self.config.build))
920 }
921
922 fn rustc_snapshot_sysroot(&self) -> &Path {
924 static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();
925 SYSROOT_CACHE.get_or_init(|| {
926 let mut rustc = Command::new(&self.initial_rustc);
927 rustc.args(["--print", "sysroot"]);
928 output(&mut rustc).trim().into()
929 })
930 }
931
932 #[track_caller]
936 fn run(
937 &self,
938 command: &mut BootstrapCommand,
939 stdout: OutputMode,
940 stderr: OutputMode,
941 ) -> CommandOutput {
942 command.mark_as_executed();
943 if self.config.dry_run() && !command.run_always {
944 return CommandOutput::default();
945 }
946
947 #[cfg(feature = "tracing")]
948 let _run_span = trace_cmd!(command);
949
950 let created_at = command.get_created_location();
951 let executed_at = std::panic::Location::caller();
952
953 self.verbose(|| {
954 println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
955 });
956
957 let cmd = command.as_command_mut();
958 cmd.stdout(stdout.stdio());
959 cmd.stderr(stderr.stdio());
960
961 let output = cmd.output();
962
963 use std::fmt::Write;
964
965 let mut message = String::new();
966 let output: CommandOutput = match output {
967 Ok(output) if output.status.success() => {
969 CommandOutput::from_output(output, stdout, stderr)
970 }
971 Ok(output) => {
973 writeln!(
974 message,
975 r#"
976Command {command:?} did not execute successfully.
977Expected success, got {}
978Created at: {created_at}
979Executed at: {executed_at}"#,
980 output.status,
981 )
982 .unwrap();
983
984 let output: CommandOutput = CommandOutput::from_output(output, stdout, stderr);
985
986 if stdout.captures() {
990 writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
991 }
992 if stderr.captures() {
993 writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
994 }
995 output
996 }
997 Err(e) => {
999 writeln!(
1000 message,
1001 "\n\nCommand {command:?} did not execute successfully.\
1002 \nIt was not possible to execute the command: {e:?}"
1003 )
1004 .unwrap();
1005 CommandOutput::did_not_start(stdout, stderr)
1006 }
1007 };
1008
1009 let fail = |message: &str, output: CommandOutput| -> ! {
1010 if self.is_verbose() {
1011 println!("{message}");
1012 } else {
1013 let (stdout, stderr) = (output.stdout_if_present(), output.stderr_if_present());
1014 if stdout.is_some() || stderr.is_some() {
1018 if let Some(stdout) =
1019 output.stdout_if_present().take_if(|s| !s.trim().is_empty())
1020 {
1021 println!("STDOUT:\n{stdout}\n");
1022 }
1023 if let Some(stderr) =
1024 output.stderr_if_present().take_if(|s| !s.trim().is_empty())
1025 {
1026 println!("STDERR:\n{stderr}\n");
1027 }
1028 println!("Command {command:?} has failed. Rerun with -v to see more details.");
1029 } else {
1030 println!("Command has failed. Rerun with -v to see more details.");
1031 }
1032 }
1033 exit!(1);
1034 };
1035
1036 if !output.is_success() {
1037 match command.failure_behavior {
1038 BehaviorOnFailure::DelayFail => {
1039 if self.fail_fast {
1040 fail(&message, output);
1041 }
1042
1043 let mut failures = self.delayed_failures.borrow_mut();
1044 failures.push(message);
1045 }
1046 BehaviorOnFailure::Exit => {
1047 fail(&message, output);
1048 }
1049 BehaviorOnFailure::Ignore => {
1050 }
1054 }
1055 }
1056 output
1057 }
1058
1059 pub fn is_verbose_than(&self, level: usize) -> bool {
1061 self.verbosity > level
1062 }
1063
1064 fn verbose_than(&self, level: usize, f: impl Fn()) {
1066 if self.is_verbose_than(level) {
1067 f()
1068 }
1069 }
1070
1071 fn info(&self, msg: &str) {
1072 match self.config.dry_run {
1073 DryRun::SelfCheck => (),
1074 DryRun::Disabled | DryRun::UserSelected => {
1075 println!("{msg}");
1076 }
1077 }
1078 }
1079
1080 #[must_use = "Groups should not be dropped until the Step finishes running"]
1081 #[track_caller]
1082 fn msg_clippy(
1083 &self,
1084 what: impl Display,
1085 target: impl Into<Option<TargetSelection>>,
1086 ) -> Option<gha::Group> {
1087 self.msg(Kind::Clippy, self.config.stage, what, self.config.build, target)
1088 }
1089
1090 #[must_use = "Groups should not be dropped until the Step finishes running"]
1091 #[track_caller]
1092 fn msg_check(
1093 &self,
1094 what: impl Display,
1095 target: impl Into<Option<TargetSelection>>,
1096 ) -> Option<gha::Group> {
1097 self.msg(Kind::Check, self.config.stage, what, self.config.build, target)
1098 }
1099
1100 #[must_use = "Groups should not be dropped until the Step finishes running"]
1101 #[track_caller]
1102 fn msg_doc(
1103 &self,
1104 compiler: Compiler,
1105 what: impl Display,
1106 target: impl Into<Option<TargetSelection>> + Copy,
1107 ) -> Option<gha::Group> {
1108 self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into())
1109 }
1110
1111 #[must_use = "Groups should not be dropped until the Step finishes running"]
1112 #[track_caller]
1113 fn msg_build(
1114 &self,
1115 compiler: Compiler,
1116 what: impl Display,
1117 target: impl Into<Option<TargetSelection>>,
1118 ) -> Option<gha::Group> {
1119 self.msg(Kind::Build, compiler.stage, what, compiler.host, target)
1120 }
1121
1122 #[must_use = "Groups should not be dropped until the Step finishes running"]
1126 #[track_caller]
1127 fn msg(
1128 &self,
1129 action: impl Into<Kind>,
1130 stage: u32,
1131 what: impl Display,
1132 host: impl Into<Option<TargetSelection>>,
1133 target: impl Into<Option<TargetSelection>>,
1134 ) -> Option<gha::Group> {
1135 let action = action.into().description();
1136 let msg = |fmt| format!("{action} stage{stage} {what}{fmt}");
1137 let msg = if let Some(target) = target.into() {
1138 let host = host.into().unwrap();
1139 if host == target {
1140 msg(format_args!(" ({target})"))
1141 } else {
1142 msg(format_args!(" ({host} -> {target})"))
1143 }
1144 } else {
1145 msg(format_args!(""))
1146 };
1147 self.group(&msg)
1148 }
1149
1150 #[must_use = "Groups should not be dropped until the Step finishes running"]
1154 #[track_caller]
1155 fn msg_unstaged(
1156 &self,
1157 action: impl Into<Kind>,
1158 what: impl Display,
1159 target: TargetSelection,
1160 ) -> Option<gha::Group> {
1161 let action = action.into().description();
1162 let msg = format!("{action} {what} for {target}");
1163 self.group(&msg)
1164 }
1165
1166 #[must_use = "Groups should not be dropped until the Step finishes running"]
1167 #[track_caller]
1168 fn msg_sysroot_tool(
1169 &self,
1170 action: impl Into<Kind>,
1171 stage: u32,
1172 what: impl Display,
1173 host: TargetSelection,
1174 target: TargetSelection,
1175 ) -> Option<gha::Group> {
1176 let action = action.into().description();
1177 let msg = |fmt| format!("{action} {what} {fmt}");
1178 let msg = if host == target {
1179 msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1))
1180 } else {
1181 msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1))
1182 };
1183 self.group(&msg)
1184 }
1185
1186 #[track_caller]
1187 fn group(&self, msg: &str) -> Option<gha::Group> {
1188 match self.config.dry_run {
1189 DryRun::SelfCheck => None,
1190 DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)),
1191 }
1192 }
1193
1194 fn jobs(&self) -> u32 {
1197 self.config.jobs.unwrap_or_else(|| {
1198 std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1199 })
1200 }
1201
1202 fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
1203 if !self.config.rust_remap_debuginfo {
1204 return None;
1205 }
1206
1207 match which {
1208 GitRepo::Rustc => {
1209 let sha = self.rust_sha().unwrap_or(&self.version);
1210 Some(format!("/rustc/{sha}"))
1211 }
1212 GitRepo::Llvm => Some(String::from("/rustc/llvm")),
1213 }
1214 }
1215
1216 fn cc(&self, target: TargetSelection) -> PathBuf {
1218 if self.config.dry_run() {
1219 return PathBuf::new();
1220 }
1221 self.cc.borrow()[&target].path().into()
1222 }
1223
1224 fn cc_tool(&self, target: TargetSelection) -> Tool {
1226 self.cc.borrow()[&target].clone()
1227 }
1228
1229 fn cxx_tool(&self, target: TargetSelection) -> Tool {
1231 self.cxx.borrow()[&target].clone()
1232 }
1233
1234 fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {
1237 if self.config.dry_run() {
1238 return Vec::new();
1239 }
1240 let base = match c {
1241 CLang::C => self.cc.borrow()[&target].clone(),
1242 CLang::Cxx => self.cxx.borrow()[&target].clone(),
1243 };
1244
1245 base.args()
1248 .iter()
1249 .map(|s| s.to_string_lossy().into_owned())
1250 .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
1251 .collect::<Vec<String>>()
1252 }
1253
1254 fn cc_unhandled_cflags(
1256 &self,
1257 target: TargetSelection,
1258 which: GitRepo,
1259 c: CLang,
1260 ) -> Vec<String> {
1261 let mut base = Vec::new();
1262
1263 if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {
1267 base.push("-stdlib=libc++".into());
1268 }
1269
1270 if &*target.triple == "i686-pc-windows-gnu" {
1274 base.push("-fno-omit-frame-pointer".into());
1275 }
1276
1277 if let Some(map_to) = self.debuginfo_map_to(which) {
1278 let map = format!("{}={}", self.src.display(), map_to);
1279 let cc = self.cc(target);
1280 if cc.ends_with("clang") || cc.ends_with("gcc") {
1281 base.push(format!("-fdebug-prefix-map={map}"));
1282 } else if cc.ends_with("clang-cl.exe") {
1283 base.push("-Xclang".into());
1284 base.push(format!("-fdebug-prefix-map={map}"));
1285 }
1286 }
1287 base
1288 }
1289
1290 fn ar(&self, target: TargetSelection) -> Option<PathBuf> {
1292 if self.config.dry_run() {
1293 return None;
1294 }
1295 self.ar.borrow().get(&target).cloned()
1296 }
1297
1298 fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {
1300 if self.config.dry_run() {
1301 return None;
1302 }
1303 self.ranlib.borrow().get(&target).cloned()
1304 }
1305
1306 fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {
1308 if self.config.dry_run() {
1309 return Ok(PathBuf::new());
1310 }
1311 match self.cxx.borrow().get(&target) {
1312 Some(p) => Ok(p.path().into()),
1313 None => Err(format!("target `{target}` is not configured as a host, only as a target")),
1314 }
1315 }
1316
1317 fn linker(&self, target: TargetSelection) -> Option<PathBuf> {
1319 if self.config.dry_run() {
1320 return Some(PathBuf::new());
1321 }
1322 if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
1323 {
1324 Some(linker)
1325 } else if target.contains("vxworks") {
1326 Some(self.cxx.borrow()[&target].path().into())
1329 } else if !self.config.is_host_target(target)
1330 && helpers::use_host_linker(target)
1331 && !target.is_msvc()
1332 {
1333 Some(self.cc(target))
1334 } else if self.config.lld_mode.is_used()
1335 && self.is_lld_direct_linker(target)
1336 && self.build == target
1337 {
1338 match self.config.lld_mode {
1339 LldMode::SelfContained => Some(self.initial_lld.clone()),
1340 LldMode::External => Some("lld".into()),
1341 LldMode::Unused => None,
1342 }
1343 } else {
1344 None
1345 }
1346 }
1347
1348 fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
1351 target.is_msvc()
1352 }
1353
1354 fn crt_static(&self, target: TargetSelection) -> Option<bool> {
1356 if target.contains("pc-windows-msvc") {
1357 Some(true)
1358 } else {
1359 self.config.target_config.get(&target).and_then(|t| t.crt_static)
1360 }
1361 }
1362
1363 fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
1365 self.config
1366 .target_config
1367 .get(&target)
1368 .and_then(|t| t.musl_root.as_ref())
1369 .or(self.config.musl_root.as_ref())
1370 .map(|p| &**p)
1371 }
1372
1373 fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1375 let t = self.config.target_config.get(&target)?;
1376 if let libdir @ Some(_) = &t.musl_libdir {
1377 return libdir.clone();
1378 }
1379 self.musl_root(target).map(|root| root.join("lib"))
1380 }
1381
1382 fn wasi_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1389 let configured =
1390 self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p);
1391 if let Some(path) = configured {
1392 return Some(path.join("lib").join(target.to_string()));
1393 }
1394 let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?);
1395 env_root.push("share");
1396 env_root.push("wasi-sysroot");
1397 env_root.push("lib");
1398 env_root.push(target.to_string());
1399 Some(env_root)
1400 }
1401
1402 fn no_std(&self, target: TargetSelection) -> Option<bool> {
1404 self.config.target_config.get(&target).map(|t| t.no_std)
1405 }
1406
1407 fn remote_tested(&self, target: TargetSelection) -> bool {
1410 self.qemu_rootfs(target).is_some()
1411 || target.contains("android")
1412 || env::var_os("TEST_DEVICE_ADDR").is_some()
1413 }
1414
1415 fn runner(&self, target: TargetSelection) -> Option<String> {
1421 let configured_runner =
1422 self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p);
1423 if let Some(runner) = configured_runner {
1424 return Some(runner.to_owned());
1425 }
1426
1427 if target.starts_with("wasm") && target.contains("wasi") {
1428 self.default_wasi_runner(target)
1429 } else {
1430 None
1431 }
1432 }
1433
1434 fn default_wasi_runner(&self, target: TargetSelection) -> Option<String> {
1438 let mut finder = crate::core::sanity::Finder::new();
1439
1440 if let Some(path) = finder.maybe_have("wasmtime") {
1444 if let Ok(mut path) = path.into_os_string().into_string() {
1445 path.push_str(" run -C cache=n --dir .");
1446 path.push_str(" --env RUSTC_BOOTSTRAP");
1453
1454 if target.contains("wasip2") {
1455 path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
1456 }
1457
1458 return Some(path);
1459 }
1460 }
1461
1462 None
1463 }
1464
1465 fn tool_enabled(&self, tool: &str) -> bool {
1470 if !self.config.extended {
1471 return false;
1472 }
1473 match &self.config.tools {
1474 Some(set) => set.contains(tool),
1475 None => true,
1476 }
1477 }
1478
1479 fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {
1485 self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)
1486 }
1487
1488 fn python(&self) -> &Path {
1490 if self.config.build.ends_with("apple-darwin") {
1491 Path::new("/usr/bin/python3")
1495 } else {
1496 self.config
1497 .python
1498 .as_ref()
1499 .expect("python is required for running LLDB or rustdoc tests")
1500 }
1501 }
1502
1503 fn extended_error_dir(&self) -> PathBuf {
1505 self.out.join("tmp/extended-error-metadata")
1506 }
1507
1508 fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {
1527 !self.config.full_bootstrap
1528 && !self.config.download_rustc()
1529 && stage >= 2
1530 && (self.hosts.contains(&target) || target == self.build)
1531 }
1532
1533 fn force_use_stage2(&self, stage: u32) -> bool {
1539 self.config.download_rustc() && stage >= 2
1540 }
1541
1542 fn release(&self, num: &str) -> String {
1548 match &self.config.channel[..] {
1549 "stable" => num.to_string(),
1550 "beta" => {
1551 if !self.config.omit_git_hash {
1552 format!("{}-beta.{}", num, self.beta_prerelease_version())
1553 } else {
1554 format!("{num}-beta")
1555 }
1556 }
1557 "nightly" => format!("{num}-nightly"),
1558 _ => format!("{num}-dev"),
1559 }
1560 }
1561
1562 fn beta_prerelease_version(&self) -> u32 {
1563 fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
1564 let version = fs::read_to_string(version_file).ok()?;
1565
1566 helpers::extract_beta_rev(&version)
1567 }
1568
1569 if let Some(s) = self.prerelease_version.get() {
1570 return s;
1571 }
1572
1573 let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {
1577 helpers::git(Some(&self.src))
1581 .arg("rev-list")
1582 .arg("--count")
1583 .arg("--merges")
1584 .arg(format!(
1585 "refs/remotes/origin/{}..HEAD",
1586 self.config.stage0_metadata.config.nightly_branch
1587 ))
1588 .run_always()
1589 .run_capture(self)
1590 .stdout()
1591 });
1592 let n = count.trim().parse().unwrap();
1593 self.prerelease_version.set(Some(n));
1594 n
1595 }
1596
1597 fn rust_release(&self) -> String {
1599 self.release(&self.version)
1600 }
1601
1602 fn package_vers(&self, num: &str) -> String {
1609 match &self.config.channel[..] {
1610 "stable" => num.to_string(),
1611 "beta" => "beta".to_string(),
1612 "nightly" => "nightly".to_string(),
1613 _ => format!("{num}-dev"),
1614 }
1615 }
1616
1617 fn rust_package_vers(&self) -> String {
1619 self.package_vers(&self.version)
1620 }
1621
1622 fn rust_version(&self) -> String {
1628 let mut version = self.rust_info().version(self, &self.version);
1629 if let Some(ref s) = self.config.description {
1630 if !s.is_empty() {
1631 version.push_str(" (");
1632 version.push_str(s);
1633 version.push(')');
1634 }
1635 }
1636 version
1637 }
1638
1639 fn rust_sha(&self) -> Option<&str> {
1641 self.rust_info().sha()
1642 }
1643
1644 fn release_num(&self, package: &str) -> String {
1646 let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml"));
1647 let toml = t!(fs::read_to_string(toml_file_name));
1648 for line in toml.lines() {
1649 if let Some(stripped) =
1650 line.strip_prefix("version = \"").and_then(|s| s.strip_suffix('"'))
1651 {
1652 return stripped.to_owned();
1653 }
1654 }
1655
1656 panic!("failed to find version in {package}'s Cargo.toml")
1657 }
1658
1659 fn unstable_features(&self) -> bool {
1662 !matches!(&self.config.channel[..], "stable" | "beta")
1663 }
1664
1665 fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {
1669 let mut ret = Vec::new();
1670 let mut list = vec![root.to_owned()];
1671 let mut visited = HashSet::new();
1672 while let Some(krate) = list.pop() {
1673 let krate = self
1674 .crates
1675 .get(&krate)
1676 .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));
1677 ret.push(krate);
1678 for dep in &krate.deps {
1679 if !self.crates.contains_key(dep) {
1680 continue;
1682 }
1683 if visited.insert(dep)
1689 && (dep != "profiler_builtins"
1690 || target
1691 .map(|t| self.config.profiler_enabled(t))
1692 .unwrap_or_else(|| self.config.any_profiler_enabled()))
1693 && (dep != "rustc_codegen_llvm"
1694 || self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))
1695 {
1696 list.push(dep.clone());
1697 }
1698 }
1699 }
1700 ret.sort_unstable_by_key(|krate| krate.name.clone()); ret
1702 }
1703
1704 fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> {
1705 if self.config.dry_run() {
1706 return Vec::new();
1707 }
1708
1709 if !stamp.path().exists() {
1710 eprintln!(
1711 "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
1712 stamp.path().display()
1713 );
1714 crate::exit!(1);
1715 }
1716
1717 let mut paths = Vec::new();
1718 let contents = t!(fs::read(stamp.path()), stamp.path());
1719 for part in contents.split(|b| *b == 0) {
1722 if part.is_empty() {
1723 continue;
1724 }
1725 let dependency_type = match part[0] as char {
1726 'h' => DependencyType::Host,
1727 's' => DependencyType::TargetSelfContained,
1728 't' => DependencyType::Target,
1729 _ => unreachable!(),
1730 };
1731 let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
1732 paths.push((path, dependency_type));
1733 }
1734 paths
1735 }
1736
1737 pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {
1742 self.copy_link_internal(src, dst, true);
1743 }
1744
1745 pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
1750 self.copy_link_internal(src, dst, false);
1751
1752 if file_type.could_have_split_debuginfo() {
1753 if let Some(dbg_file) = split_debuginfo(src) {
1754 self.copy_link_internal(
1755 &dbg_file,
1756 &dst.with_extension(dbg_file.extension().unwrap()),
1757 false,
1758 );
1759 }
1760 }
1761 }
1762
1763 fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
1764 if self.config.dry_run() {
1765 return;
1766 }
1767 self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
1768 if src == dst {
1769 return;
1770 }
1771 if let Err(e) = fs::remove_file(dst) {
1772 if cfg!(windows) && e.kind() != io::ErrorKind::NotFound {
1773 let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
1776 let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
1777 }
1778 }
1779 let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
1780 let mut src = src.to_path_buf();
1781 if metadata.file_type().is_symlink() {
1782 if dereference_symlinks {
1783 src = t!(fs::canonicalize(src));
1784 } else {
1785 let link = t!(fs::read_link(src));
1786 t!(self.symlink_file(link, dst));
1787 return;
1788 }
1789 }
1790 if let Ok(()) = fs::hard_link(&src, dst) {
1791 } else {
1794 if let Err(e) = fs::copy(&src, dst) {
1795 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
1796 }
1797 t!(fs::set_permissions(dst, metadata.permissions()));
1798
1799 let file_times = fs::FileTimes::new()
1802 .set_accessed(t!(metadata.accessed()))
1803 .set_modified(t!(metadata.modified()));
1804 t!(set_file_times(dst, file_times));
1805 }
1806 }
1807
1808 pub fn cp_link_r(&self, src: &Path, dst: &Path) {
1812 if self.config.dry_run() {
1813 return;
1814 }
1815 for f in self.read_dir(src) {
1816 let path = f.path();
1817 let name = path.file_name().unwrap();
1818 let dst = dst.join(name);
1819 if t!(f.file_type()).is_dir() {
1820 t!(fs::create_dir_all(&dst));
1821 self.cp_link_r(&path, &dst);
1822 } else {
1823 self.copy_link(&path, &dst, FileType::Regular);
1824 }
1825 }
1826 }
1827
1828 pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1834 self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)
1836 }
1837
1838 fn cp_link_filtered_recurse(
1840 &self,
1841 src: &Path,
1842 dst: &Path,
1843 relative: &Path,
1844 filter: &dyn Fn(&Path) -> bool,
1845 ) {
1846 for f in self.read_dir(src) {
1847 let path = f.path();
1848 let name = path.file_name().unwrap();
1849 let dst = dst.join(name);
1850 let relative = relative.join(name);
1851 if filter(&relative) {
1853 if t!(f.file_type()).is_dir() {
1854 let _ = fs::remove_dir_all(&dst);
1855 self.create_dir(&dst);
1856 self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
1857 } else {
1858 let _ = fs::remove_file(&dst);
1859 self.copy_link(&path, &dst, FileType::Regular);
1860 }
1861 }
1862 }
1863 }
1864
1865 fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {
1866 let file_name = src.file_name().unwrap();
1867 let dest = dest_folder.join(file_name);
1868 self.copy_link(src, &dest, FileType::Regular);
1869 }
1870
1871 fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {
1872 if self.config.dry_run() {
1873 return;
1874 }
1875 let dst = dstdir.join(src.file_name().unwrap());
1876 self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
1877 t!(fs::create_dir_all(dstdir));
1878 if !src.exists() {
1879 panic!("ERROR: File \"{}\" not found!", src.display());
1880 }
1881
1882 self.copy_link_internal(src, &dst, true);
1883 chmod(&dst, file_type.perms());
1884
1885 if file_type.could_have_split_debuginfo() {
1887 if let Some(dbg_file) = split_debuginfo(src) {
1888 self.install(&dbg_file, dstdir, FileType::Regular);
1889 }
1890 }
1891 }
1892
1893 fn read(&self, path: &Path) -> String {
1894 if self.config.dry_run() {
1895 return String::new();
1896 }
1897 t!(fs::read_to_string(path))
1898 }
1899
1900 fn create_dir(&self, dir: &Path) {
1901 if self.config.dry_run() {
1902 return;
1903 }
1904 t!(fs::create_dir_all(dir))
1905 }
1906
1907 fn remove_dir(&self, dir: &Path) {
1908 if self.config.dry_run() {
1909 return;
1910 }
1911 t!(fs::remove_dir_all(dir))
1912 }
1913
1914 fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {
1915 let iter = match fs::read_dir(dir) {
1916 Ok(v) => v,
1917 Err(_) if self.config.dry_run() => return vec![].into_iter(),
1918 Err(err) => panic!("could not read dir {dir:?}: {err:?}"),
1919 };
1920 iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
1921 }
1922
1923 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {
1924 #[cfg(unix)]
1925 use std::os::unix::fs::symlink as symlink_file;
1926 #[cfg(windows)]
1927 use std::os::windows::fs::symlink_file;
1928 if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
1929 }
1930
1931 fn ninja(&self) -> bool {
1934 let mut cmd_finder = crate::core::sanity::Finder::new();
1935
1936 if self.config.ninja_in_file {
1937 if cmd_finder.maybe_have("ninja-build").is_none()
1940 && cmd_finder.maybe_have("ninja").is_none()
1941 {
1942 eprintln!(
1943 "
1944Couldn't find required command: ninja (or ninja-build)
1945
1946You should install ninja as described at
1947<https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
1948or set `ninja = false` in the `[llvm]` section of `bootstrap.toml`.
1949Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
1950to download LLVM rather than building it.
1951"
1952 );
1953 exit!(1);
1954 }
1955 }
1956
1957 if !self.config.ninja_in_file
1965 && self.config.build.is_msvc()
1966 && cmd_finder.maybe_have("ninja").is_some()
1967 {
1968 return true;
1969 }
1970
1971 self.config.ninja_in_file
1972 }
1973
1974 pub fn colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1975 self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f)
1976 }
1977
1978 pub fn colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1979 self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f)
1980 }
1981
1982 fn colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R
1983 where
1984 C: Fn(ColorChoice) -> StandardStream,
1985 F: FnOnce(&mut dyn WriteColor) -> R,
1986 {
1987 let choice = match self.config.color {
1988 flags::Color::Always => ColorChoice::Always,
1989 flags::Color::Never => ColorChoice::Never,
1990 flags::Color::Auto if !is_tty => ColorChoice::Never,
1991 flags::Color::Auto => ColorChoice::Auto,
1992 };
1993 let mut stream = constructor(choice);
1994 let result = f(&mut stream);
1995 stream.reset().unwrap();
1996 result
1997 }
1998}
1999
2000#[cfg(unix)]
2001fn chmod(path: &Path, perms: u32) {
2002 use std::os::unix::fs::*;
2003 t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
2004}
2005#[cfg(windows)]
2006fn chmod(_path: &Path, _perms: u32) {}
2007
2008impl Compiler {
2009 pub fn new(stage: u32, host: TargetSelection) -> Self {
2010 Self { stage, host, forced_compiler: false }
2011 }
2012
2013 pub fn forced_compiler(&mut self, forced_compiler: bool) {
2014 self.forced_compiler = forced_compiler;
2015 }
2016
2017 pub fn with_stage(mut self, stage: u32) -> Compiler {
2018 self.stage = stage;
2019 self
2020 }
2021
2022 pub fn is_snapshot(&self, build: &Build) -> bool {
2024 self.stage == 0 && self.host == build.build
2025 }
2026
2027 pub fn is_forced_compiler(&self) -> bool {
2029 self.forced_compiler
2030 }
2031}
2032
2033fn envify(s: &str) -> String {
2034 s.chars()
2035 .map(|c| match c {
2036 '-' => '_',
2037 c => c,
2038 })
2039 .flat_map(|c| c.to_uppercase())
2040 .collect()
2041}
2042
2043pub fn prepare_behaviour_dump_dir(build: &Build) {
2045 static INITIALIZED: OnceLock<bool> = OnceLock::new();
2046
2047 let dump_path = build.out.join("bootstrap-shims-dump");
2048
2049 let initialized = INITIALIZED.get().unwrap_or(&false);
2050 if !initialized {
2051 if dump_path.exists() {
2053 t!(fs::remove_dir_all(&dump_path));
2054 }
2055
2056 t!(fs::create_dir_all(&dump_path));
2057
2058 t!(INITIALIZED.set(true));
2059 }
2060}