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}
329
330impl Build {
331 pub fn new(mut config: Config) -> Build {
336 let src = config.src.clone();
337 let out = config.out.clone();
338
339 #[cfg(unix)]
340 let is_sudo = match env::var_os("SUDO_USER") {
343 Some(_sudo_user) => {
344 let uid = unsafe { libc::getuid() };
349 uid == 0
350 }
351 None => false,
352 };
353 #[cfg(not(unix))]
354 let is_sudo = false;
355
356 let rust_info = config.rust_info.clone();
357 let cargo_info = config.cargo_info.clone();
358 let rust_analyzer_info = config.rust_analyzer_info.clone();
359 let clippy_info = config.clippy_info.clone();
360 let miri_info = config.miri_info.clone();
361 let rustfmt_info = config.rustfmt_info.clone();
362 let enzyme_info = config.enzyme_info.clone();
363 let in_tree_llvm_info = config.in_tree_llvm_info.clone();
364 let in_tree_gcc_info = config.in_tree_gcc_info.clone();
365
366 let initial_target_libdir =
367 output(Command::new(&config.initial_rustc).args(["--print", "target-libdir"]))
368 .trim()
369 .to_owned();
370
371 let initial_target_dir = Path::new(&initial_target_libdir)
372 .parent()
373 .unwrap_or_else(|| panic!("{initial_target_libdir} has no parent"));
374
375 let initial_lld = initial_target_dir.join("bin").join("rust-lld");
376
377 let initial_relative_libdir = if cfg!(test) {
378 PathBuf::default()
380 } else {
381 let ancestor = initial_target_dir.ancestors().nth(2).unwrap_or_else(|| {
382 panic!("Not enough ancestors for {}", initial_target_dir.display())
383 });
384
385 ancestor
386 .strip_prefix(&config.initial_sysroot)
387 .unwrap_or_else(|_| {
388 panic!(
389 "Couldn’t resolve the initial relative libdir from {}",
390 initial_target_dir.display()
391 )
392 })
393 .to_path_buf()
394 };
395
396 let version = std::fs::read_to_string(src.join("src").join("version"))
397 .expect("failed to read src/version");
398 let version = version.trim();
399
400 let mut bootstrap_out = std::env::current_exe()
401 .expect("could not determine path to running process")
402 .parent()
403 .unwrap()
404 .to_path_buf();
405 if bootstrap_out.ends_with("deps") {
408 bootstrap_out.pop();
409 }
410 if !bootstrap_out.join(exe("rustc", config.build)).exists() && !cfg!(test) {
411 panic!(
413 "`rustc` not found in {}, run `cargo build --bins` before `cargo run`",
414 bootstrap_out.display()
415 )
416 }
417
418 if rust_info.is_from_tarball() && config.description.is_none() {
419 config.description = Some("built from a source tarball".to_owned());
420 }
421
422 let mut build = Build {
423 initial_lld,
424 initial_relative_libdir,
425 initial_rustc: config.initial_rustc.clone(),
426 initial_rustdoc: config.initial_rustc.with_file_name(exe("rustdoc", config.build)),
427 initial_cargo: config.initial_cargo.clone(),
428 initial_sysroot: config.initial_sysroot.clone(),
429 local_rebuild: config.local_rebuild,
430 fail_fast: config.cmd.fail_fast(),
431 doc_tests: config.cmd.doc_tests(),
432 verbosity: config.verbose,
433
434 build: config.build,
435 hosts: config.hosts.clone(),
436 targets: config.targets.clone(),
437
438 config,
439 version: version.to_string(),
440 src,
441 out,
442 bootstrap_out,
443
444 cargo_info,
445 rust_analyzer_info,
446 clippy_info,
447 miri_info,
448 rustfmt_info,
449 enzyme_info,
450 in_tree_llvm_info,
451 in_tree_gcc_info,
452 cc: RefCell::new(HashMap::new()),
453 cxx: RefCell::new(HashMap::new()),
454 ar: RefCell::new(HashMap::new()),
455 ranlib: RefCell::new(HashMap::new()),
456 crates: HashMap::new(),
457 crate_paths: HashMap::new(),
458 is_sudo,
459 delayed_failures: RefCell::new(Vec::new()),
460 prerelease_version: Cell::new(None),
461
462 #[cfg(feature = "build-metrics")]
463 metrics: crate::utils::metrics::BuildMetrics::init(),
464 };
465
466 let local_version_verbose =
469 output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
470 let local_release = local_version_verbose
471 .lines()
472 .filter_map(|x| x.strip_prefix("release:"))
473 .next()
474 .unwrap()
475 .trim();
476 if local_release.split('.').take(2).eq(version.split('.').take(2)) {
477 build.verbose(|| println!("auto-detected local-rebuild {local_release}"));
478 build.local_rebuild = true;
479 }
480
481 build.verbose(|| println!("finding compilers"));
482 utils::cc_detect::find(&build);
483 if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
489 build.verbose(|| println!("running sanity check"));
490 crate::core::sanity::check(&mut build);
491
492 let rust_submodules = ["library/backtrace", "library/stdarch"];
495 for s in rust_submodules {
496 build.require_submodule(
497 s,
498 Some(
499 "The submodule is required for the standard library \
500 and the main Cargo workspace.",
501 ),
502 );
503 }
504 build.update_existing_submodules();
506
507 build.verbose(|| println!("learning about cargo"));
508 crate::core::metadata::build(&mut build);
509 }
510
511 let build_triple = build.out.join(build.build);
513 t!(fs::create_dir_all(&build_triple));
514 let host = build.out.join("host");
515 if host.is_symlink() {
516 #[cfg(windows)]
519 t!(fs::remove_dir(&host));
520 #[cfg(not(windows))]
521 t!(fs::remove_file(&host));
522 }
523 t!(
524 symlink_dir(&build.config, &build_triple, &host),
525 format!("symlink_dir({} => {}) failed", host.display(), build_triple.display())
526 );
527
528 build
529 }
530
531 #[cfg_attr(
540 feature = "tracing",
541 instrument(
542 level = "trace",
543 name = "Build::require_submodule",
544 skip_all,
545 fields(submodule = submodule),
546 ),
547 )]
548 pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) {
549 if self.rust_info().is_from_tarball() {
550 return;
551 }
552
553 if cfg!(test) && !self.config.submodules() {
556 return;
557 }
558 self.config.update_submodule(submodule);
559 let absolute_path = self.config.src.join(submodule);
560 if !absolute_path.exists() || dir_is_empty(&absolute_path) {
561 let maybe_enable = if !self.config.submodules()
562 && self.config.rust_info.is_managed_git_subrepository()
563 {
564 "\nConsider setting `build.submodules = true` or manually initializing the submodules."
565 } else {
566 ""
567 };
568 let err_hint = err_hint.map_or_else(String::new, |e| format!("\n{e}"));
569 eprintln!(
570 "submodule {submodule} does not appear to be checked out, \
571 but it is required for this step{maybe_enable}{err_hint}"
572 );
573 exit!(1);
574 }
575 }
576
577 pub fn require_and_update_all_submodules(&self) {
580 for submodule in build_helper::util::parse_gitmodules(&self.src) {
581 self.require_submodule(submodule, None);
582 }
583 }
584
585 fn update_existing_submodules(&self) {
588 if !self.config.submodules() {
591 return;
592 }
593 let output = helpers::git(Some(&self.src))
594 .args(["config", "--file"])
595 .arg(".gitmodules")
596 .args(["--get-regexp", "path"])
597 .run_capture(self)
598 .stdout();
599 std::thread::scope(|s| {
600 for line in output.lines() {
603 let submodule = line.split_once(' ').unwrap().1;
604 let config = self.config.clone();
605 s.spawn(move || {
606 Self::update_existing_submodule(&config, submodule);
607 });
608 }
609 });
610 }
611
612 pub fn update_existing_submodule(config: &Config, submodule: &str) {
614 if !config.submodules() {
616 return;
617 }
618
619 if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
620 config.update_submodule(submodule);
621 }
622 }
623
624 #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))]
626 pub fn build(&mut self) {
627 trace!("setting up job management");
628 unsafe {
629 crate::utils::job::setup(self);
630 }
631
632 {
634 #[cfg(feature = "tracing")]
635 let _hardcoded_span = span!(
636 tracing::Level::DEBUG,
637 "handling hardcoded subcommands (Format, Suggest, Perf)"
638 )
639 .entered();
640
641 match &self.config.cmd {
642 Subcommand::Format { check, all } => {
643 return core::build_steps::format::format(
644 &builder::Builder::new(self),
645 *check,
646 *all,
647 &self.config.paths,
648 );
649 }
650 Subcommand::Suggest { run } => {
651 return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
652 }
653 Subcommand::Perf(args) => {
654 return core::build_steps::perf::perf(&builder::Builder::new(self), args);
655 }
656 _cmd => {
657 debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");
658 }
659 }
660
661 debug!("handling subcommand normally");
662 }
663
664 if !self.config.dry_run() {
665 #[cfg(feature = "tracing")]
666 let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered();
667
668 {
671 #[cfg(feature = "tracing")]
672 let _sanity_check_span =
673 span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered();
674 self.config.dry_run = DryRun::SelfCheck;
675 let builder = builder::Builder::new(self);
676 builder.execute_cli();
677 }
678
679 {
681 #[cfg(feature = "tracing")]
682 let _actual_run_span =
683 span!(tracing::Level::DEBUG, "(2) executing actual run").entered();
684 self.config.dry_run = DryRun::Disabled;
685 let builder = builder::Builder::new(self);
686 builder.execute_cli();
687 }
688 } else {
689 #[cfg(feature = "tracing")]
690 let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered();
691
692 let builder = builder::Builder::new(self);
693 builder.execute_cli();
694 }
695
696 #[cfg(feature = "tracing")]
697 debug!("checking for postponed test failures from `test --no-fail-fast`");
698
699 let failures = self.delayed_failures.borrow();
701 if !failures.is_empty() {
702 eprintln!("\n{} command(s) did not execute successfully:\n", failures.len());
703 for failure in failures.iter() {
704 eprintln!(" - {failure}\n");
705 }
706 exit!(1);
707 }
708
709 #[cfg(feature = "build-metrics")]
710 self.metrics.persist(self);
711 }
712
713 fn rust_info(&self) -> &GitInfo {
714 &self.config.rust_info
715 }
716
717 fn std_features(&self, target: TargetSelection) -> String {
720 let mut features: BTreeSet<&str> =
721 self.config.rust_std_features.iter().map(|s| s.as_str()).collect();
722
723 match self.config.llvm_libunwind(target) {
724 LlvmLibunwind::InTree => features.insert("llvm-libunwind"),
725 LlvmLibunwind::System => features.insert("system-llvm-libunwind"),
726 LlvmLibunwind::No => false,
727 };
728
729 if self.config.backtrace {
730 features.insert("backtrace");
731 }
732
733 if self.config.profiler_enabled(target) {
734 features.insert("profiler");
735 }
736
737 if target.contains("zkvm") {
739 features.insert("compiler-builtins-mem");
740 }
741
742 features.into_iter().collect::<Vec<_>>().join(" ")
743 }
744
745 fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {
747 let possible_features_by_crates: HashSet<_> = crates
748 .iter()
749 .flat_map(|krate| &self.crates[krate].features)
750 .map(std::ops::Deref::deref)
751 .collect();
752 let check = |feature: &str| -> bool {
753 crates.is_empty() || possible_features_by_crates.contains(feature)
754 };
755 let mut features = vec![];
756 if self.config.jemalloc(target) && check("jemalloc") {
757 features.push("jemalloc");
758 }
759 if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
760 features.push("llvm");
761 }
762 if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {
764 features.push("rustc_randomized_layouts");
765 }
766
767 if !self.config.rust_debug_logging && check("max_level_info") {
773 features.push("max_level_info");
774 }
775
776 features.join(" ")
777 }
778
779 fn cargo_dir(&self) -> &'static str {
782 if self.config.rust_optimize.is_release() { "release" } else { "debug" }
783 }
784
785 fn tools_dir(&self, compiler: Compiler) -> PathBuf {
786 let out = self.out.join(compiler.host).join(format!("stage{}-tools-bin", compiler.stage));
787 t!(fs::create_dir_all(&out));
788 out
789 }
790
791 fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
796 let suffix = match mode {
797 Mode::Std => "-std",
798 Mode::Rustc => "-rustc",
799 Mode::Codegen => "-codegen",
800 Mode::ToolBootstrap => "-bootstrap-tools",
801 Mode::ToolStd | Mode::ToolRustc => "-tools",
802 };
803 self.out.join(compiler.host).join(format!("stage{}{}", compiler.stage, suffix))
804 }
805
806 fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {
810 self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
811 }
812
813 fn llvm_out(&self, target: TargetSelection) -> PathBuf {
818 if self.config.llvm_from_ci && self.config.is_host_target(target) {
819 self.config.ci_llvm_root()
820 } else {
821 self.out.join(target).join("llvm")
822 }
823 }
824
825 fn enzyme_out(&self, target: TargetSelection) -> PathBuf {
826 self.out.join(&*target.triple).join("enzyme")
827 }
828
829 fn gcc_out(&self, target: TargetSelection) -> PathBuf {
830 self.out.join(&*target.triple).join("gcc")
831 }
832
833 fn lld_out(&self, target: TargetSelection) -> PathBuf {
834 self.out.join(target).join("lld")
835 }
836
837 fn doc_out(&self, target: TargetSelection) -> PathBuf {
839 self.out.join(target).join("doc")
840 }
841
842 fn json_doc_out(&self, target: TargetSelection) -> PathBuf {
844 self.out.join(target).join("json-doc")
845 }
846
847 fn test_out(&self, target: TargetSelection) -> PathBuf {
848 self.out.join(target).join("test")
849 }
850
851 fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
853 self.out.join(target).join("compiler-doc")
854 }
855
856 fn md_doc_out(&self, target: TargetSelection) -> PathBuf {
858 self.out.join(target).join("md-doc")
859 }
860
861 fn vendored_crates_path(&self) -> Option<PathBuf> {
863 if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None }
864 }
865
866 fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf {
868 let target_config = self.config.target_config.get(&target);
869 if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
870 s.to_path_buf()
871 } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
872 let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(self).stdout();
873 let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));
874 if filecheck.exists() {
875 filecheck
876 } else {
877 let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(self).stdout();
880 let lib_filecheck =
881 Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));
882 if lib_filecheck.exists() {
883 lib_filecheck
884 } else {
885 filecheck
889 }
890 }
891 } else {
892 let base = self.llvm_out(target).join("build");
893 let base = if !self.ninja() && target.is_msvc() {
894 if self.config.llvm_optimize {
895 if self.config.llvm_release_debuginfo {
896 base.join("RelWithDebInfo")
897 } else {
898 base.join("Release")
899 }
900 } else {
901 base.join("Debug")
902 }
903 } else {
904 base
905 };
906 base.join("bin").join(exe("FileCheck", target))
907 }
908 }
909
910 fn native_dir(&self, target: TargetSelection) -> PathBuf {
912 self.out.join(target).join("native")
913 }
914
915 fn test_helpers_out(&self, target: TargetSelection) -> PathBuf {
918 self.native_dir(target).join("rust-test-helpers")
919 }
920
921 fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) {
923 if env::var_os("RUST_TEST_THREADS").is_none() {
924 cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
925 }
926 }
927
928 fn rustc_snapshot_libdir(&self) -> PathBuf {
930 self.rustc_snapshot_sysroot().join(libdir(self.config.build))
931 }
932
933 fn rustc_snapshot_sysroot(&self) -> &Path {
935 static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();
936 SYSROOT_CACHE.get_or_init(|| {
937 let mut rustc = Command::new(&self.initial_rustc);
938 rustc.args(["--print", "sysroot"]);
939 output(&mut rustc).trim().into()
940 })
941 }
942
943 #[track_caller]
947 fn run(
948 &self,
949 command: &mut BootstrapCommand,
950 stdout: OutputMode,
951 stderr: OutputMode,
952 ) -> CommandOutput {
953 command.mark_as_executed();
954 if self.config.dry_run() && !command.run_always {
955 return CommandOutput::default();
956 }
957
958 #[cfg(feature = "tracing")]
959 let _run_span = trace_cmd!(command);
960
961 let created_at = command.get_created_location();
962 let executed_at = std::panic::Location::caller();
963
964 self.verbose(|| {
965 println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
966 });
967
968 let cmd = command.as_command_mut();
969 cmd.stdout(stdout.stdio());
970 cmd.stderr(stderr.stdio());
971
972 let output = cmd.output();
973
974 use std::fmt::Write;
975
976 let mut message = String::new();
977 let output: CommandOutput = match output {
978 Ok(output) if output.status.success() => {
980 CommandOutput::from_output(output, stdout, stderr)
981 }
982 Ok(output) => {
984 writeln!(
985 message,
986 r#"
987Command {command:?} did not execute successfully.
988Expected success, got {}
989Created at: {created_at}
990Executed at: {executed_at}"#,
991 output.status,
992 )
993 .unwrap();
994
995 let output: CommandOutput = CommandOutput::from_output(output, stdout, stderr);
996
997 if stdout.captures() {
1001 writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
1002 }
1003 if stderr.captures() {
1004 writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
1005 }
1006 output
1007 }
1008 Err(e) => {
1010 writeln!(
1011 message,
1012 "\n\nCommand {command:?} did not execute successfully.\
1013 \nIt was not possible to execute the command: {e:?}"
1014 )
1015 .unwrap();
1016 CommandOutput::did_not_start(stdout, stderr)
1017 }
1018 };
1019
1020 let fail = |message: &str, output: CommandOutput| -> ! {
1021 if self.is_verbose() {
1022 println!("{message}");
1023 } else {
1024 let (stdout, stderr) = (output.stdout_if_present(), output.stderr_if_present());
1025 if stdout.is_some() || stderr.is_some() {
1029 if let Some(stdout) =
1030 output.stdout_if_present().take_if(|s| !s.trim().is_empty())
1031 {
1032 println!("STDOUT:\n{stdout}\n");
1033 }
1034 if let Some(stderr) =
1035 output.stderr_if_present().take_if(|s| !s.trim().is_empty())
1036 {
1037 println!("STDERR:\n{stderr}\n");
1038 }
1039 println!("Command {command:?} has failed. Rerun with -v to see more details.");
1040 } else {
1041 println!("Command has failed. Rerun with -v to see more details.");
1042 }
1043 }
1044 exit!(1);
1045 };
1046
1047 if !output.is_success() {
1048 match command.failure_behavior {
1049 BehaviorOnFailure::DelayFail => {
1050 if self.fail_fast {
1051 fail(&message, output);
1052 }
1053
1054 let mut failures = self.delayed_failures.borrow_mut();
1055 failures.push(message);
1056 }
1057 BehaviorOnFailure::Exit => {
1058 fail(&message, output);
1059 }
1060 BehaviorOnFailure::Ignore => {
1061 }
1065 }
1066 }
1067 output
1068 }
1069
1070 pub fn is_verbose_than(&self, level: usize) -> bool {
1072 self.verbosity > level
1073 }
1074
1075 fn verbose_than(&self, level: usize, f: impl Fn()) {
1077 if self.is_verbose_than(level) {
1078 f()
1079 }
1080 }
1081
1082 fn info(&self, msg: &str) {
1083 match self.config.dry_run {
1084 DryRun::SelfCheck => (),
1085 DryRun::Disabled | DryRun::UserSelected => {
1086 println!("{msg}");
1087 }
1088 }
1089 }
1090
1091 #[must_use = "Groups should not be dropped until the Step finishes running"]
1092 #[track_caller]
1093 fn msg_clippy(
1094 &self,
1095 what: impl Display,
1096 target: impl Into<Option<TargetSelection>>,
1097 ) -> Option<gha::Group> {
1098 self.msg(Kind::Clippy, self.config.stage, what, self.config.build, target)
1099 }
1100
1101 #[must_use = "Groups should not be dropped until the Step finishes running"]
1102 #[track_caller]
1103 fn msg_check(
1104 &self,
1105 what: impl Display,
1106 target: impl Into<Option<TargetSelection>>,
1107 ) -> Option<gha::Group> {
1108 self.msg(Kind::Check, self.config.stage, what, self.config.build, target)
1109 }
1110
1111 #[must_use = "Groups should not be dropped until the Step finishes running"]
1112 #[track_caller]
1113 fn msg_doc(
1114 &self,
1115 compiler: Compiler,
1116 what: impl Display,
1117 target: impl Into<Option<TargetSelection>> + Copy,
1118 ) -> Option<gha::Group> {
1119 self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into())
1120 }
1121
1122 #[must_use = "Groups should not be dropped until the Step finishes running"]
1123 #[track_caller]
1124 fn msg_build(
1125 &self,
1126 compiler: Compiler,
1127 what: impl Display,
1128 target: impl Into<Option<TargetSelection>>,
1129 ) -> Option<gha::Group> {
1130 self.msg(Kind::Build, compiler.stage, what, compiler.host, target)
1131 }
1132
1133 #[must_use = "Groups should not be dropped until the Step finishes running"]
1137 #[track_caller]
1138 fn msg(
1139 &self,
1140 action: impl Into<Kind>,
1141 stage: u32,
1142 what: impl Display,
1143 host: impl Into<Option<TargetSelection>>,
1144 target: impl Into<Option<TargetSelection>>,
1145 ) -> Option<gha::Group> {
1146 let action = action.into().description();
1147 let msg = |fmt| format!("{action} stage{stage} {what}{fmt}");
1148 let msg = if let Some(target) = target.into() {
1149 let host = host.into().unwrap();
1150 if host == target {
1151 msg(format_args!(" ({target})"))
1152 } else {
1153 msg(format_args!(" ({host} -> {target})"))
1154 }
1155 } else {
1156 msg(format_args!(""))
1157 };
1158 self.group(&msg)
1159 }
1160
1161 #[must_use = "Groups should not be dropped until the Step finishes running"]
1165 #[track_caller]
1166 fn msg_unstaged(
1167 &self,
1168 action: impl Into<Kind>,
1169 what: impl Display,
1170 target: TargetSelection,
1171 ) -> Option<gha::Group> {
1172 let action = action.into().description();
1173 let msg = format!("{action} {what} for {target}");
1174 self.group(&msg)
1175 }
1176
1177 #[must_use = "Groups should not be dropped until the Step finishes running"]
1178 #[track_caller]
1179 fn msg_sysroot_tool(
1180 &self,
1181 action: impl Into<Kind>,
1182 stage: u32,
1183 what: impl Display,
1184 host: TargetSelection,
1185 target: TargetSelection,
1186 ) -> Option<gha::Group> {
1187 let action = action.into().description();
1188 let msg = |fmt| format!("{action} {what} {fmt}");
1189 let msg = if host == target {
1190 msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1))
1191 } else {
1192 msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1))
1193 };
1194 self.group(&msg)
1195 }
1196
1197 #[track_caller]
1198 fn group(&self, msg: &str) -> Option<gha::Group> {
1199 match self.config.dry_run {
1200 DryRun::SelfCheck => None,
1201 DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)),
1202 }
1203 }
1204
1205 fn jobs(&self) -> u32 {
1208 self.config.jobs.unwrap_or_else(|| {
1209 std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1210 })
1211 }
1212
1213 fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
1214 if !self.config.rust_remap_debuginfo {
1215 return None;
1216 }
1217
1218 match which {
1219 GitRepo::Rustc => {
1220 let sha = self.rust_sha().unwrap_or(&self.version);
1221 Some(format!("/rustc/{sha}"))
1222 }
1223 GitRepo::Llvm => Some(String::from("/rustc/llvm")),
1224 }
1225 }
1226
1227 fn cc(&self, target: TargetSelection) -> PathBuf {
1229 if self.config.dry_run() {
1230 return PathBuf::new();
1231 }
1232 self.cc.borrow()[&target].path().into()
1233 }
1234
1235 fn cc_tool(&self, target: TargetSelection) -> Tool {
1237 self.cc.borrow()[&target].clone()
1238 }
1239
1240 fn cxx_tool(&self, target: TargetSelection) -> Tool {
1242 self.cxx.borrow()[&target].clone()
1243 }
1244
1245 fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {
1248 if self.config.dry_run() {
1249 return Vec::new();
1250 }
1251 let base = match c {
1252 CLang::C => self.cc.borrow()[&target].clone(),
1253 CLang::Cxx => self.cxx.borrow()[&target].clone(),
1254 };
1255
1256 base.args()
1259 .iter()
1260 .map(|s| s.to_string_lossy().into_owned())
1261 .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
1262 .collect::<Vec<String>>()
1263 }
1264
1265 fn cc_unhandled_cflags(
1267 &self,
1268 target: TargetSelection,
1269 which: GitRepo,
1270 c: CLang,
1271 ) -> Vec<String> {
1272 let mut base = Vec::new();
1273
1274 if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {
1278 base.push("-stdlib=libc++".into());
1279 }
1280
1281 if &*target.triple == "i686-pc-windows-gnu" {
1285 base.push("-fno-omit-frame-pointer".into());
1286 }
1287
1288 if let Some(map_to) = self.debuginfo_map_to(which) {
1289 let map = format!("{}={}", self.src.display(), map_to);
1290 let cc = self.cc(target);
1291 if cc.ends_with("clang") || cc.ends_with("gcc") {
1292 base.push(format!("-fdebug-prefix-map={map}"));
1293 } else if cc.ends_with("clang-cl.exe") {
1294 base.push("-Xclang".into());
1295 base.push(format!("-fdebug-prefix-map={map}"));
1296 }
1297 }
1298 base
1299 }
1300
1301 fn ar(&self, target: TargetSelection) -> Option<PathBuf> {
1303 if self.config.dry_run() {
1304 return None;
1305 }
1306 self.ar.borrow().get(&target).cloned()
1307 }
1308
1309 fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {
1311 if self.config.dry_run() {
1312 return None;
1313 }
1314 self.ranlib.borrow().get(&target).cloned()
1315 }
1316
1317 fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {
1319 if self.config.dry_run() {
1320 return Ok(PathBuf::new());
1321 }
1322 match self.cxx.borrow().get(&target) {
1323 Some(p) => Ok(p.path().into()),
1324 None => Err(format!("target `{target}` is not configured as a host, only as a target")),
1325 }
1326 }
1327
1328 fn linker(&self, target: TargetSelection) -> Option<PathBuf> {
1330 if self.config.dry_run() {
1331 return Some(PathBuf::new());
1332 }
1333 if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
1334 {
1335 Some(linker)
1336 } else if target.contains("vxworks") {
1337 Some(self.cxx.borrow()[&target].path().into())
1340 } else if !self.config.is_host_target(target)
1341 && helpers::use_host_linker(target)
1342 && !target.is_msvc()
1343 {
1344 Some(self.cc(target))
1345 } else if self.config.lld_mode.is_used()
1346 && self.is_lld_direct_linker(target)
1347 && self.build == target
1348 {
1349 match self.config.lld_mode {
1350 LldMode::SelfContained => Some(self.initial_lld.clone()),
1351 LldMode::External => Some("lld".into()),
1352 LldMode::Unused => None,
1353 }
1354 } else {
1355 None
1356 }
1357 }
1358
1359 fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
1362 target.is_msvc()
1363 }
1364
1365 fn crt_static(&self, target: TargetSelection) -> Option<bool> {
1367 if target.contains("pc-windows-msvc") {
1368 Some(true)
1369 } else {
1370 self.config.target_config.get(&target).and_then(|t| t.crt_static)
1371 }
1372 }
1373
1374 fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
1376 self.config
1377 .target_config
1378 .get(&target)
1379 .and_then(|t| t.musl_root.as_ref())
1380 .or(self.config.musl_root.as_ref())
1381 .map(|p| &**p)
1382 }
1383
1384 fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1386 let t = self.config.target_config.get(&target)?;
1387 if let libdir @ Some(_) = &t.musl_libdir {
1388 return libdir.clone();
1389 }
1390 self.musl_root(target).map(|root| root.join("lib"))
1391 }
1392
1393 fn wasi_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1400 let configured =
1401 self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p);
1402 if let Some(path) = configured {
1403 return Some(path.join("lib").join(target.to_string()));
1404 }
1405 let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?);
1406 env_root.push("share");
1407 env_root.push("wasi-sysroot");
1408 env_root.push("lib");
1409 env_root.push(target.to_string());
1410 Some(env_root)
1411 }
1412
1413 fn no_std(&self, target: TargetSelection) -> Option<bool> {
1415 self.config.target_config.get(&target).map(|t| t.no_std)
1416 }
1417
1418 fn remote_tested(&self, target: TargetSelection) -> bool {
1421 self.qemu_rootfs(target).is_some()
1422 || target.contains("android")
1423 || env::var_os("TEST_DEVICE_ADDR").is_some()
1424 }
1425
1426 fn runner(&self, target: TargetSelection) -> Option<String> {
1432 let configured_runner =
1433 self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p);
1434 if let Some(runner) = configured_runner {
1435 return Some(runner.to_owned());
1436 }
1437
1438 if target.starts_with("wasm") && target.contains("wasi") {
1439 self.default_wasi_runner(target)
1440 } else {
1441 None
1442 }
1443 }
1444
1445 fn default_wasi_runner(&self, target: TargetSelection) -> Option<String> {
1449 let mut finder = crate::core::sanity::Finder::new();
1450
1451 if let Some(path) = finder.maybe_have("wasmtime") {
1455 if let Ok(mut path) = path.into_os_string().into_string() {
1456 path.push_str(" run -C cache=n --dir .");
1457 path.push_str(" --env RUSTC_BOOTSTRAP");
1464
1465 if target.contains("wasip2") {
1466 path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
1467 }
1468
1469 return Some(path);
1470 }
1471 }
1472
1473 None
1474 }
1475
1476 fn tool_enabled(&self, tool: &str) -> bool {
1481 if !self.config.extended {
1482 return false;
1483 }
1484 match &self.config.tools {
1485 Some(set) => set.contains(tool),
1486 None => true,
1487 }
1488 }
1489
1490 fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {
1496 self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)
1497 }
1498
1499 fn python(&self) -> &Path {
1501 if self.config.build.ends_with("apple-darwin") {
1502 Path::new("/usr/bin/python3")
1506 } else {
1507 self.config
1508 .python
1509 .as_ref()
1510 .expect("python is required for running LLDB or rustdoc tests")
1511 }
1512 }
1513
1514 fn extended_error_dir(&self) -> PathBuf {
1516 self.out.join("tmp/extended-error-metadata")
1517 }
1518
1519 fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {
1538 !self.config.full_bootstrap
1539 && !self.config.download_rustc()
1540 && stage >= 2
1541 && (self.hosts.contains(&target) || target == self.build)
1542 }
1543
1544 fn force_use_stage2(&self, stage: u32) -> bool {
1550 self.config.download_rustc() && stage >= 2
1551 }
1552
1553 fn release(&self, num: &str) -> String {
1559 match &self.config.channel[..] {
1560 "stable" => num.to_string(),
1561 "beta" => {
1562 if !self.config.omit_git_hash {
1563 format!("{}-beta.{}", num, self.beta_prerelease_version())
1564 } else {
1565 format!("{num}-beta")
1566 }
1567 }
1568 "nightly" => format!("{num}-nightly"),
1569 _ => format!("{num}-dev"),
1570 }
1571 }
1572
1573 fn beta_prerelease_version(&self) -> u32 {
1574 fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
1575 let version = fs::read_to_string(version_file).ok()?;
1576
1577 helpers::extract_beta_rev(&version)
1578 }
1579
1580 if let Some(s) = self.prerelease_version.get() {
1581 return s;
1582 }
1583
1584 let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {
1588 helpers::git(Some(&self.src))
1592 .arg("rev-list")
1593 .arg("--count")
1594 .arg("--merges")
1595 .arg(format!(
1596 "refs/remotes/origin/{}..HEAD",
1597 self.config.stage0_metadata.config.nightly_branch
1598 ))
1599 .run_always()
1600 .run_capture(self)
1601 .stdout()
1602 });
1603 let n = count.trim().parse().unwrap();
1604 self.prerelease_version.set(Some(n));
1605 n
1606 }
1607
1608 fn rust_release(&self) -> String {
1610 self.release(&self.version)
1611 }
1612
1613 fn package_vers(&self, num: &str) -> String {
1620 match &self.config.channel[..] {
1621 "stable" => num.to_string(),
1622 "beta" => "beta".to_string(),
1623 "nightly" => "nightly".to_string(),
1624 _ => format!("{num}-dev"),
1625 }
1626 }
1627
1628 fn rust_package_vers(&self) -> String {
1630 self.package_vers(&self.version)
1631 }
1632
1633 fn rust_version(&self) -> String {
1639 let mut version = self.rust_info().version(self, &self.version);
1640 if let Some(ref s) = self.config.description {
1641 if !s.is_empty() {
1642 version.push_str(" (");
1643 version.push_str(s);
1644 version.push(')');
1645 }
1646 }
1647 version
1648 }
1649
1650 fn rust_sha(&self) -> Option<&str> {
1652 self.rust_info().sha()
1653 }
1654
1655 fn release_num(&self, package: &str) -> String {
1657 let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml"));
1658 let toml = t!(fs::read_to_string(toml_file_name));
1659 for line in toml.lines() {
1660 if let Some(stripped) =
1661 line.strip_prefix("version = \"").and_then(|s| s.strip_suffix('"'))
1662 {
1663 return stripped.to_owned();
1664 }
1665 }
1666
1667 panic!("failed to find version in {package}'s Cargo.toml")
1668 }
1669
1670 fn unstable_features(&self) -> bool {
1673 !matches!(&self.config.channel[..], "stable" | "beta")
1674 }
1675
1676 fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {
1680 let mut ret = Vec::new();
1681 let mut list = vec![root.to_owned()];
1682 let mut visited = HashSet::new();
1683 while let Some(krate) = list.pop() {
1684 let krate = self
1685 .crates
1686 .get(&krate)
1687 .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));
1688 ret.push(krate);
1689 for dep in &krate.deps {
1690 if !self.crates.contains_key(dep) {
1691 continue;
1693 }
1694 if visited.insert(dep)
1700 && (dep != "profiler_builtins"
1701 || target
1702 .map(|t| self.config.profiler_enabled(t))
1703 .unwrap_or_else(|| self.config.any_profiler_enabled()))
1704 && (dep != "rustc_codegen_llvm"
1705 || self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))
1706 {
1707 list.push(dep.clone());
1708 }
1709 }
1710 }
1711 ret.sort_unstable_by_key(|krate| krate.name.clone()); ret
1713 }
1714
1715 fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> {
1716 if self.config.dry_run() {
1717 return Vec::new();
1718 }
1719
1720 if !stamp.path().exists() {
1721 eprintln!(
1722 "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
1723 stamp.path().display()
1724 );
1725 crate::exit!(1);
1726 }
1727
1728 let mut paths = Vec::new();
1729 let contents = t!(fs::read(stamp.path()), stamp.path());
1730 for part in contents.split(|b| *b == 0) {
1733 if part.is_empty() {
1734 continue;
1735 }
1736 let dependency_type = match part[0] as char {
1737 'h' => DependencyType::Host,
1738 's' => DependencyType::TargetSelfContained,
1739 't' => DependencyType::Target,
1740 _ => unreachable!(),
1741 };
1742 let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
1743 paths.push((path, dependency_type));
1744 }
1745 paths
1746 }
1747
1748 pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {
1753 self.copy_link_internal(src, dst, true);
1754 }
1755
1756 pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
1761 self.copy_link_internal(src, dst, false);
1762
1763 if file_type.could_have_split_debuginfo() {
1764 if let Some(dbg_file) = split_debuginfo(src) {
1765 self.copy_link_internal(
1766 &dbg_file,
1767 &dst.with_extension(dbg_file.extension().unwrap()),
1768 false,
1769 );
1770 }
1771 }
1772 }
1773
1774 fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
1775 if self.config.dry_run() {
1776 return;
1777 }
1778 self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
1779 if src == dst {
1780 return;
1781 }
1782 if let Err(e) = fs::remove_file(dst) {
1783 if cfg!(windows) && e.kind() != io::ErrorKind::NotFound {
1784 let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
1787 let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
1788 }
1789 }
1790 let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
1791 let mut src = src.to_path_buf();
1792 if metadata.file_type().is_symlink() {
1793 if dereference_symlinks {
1794 src = t!(fs::canonicalize(src));
1795 } else {
1796 let link = t!(fs::read_link(src));
1797 t!(self.symlink_file(link, dst));
1798 return;
1799 }
1800 }
1801 if let Ok(()) = fs::hard_link(&src, dst) {
1802 } else {
1805 if let Err(e) = fs::copy(&src, dst) {
1806 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
1807 }
1808 t!(fs::set_permissions(dst, metadata.permissions()));
1809
1810 let file_times = fs::FileTimes::new()
1813 .set_accessed(t!(metadata.accessed()))
1814 .set_modified(t!(metadata.modified()));
1815 t!(set_file_times(dst, file_times));
1816 }
1817 }
1818
1819 pub fn cp_link_r(&self, src: &Path, dst: &Path) {
1823 if self.config.dry_run() {
1824 return;
1825 }
1826 for f in self.read_dir(src) {
1827 let path = f.path();
1828 let name = path.file_name().unwrap();
1829 let dst = dst.join(name);
1830 if t!(f.file_type()).is_dir() {
1831 t!(fs::create_dir_all(&dst));
1832 self.cp_link_r(&path, &dst);
1833 } else {
1834 self.copy_link(&path, &dst, FileType::Regular);
1835 }
1836 }
1837 }
1838
1839 pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1845 self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)
1847 }
1848
1849 fn cp_link_filtered_recurse(
1851 &self,
1852 src: &Path,
1853 dst: &Path,
1854 relative: &Path,
1855 filter: &dyn Fn(&Path) -> bool,
1856 ) {
1857 for f in self.read_dir(src) {
1858 let path = f.path();
1859 let name = path.file_name().unwrap();
1860 let dst = dst.join(name);
1861 let relative = relative.join(name);
1862 if filter(&relative) {
1864 if t!(f.file_type()).is_dir() {
1865 let _ = fs::remove_dir_all(&dst);
1866 self.create_dir(&dst);
1867 self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
1868 } else {
1869 let _ = fs::remove_file(&dst);
1870 self.copy_link(&path, &dst, FileType::Regular);
1871 }
1872 }
1873 }
1874 }
1875
1876 fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {
1877 let file_name = src.file_name().unwrap();
1878 let dest = dest_folder.join(file_name);
1879 self.copy_link(src, &dest, FileType::Regular);
1880 }
1881
1882 fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {
1883 if self.config.dry_run() {
1884 return;
1885 }
1886 let dst = dstdir.join(src.file_name().unwrap());
1887 self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
1888 t!(fs::create_dir_all(dstdir));
1889 if !src.exists() {
1890 panic!("ERROR: File \"{}\" not found!", src.display());
1891 }
1892
1893 self.copy_link_internal(src, &dst, true);
1894 chmod(&dst, file_type.perms());
1895
1896 if file_type.could_have_split_debuginfo() {
1898 if let Some(dbg_file) = split_debuginfo(src) {
1899 self.install(&dbg_file, dstdir, FileType::Regular);
1900 }
1901 }
1902 }
1903
1904 fn read(&self, path: &Path) -> String {
1905 if self.config.dry_run() {
1906 return String::new();
1907 }
1908 t!(fs::read_to_string(path))
1909 }
1910
1911 fn create_dir(&self, dir: &Path) {
1912 if self.config.dry_run() {
1913 return;
1914 }
1915 t!(fs::create_dir_all(dir))
1916 }
1917
1918 fn remove_dir(&self, dir: &Path) {
1919 if self.config.dry_run() {
1920 return;
1921 }
1922 t!(fs::remove_dir_all(dir))
1923 }
1924
1925 fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {
1926 let iter = match fs::read_dir(dir) {
1927 Ok(v) => v,
1928 Err(_) if self.config.dry_run() => return vec![].into_iter(),
1929 Err(err) => panic!("could not read dir {dir:?}: {err:?}"),
1930 };
1931 iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
1932 }
1933
1934 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {
1935 #[cfg(unix)]
1936 use std::os::unix::fs::symlink as symlink_file;
1937 #[cfg(windows)]
1938 use std::os::windows::fs::symlink_file;
1939 if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
1940 }
1941
1942 fn ninja(&self) -> bool {
1945 let mut cmd_finder = crate::core::sanity::Finder::new();
1946
1947 if self.config.ninja_in_file {
1948 if cmd_finder.maybe_have("ninja-build").is_none()
1951 && cmd_finder.maybe_have("ninja").is_none()
1952 {
1953 eprintln!(
1954 "
1955Couldn't find required command: ninja (or ninja-build)
1956
1957You should install ninja as described at
1958<https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
1959or set `ninja = false` in the `[llvm]` section of `bootstrap.toml`.
1960Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
1961to download LLVM rather than building it.
1962"
1963 );
1964 exit!(1);
1965 }
1966 }
1967
1968 if !self.config.ninja_in_file
1976 && self.config.build.is_msvc()
1977 && cmd_finder.maybe_have("ninja").is_some()
1978 {
1979 return true;
1980 }
1981
1982 self.config.ninja_in_file
1983 }
1984
1985 pub fn colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1986 self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f)
1987 }
1988
1989 pub fn colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1990 self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f)
1991 }
1992
1993 fn colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R
1994 where
1995 C: Fn(ColorChoice) -> StandardStream,
1996 F: FnOnce(&mut dyn WriteColor) -> R,
1997 {
1998 let choice = match self.config.color {
1999 flags::Color::Always => ColorChoice::Always,
2000 flags::Color::Never => ColorChoice::Never,
2001 flags::Color::Auto if !is_tty => ColorChoice::Never,
2002 flags::Color::Auto => ColorChoice::Auto,
2003 };
2004 let mut stream = constructor(choice);
2005 let result = f(&mut stream);
2006 stream.reset().unwrap();
2007 result
2008 }
2009}
2010
2011#[cfg(unix)]
2012fn chmod(path: &Path, perms: u32) {
2013 use std::os::unix::fs::*;
2014 t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
2015}
2016#[cfg(windows)]
2017fn chmod(_path: &Path, _perms: u32) {}
2018
2019impl Compiler {
2020 pub fn new(stage: u32, host: TargetSelection) -> Self {
2021 Self { stage, host, forced_compiler: false }
2022 }
2023
2024 pub fn forced_compiler(&mut self, forced_compiler: bool) {
2025 self.forced_compiler = forced_compiler;
2026 }
2027
2028 pub fn with_stage(mut self, stage: u32) -> Compiler {
2029 self.stage = stage;
2030 self
2031 }
2032
2033 pub fn is_snapshot(&self, build: &Build) -> bool {
2035 self.stage == 0 && self.host == build.build
2036 }
2037
2038 pub fn is_forced_compiler(&self) -> bool {
2040 self.forced_compiler
2041 }
2042}
2043
2044fn envify(s: &str) -> String {
2045 s.chars()
2046 .map(|c| match c {
2047 '-' => '_',
2048 c => c,
2049 })
2050 .flat_map(|c| c.to_uppercase())
2051 .collect()
2052}
2053
2054pub fn prepare_behaviour_dump_dir(build: &Build) {
2056 static INITIALIZED: OnceLock<bool> = OnceLock::new();
2057
2058 let dump_path = build.out.join("bootstrap-shims-dump");
2059
2060 let initialized = INITIALIZED.get().unwrap_or(&false);
2061 if !initialized {
2062 if dump_path.exists() {
2064 t!(fs::remove_dir_all(&dump_path));
2065 }
2066
2067 t!(fs::create_dir_all(&dump_path));
2068
2069 t!(INITIALIZED.set(true));
2070 }
2071}