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 custom_stage: Option<u32>,
1108 ) -> Option<gha::Group> {
1109 self.msg(
1110 Kind::Check,
1111 custom_stage.unwrap_or(self.config.stage),
1112 what,
1113 self.config.build,
1114 target,
1115 )
1116 }
1117
1118 #[must_use = "Groups should not be dropped until the Step finishes running"]
1119 #[track_caller]
1120 fn msg_doc(
1121 &self,
1122 compiler: Compiler,
1123 what: impl Display,
1124 target: impl Into<Option<TargetSelection>> + Copy,
1125 ) -> Option<gha::Group> {
1126 self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into())
1127 }
1128
1129 #[must_use = "Groups should not be dropped until the Step finishes running"]
1130 #[track_caller]
1131 fn msg_build(
1132 &self,
1133 compiler: Compiler,
1134 what: impl Display,
1135 target: impl Into<Option<TargetSelection>>,
1136 ) -> Option<gha::Group> {
1137 self.msg(Kind::Build, compiler.stage, what, compiler.host, target)
1138 }
1139
1140 #[must_use = "Groups should not be dropped until the Step finishes running"]
1144 #[track_caller]
1145 fn msg(
1146 &self,
1147 action: impl Into<Kind>,
1148 stage: u32,
1149 what: impl Display,
1150 host: impl Into<Option<TargetSelection>>,
1151 target: impl Into<Option<TargetSelection>>,
1152 ) -> Option<gha::Group> {
1153 let action = action.into().description();
1154 let msg = |fmt| format!("{action} stage{stage} {what}{fmt}");
1155 let msg = if let Some(target) = target.into() {
1156 let host = host.into().unwrap();
1157 if host == target {
1158 msg(format_args!(" ({target})"))
1159 } else {
1160 msg(format_args!(" ({host} -> {target})"))
1161 }
1162 } else {
1163 msg(format_args!(""))
1164 };
1165 self.group(&msg)
1166 }
1167
1168 #[must_use = "Groups should not be dropped until the Step finishes running"]
1172 #[track_caller]
1173 fn msg_unstaged(
1174 &self,
1175 action: impl Into<Kind>,
1176 what: impl Display,
1177 target: TargetSelection,
1178 ) -> Option<gha::Group> {
1179 let action = action.into().description();
1180 let msg = format!("{action} {what} for {target}");
1181 self.group(&msg)
1182 }
1183
1184 #[must_use = "Groups should not be dropped until the Step finishes running"]
1185 #[track_caller]
1186 fn msg_sysroot_tool(
1187 &self,
1188 action: impl Into<Kind>,
1189 stage: u32,
1190 what: impl Display,
1191 host: TargetSelection,
1192 target: TargetSelection,
1193 ) -> Option<gha::Group> {
1194 let action = action.into().description();
1195 let msg = |fmt| format!("{action} {what} {fmt}");
1196 let msg = if host == target {
1197 msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1))
1198 } else {
1199 msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1))
1200 };
1201 self.group(&msg)
1202 }
1203
1204 #[track_caller]
1205 fn group(&self, msg: &str) -> Option<gha::Group> {
1206 match self.config.dry_run {
1207 DryRun::SelfCheck => None,
1208 DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)),
1209 }
1210 }
1211
1212 fn jobs(&self) -> u32 {
1215 self.config.jobs.unwrap_or_else(|| {
1216 std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1217 })
1218 }
1219
1220 fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
1221 if !self.config.rust_remap_debuginfo {
1222 return None;
1223 }
1224
1225 match which {
1226 GitRepo::Rustc => {
1227 let sha = self.rust_sha().unwrap_or(&self.version);
1228 Some(format!("/rustc/{sha}"))
1229 }
1230 GitRepo::Llvm => Some(String::from("/rustc/llvm")),
1231 }
1232 }
1233
1234 fn cc(&self, target: TargetSelection) -> PathBuf {
1236 if self.config.dry_run() {
1237 return PathBuf::new();
1238 }
1239 self.cc.borrow()[&target].path().into()
1240 }
1241
1242 fn cc_tool(&self, target: TargetSelection) -> Tool {
1244 self.cc.borrow()[&target].clone()
1245 }
1246
1247 fn cxx_tool(&self, target: TargetSelection) -> Tool {
1249 self.cxx.borrow()[&target].clone()
1250 }
1251
1252 fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {
1255 if self.config.dry_run() {
1256 return Vec::new();
1257 }
1258 let base = match c {
1259 CLang::C => self.cc.borrow()[&target].clone(),
1260 CLang::Cxx => self.cxx.borrow()[&target].clone(),
1261 };
1262
1263 base.args()
1266 .iter()
1267 .map(|s| s.to_string_lossy().into_owned())
1268 .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
1269 .collect::<Vec<String>>()
1270 }
1271
1272 fn cc_unhandled_cflags(
1274 &self,
1275 target: TargetSelection,
1276 which: GitRepo,
1277 c: CLang,
1278 ) -> Vec<String> {
1279 let mut base = Vec::new();
1280
1281 if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {
1285 base.push("-stdlib=libc++".into());
1286 }
1287
1288 if &*target.triple == "i686-pc-windows-gnu" {
1292 base.push("-fno-omit-frame-pointer".into());
1293 }
1294
1295 if let Some(map_to) = self.debuginfo_map_to(which) {
1296 let map = format!("{}={}", self.src.display(), map_to);
1297 let cc = self.cc(target);
1298 if cc.ends_with("clang") || cc.ends_with("gcc") {
1299 base.push(format!("-fdebug-prefix-map={map}"));
1300 } else if cc.ends_with("clang-cl.exe") {
1301 base.push("-Xclang".into());
1302 base.push(format!("-fdebug-prefix-map={map}"));
1303 }
1304 }
1305 base
1306 }
1307
1308 fn ar(&self, target: TargetSelection) -> Option<PathBuf> {
1310 if self.config.dry_run() {
1311 return None;
1312 }
1313 self.ar.borrow().get(&target).cloned()
1314 }
1315
1316 fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {
1318 if self.config.dry_run() {
1319 return None;
1320 }
1321 self.ranlib.borrow().get(&target).cloned()
1322 }
1323
1324 fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {
1326 if self.config.dry_run() {
1327 return Ok(PathBuf::new());
1328 }
1329 match self.cxx.borrow().get(&target) {
1330 Some(p) => Ok(p.path().into()),
1331 None => Err(format!("target `{target}` is not configured as a host, only as a target")),
1332 }
1333 }
1334
1335 fn linker(&self, target: TargetSelection) -> Option<PathBuf> {
1337 if self.config.dry_run() {
1338 return Some(PathBuf::new());
1339 }
1340 if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
1341 {
1342 Some(linker)
1343 } else if target.contains("vxworks") {
1344 Some(self.cxx.borrow()[&target].path().into())
1347 } else if !self.config.is_host_target(target)
1348 && helpers::use_host_linker(target)
1349 && !target.is_msvc()
1350 {
1351 Some(self.cc(target))
1352 } else if self.config.lld_mode.is_used()
1353 && self.is_lld_direct_linker(target)
1354 && self.build == target
1355 {
1356 match self.config.lld_mode {
1357 LldMode::SelfContained => Some(self.initial_lld.clone()),
1358 LldMode::External => Some("lld".into()),
1359 LldMode::Unused => None,
1360 }
1361 } else {
1362 None
1363 }
1364 }
1365
1366 fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
1369 target.is_msvc()
1370 }
1371
1372 fn crt_static(&self, target: TargetSelection) -> Option<bool> {
1374 if target.contains("pc-windows-msvc") {
1375 Some(true)
1376 } else {
1377 self.config.target_config.get(&target).and_then(|t| t.crt_static)
1378 }
1379 }
1380
1381 fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
1383 self.config
1384 .target_config
1385 .get(&target)
1386 .and_then(|t| t.musl_root.as_ref())
1387 .or(self.config.musl_root.as_ref())
1388 .map(|p| &**p)
1389 }
1390
1391 fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1393 let t = self.config.target_config.get(&target)?;
1394 if let libdir @ Some(_) = &t.musl_libdir {
1395 return libdir.clone();
1396 }
1397 self.musl_root(target).map(|root| root.join("lib"))
1398 }
1399
1400 fn wasi_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1407 let configured =
1408 self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p);
1409 if let Some(path) = configured {
1410 return Some(path.join("lib").join(target.to_string()));
1411 }
1412 let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?);
1413 env_root.push("share");
1414 env_root.push("wasi-sysroot");
1415 env_root.push("lib");
1416 env_root.push(target.to_string());
1417 Some(env_root)
1418 }
1419
1420 fn no_std(&self, target: TargetSelection) -> Option<bool> {
1422 self.config.target_config.get(&target).map(|t| t.no_std)
1423 }
1424
1425 fn remote_tested(&self, target: TargetSelection) -> bool {
1428 self.qemu_rootfs(target).is_some()
1429 || target.contains("android")
1430 || env::var_os("TEST_DEVICE_ADDR").is_some()
1431 }
1432
1433 fn runner(&self, target: TargetSelection) -> Option<String> {
1439 let configured_runner =
1440 self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p);
1441 if let Some(runner) = configured_runner {
1442 return Some(runner.to_owned());
1443 }
1444
1445 if target.starts_with("wasm") && target.contains("wasi") {
1446 self.default_wasi_runner(target)
1447 } else {
1448 None
1449 }
1450 }
1451
1452 fn default_wasi_runner(&self, target: TargetSelection) -> Option<String> {
1456 let mut finder = crate::core::sanity::Finder::new();
1457
1458 if let Some(path) = finder.maybe_have("wasmtime")
1462 && let Ok(mut path) = path.into_os_string().into_string()
1463 {
1464 path.push_str(" run -C cache=n --dir .");
1465 path.push_str(" --env RUSTC_BOOTSTRAP");
1472
1473 if target.contains("wasip2") {
1474 path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
1475 }
1476
1477 return Some(path);
1478 }
1479
1480 None
1481 }
1482
1483 fn tool_enabled(&self, tool: &str) -> bool {
1488 if !self.config.extended {
1489 return false;
1490 }
1491 match &self.config.tools {
1492 Some(set) => set.contains(tool),
1493 None => true,
1494 }
1495 }
1496
1497 fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {
1503 self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)
1504 }
1505
1506 fn python(&self) -> &Path {
1508 if self.config.build.ends_with("apple-darwin") {
1509 Path::new("/usr/bin/python3")
1513 } else {
1514 self.config
1515 .python
1516 .as_ref()
1517 .expect("python is required for running LLDB or rustdoc tests")
1518 }
1519 }
1520
1521 fn extended_error_dir(&self) -> PathBuf {
1523 self.out.join("tmp/extended-error-metadata")
1524 }
1525
1526 fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {
1545 !self.config.full_bootstrap
1546 && !self.config.download_rustc()
1547 && stage >= 2
1548 && (self.hosts.contains(&target) || target == self.build)
1549 }
1550
1551 fn force_use_stage2(&self, stage: u32) -> bool {
1557 self.config.download_rustc() && stage >= 2
1558 }
1559
1560 fn release(&self, num: &str) -> String {
1566 match &self.config.channel[..] {
1567 "stable" => num.to_string(),
1568 "beta" => {
1569 if !self.config.omit_git_hash {
1570 format!("{}-beta.{}", num, self.beta_prerelease_version())
1571 } else {
1572 format!("{num}-beta")
1573 }
1574 }
1575 "nightly" => format!("{num}-nightly"),
1576 _ => format!("{num}-dev"),
1577 }
1578 }
1579
1580 fn beta_prerelease_version(&self) -> u32 {
1581 fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
1582 let version = fs::read_to_string(version_file).ok()?;
1583
1584 helpers::extract_beta_rev(&version)
1585 }
1586
1587 if let Some(s) = self.prerelease_version.get() {
1588 return s;
1589 }
1590
1591 let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {
1595 helpers::git(Some(&self.src))
1599 .arg("rev-list")
1600 .arg("--count")
1601 .arg("--merges")
1602 .arg(format!(
1603 "refs/remotes/origin/{}..HEAD",
1604 self.config.stage0_metadata.config.nightly_branch
1605 ))
1606 .run_always()
1607 .run_capture(self)
1608 .stdout()
1609 });
1610 let n = count.trim().parse().unwrap();
1611 self.prerelease_version.set(Some(n));
1612 n
1613 }
1614
1615 fn rust_release(&self) -> String {
1617 self.release(&self.version)
1618 }
1619
1620 fn package_vers(&self, num: &str) -> String {
1627 match &self.config.channel[..] {
1628 "stable" => num.to_string(),
1629 "beta" => "beta".to_string(),
1630 "nightly" => "nightly".to_string(),
1631 _ => format!("{num}-dev"),
1632 }
1633 }
1634
1635 fn rust_package_vers(&self) -> String {
1637 self.package_vers(&self.version)
1638 }
1639
1640 fn rust_version(&self) -> String {
1646 let mut version = self.rust_info().version(self, &self.version);
1647 if let Some(ref s) = self.config.description
1648 && !s.is_empty()
1649 {
1650 version.push_str(" (");
1651 version.push_str(s);
1652 version.push(')');
1653 }
1654 version
1655 }
1656
1657 fn rust_sha(&self) -> Option<&str> {
1659 self.rust_info().sha()
1660 }
1661
1662 fn release_num(&self, package: &str) -> String {
1664 let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml"));
1665 let toml = t!(fs::read_to_string(toml_file_name));
1666 for line in toml.lines() {
1667 if let Some(stripped) =
1668 line.strip_prefix("version = \"").and_then(|s| s.strip_suffix('"'))
1669 {
1670 return stripped.to_owned();
1671 }
1672 }
1673
1674 panic!("failed to find version in {package}'s Cargo.toml")
1675 }
1676
1677 fn unstable_features(&self) -> bool {
1680 !matches!(&self.config.channel[..], "stable" | "beta")
1681 }
1682
1683 fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {
1687 let mut ret = Vec::new();
1688 let mut list = vec![root.to_owned()];
1689 let mut visited = HashSet::new();
1690 while let Some(krate) = list.pop() {
1691 let krate = self
1692 .crates
1693 .get(&krate)
1694 .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));
1695 ret.push(krate);
1696 for dep in &krate.deps {
1697 if !self.crates.contains_key(dep) {
1698 continue;
1700 }
1701 if visited.insert(dep)
1707 && (dep != "profiler_builtins"
1708 || target
1709 .map(|t| self.config.profiler_enabled(t))
1710 .unwrap_or_else(|| self.config.any_profiler_enabled()))
1711 && (dep != "rustc_codegen_llvm"
1712 || self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))
1713 {
1714 list.push(dep.clone());
1715 }
1716 }
1717 }
1718 ret.sort_unstable_by_key(|krate| krate.name.clone()); ret
1720 }
1721
1722 fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> {
1723 if self.config.dry_run() {
1724 return Vec::new();
1725 }
1726
1727 if !stamp.path().exists() {
1728 eprintln!(
1729 "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
1730 stamp.path().display()
1731 );
1732 crate::exit!(1);
1733 }
1734
1735 let mut paths = Vec::new();
1736 let contents = t!(fs::read(stamp.path()), stamp.path());
1737 for part in contents.split(|b| *b == 0) {
1740 if part.is_empty() {
1741 continue;
1742 }
1743 let dependency_type = match part[0] as char {
1744 'h' => DependencyType::Host,
1745 's' => DependencyType::TargetSelfContained,
1746 't' => DependencyType::Target,
1747 _ => unreachable!(),
1748 };
1749 let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
1750 paths.push((path, dependency_type));
1751 }
1752 paths
1753 }
1754
1755 pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {
1760 self.copy_link_internal(src, dst, true);
1761 }
1762
1763 pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
1768 self.copy_link_internal(src, dst, false);
1769
1770 if file_type.could_have_split_debuginfo()
1771 && let Some(dbg_file) = split_debuginfo(src)
1772 {
1773 self.copy_link_internal(
1774 &dbg_file,
1775 &dst.with_extension(dbg_file.extension().unwrap()),
1776 false,
1777 );
1778 }
1779 }
1780
1781 fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
1782 if self.config.dry_run() {
1783 return;
1784 }
1785 self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
1786 if src == dst {
1787 return;
1788 }
1789 if let Err(e) = fs::remove_file(dst)
1790 && cfg!(windows)
1791 && e.kind() != io::ErrorKind::NotFound
1792 {
1793 let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
1796 let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
1797 }
1798 let mut metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
1799 let mut src = src.to_path_buf();
1800 if metadata.file_type().is_symlink() {
1801 if dereference_symlinks {
1802 src = t!(fs::canonicalize(src));
1803 metadata = t!(fs::metadata(&src), format!("target = {}", src.display()));
1804 } else {
1805 let link = t!(fs::read_link(src));
1806 t!(self.symlink_file(link, dst));
1807 return;
1808 }
1809 }
1810 if let Ok(()) = fs::hard_link(&src, dst) {
1811 } else {
1814 if let Err(e) = fs::copy(&src, dst) {
1815 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
1816 }
1817 t!(fs::set_permissions(dst, metadata.permissions()));
1818
1819 let file_times = fs::FileTimes::new()
1822 .set_accessed(t!(metadata.accessed()))
1823 .set_modified(t!(metadata.modified()));
1824 t!(set_file_times(dst, file_times));
1825 }
1826 }
1827
1828 pub fn cp_link_r(&self, src: &Path, dst: &Path) {
1832 if self.config.dry_run() {
1833 return;
1834 }
1835 for f in self.read_dir(src) {
1836 let path = f.path();
1837 let name = path.file_name().unwrap();
1838 let dst = dst.join(name);
1839 if t!(f.file_type()).is_dir() {
1840 t!(fs::create_dir_all(&dst));
1841 self.cp_link_r(&path, &dst);
1842 } else {
1843 self.copy_link(&path, &dst, FileType::Regular);
1844 }
1845 }
1846 }
1847
1848 pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1854 self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)
1856 }
1857
1858 fn cp_link_filtered_recurse(
1860 &self,
1861 src: &Path,
1862 dst: &Path,
1863 relative: &Path,
1864 filter: &dyn Fn(&Path) -> bool,
1865 ) {
1866 for f in self.read_dir(src) {
1867 let path = f.path();
1868 let name = path.file_name().unwrap();
1869 let dst = dst.join(name);
1870 let relative = relative.join(name);
1871 if filter(&relative) {
1873 if t!(f.file_type()).is_dir() {
1874 let _ = fs::remove_dir_all(&dst);
1875 self.create_dir(&dst);
1876 self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
1877 } else {
1878 let _ = fs::remove_file(&dst);
1879 self.copy_link(&path, &dst, FileType::Regular);
1880 }
1881 }
1882 }
1883 }
1884
1885 fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {
1886 let file_name = src.file_name().unwrap();
1887 let dest = dest_folder.join(file_name);
1888 self.copy_link(src, &dest, FileType::Regular);
1889 }
1890
1891 fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {
1892 if self.config.dry_run() {
1893 return;
1894 }
1895 let dst = dstdir.join(src.file_name().unwrap());
1896 self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
1897 t!(fs::create_dir_all(dstdir));
1898 if !src.exists() {
1899 panic!("ERROR: File \"{}\" not found!", src.display());
1900 }
1901
1902 self.copy_link_internal(src, &dst, true);
1903 chmod(&dst, file_type.perms());
1904
1905 if file_type.could_have_split_debuginfo()
1907 && let Some(dbg_file) = split_debuginfo(src)
1908 {
1909 self.install(&dbg_file, dstdir, FileType::Regular);
1910 }
1911 }
1912
1913 fn read(&self, path: &Path) -> String {
1914 if self.config.dry_run() {
1915 return String::new();
1916 }
1917 t!(fs::read_to_string(path))
1918 }
1919
1920 fn create_dir(&self, dir: &Path) {
1921 if self.config.dry_run() {
1922 return;
1923 }
1924 t!(fs::create_dir_all(dir))
1925 }
1926
1927 fn remove_dir(&self, dir: &Path) {
1928 if self.config.dry_run() {
1929 return;
1930 }
1931 t!(fs::remove_dir_all(dir))
1932 }
1933
1934 fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {
1935 let iter = match fs::read_dir(dir) {
1936 Ok(v) => v,
1937 Err(_) if self.config.dry_run() => return vec![].into_iter(),
1938 Err(err) => panic!("could not read dir {dir:?}: {err:?}"),
1939 };
1940 iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
1941 }
1942
1943 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {
1944 #[cfg(unix)]
1945 use std::os::unix::fs::symlink as symlink_file;
1946 #[cfg(windows)]
1947 use std::os::windows::fs::symlink_file;
1948 if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
1949 }
1950
1951 fn ninja(&self) -> bool {
1954 let mut cmd_finder = crate::core::sanity::Finder::new();
1955
1956 if self.config.ninja_in_file {
1957 if cmd_finder.maybe_have("ninja-build").is_none()
1960 && cmd_finder.maybe_have("ninja").is_none()
1961 {
1962 eprintln!(
1963 "
1964Couldn't find required command: ninja (or ninja-build)
1965
1966You should install ninja as described at
1967<https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
1968or set `ninja = false` in the `[llvm]` section of `bootstrap.toml`.
1969Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
1970to download LLVM rather than building it.
1971"
1972 );
1973 exit!(1);
1974 }
1975 }
1976
1977 if !self.config.ninja_in_file
1985 && self.config.build.is_msvc()
1986 && cmd_finder.maybe_have("ninja").is_some()
1987 {
1988 return true;
1989 }
1990
1991 self.config.ninja_in_file
1992 }
1993
1994 pub fn colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1995 self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f)
1996 }
1997
1998 pub fn colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
1999 self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f)
2000 }
2001
2002 fn colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R
2003 where
2004 C: Fn(ColorChoice) -> StandardStream,
2005 F: FnOnce(&mut dyn WriteColor) -> R,
2006 {
2007 let choice = match self.config.color {
2008 flags::Color::Always => ColorChoice::Always,
2009 flags::Color::Never => ColorChoice::Never,
2010 flags::Color::Auto if !is_tty => ColorChoice::Never,
2011 flags::Color::Auto => ColorChoice::Auto,
2012 };
2013 let mut stream = constructor(choice);
2014 let result = f(&mut stream);
2015 stream.reset().unwrap();
2016 result
2017 }
2018}
2019
2020#[cfg(unix)]
2021fn chmod(path: &Path, perms: u32) {
2022 use std::os::unix::fs::*;
2023 t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
2024}
2025#[cfg(windows)]
2026fn chmod(_path: &Path, _perms: u32) {}
2027
2028impl Compiler {
2029 pub fn new(stage: u32, host: TargetSelection) -> Self {
2030 Self { stage, host, forced_compiler: false }
2031 }
2032
2033 pub fn forced_compiler(&mut self, forced_compiler: bool) {
2034 self.forced_compiler = forced_compiler;
2035 }
2036
2037 pub fn with_stage(mut self, stage: u32) -> Compiler {
2038 self.stage = stage;
2039 self
2040 }
2041
2042 pub fn is_snapshot(&self, build: &Build) -> bool {
2044 self.stage == 0 && self.host == build.build
2045 }
2046
2047 pub fn is_forced_compiler(&self) -> bool {
2049 self.forced_compiler
2050 }
2051}
2052
2053fn envify(s: &str) -> String {
2054 s.chars()
2055 .map(|c| match c {
2056 '-' => '_',
2057 c => c,
2058 })
2059 .flat_map(|c| c.to_uppercase())
2060 .collect()
2061}
2062
2063pub fn prepare_behaviour_dump_dir(build: &Build) {
2065 static INITIALIZED: OnceLock<bool> = OnceLock::new();
2066
2067 let dump_path = build.out.join("bootstrap-shims-dump");
2068
2069 let initialized = INITIALIZED.get().unwrap_or(&false);
2070 if !initialized {
2071 if dump_path.exists() {
2073 t!(fs::remove_dir_all(&dump_path));
2074 }
2075
2076 t!(fs::create_dir_all(&dump_path));
2077
2078 t!(INITIALIZED.set(true));
2079 }
2080}