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, Target, 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};
56
57use crate::core::build_steps::vendor::VENDOR_DIR;
58
59const LLVM_TOOLS: &[&str] = &[
60 "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", ];
75
76const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
78
79#[expect(clippy::type_complexity)] const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
83 (None, "bootstrap", None),
84 (Some(Mode::Rustc), "llvm_enzyme", None),
85 (Some(Mode::Codegen), "llvm_enzyme", None),
86 (Some(Mode::ToolRustc), "llvm_enzyme", None),
87 (Some(Mode::ToolRustc), "rust_analyzer", None),
88 (Some(Mode::ToolStd), "rust_analyzer", None),
89 ];
93
94#[derive(Eq, PartialOrd, Ord, Clone, Copy, Debug)]
100pub struct Compiler {
101 stage: u32,
102 host: TargetSelection,
103 forced_compiler: bool,
107}
108
109impl std::hash::Hash for Compiler {
110 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
111 self.stage.hash(state);
112 self.host.hash(state);
113 }
114}
115
116impl PartialEq for Compiler {
117 fn eq(&self, other: &Self) -> bool {
118 self.stage == other.stage && self.host == other.host
119 }
120}
121
122#[derive(PartialEq, Eq, Copy, Clone, Debug)]
123pub enum DocTests {
124 Yes,
126 No,
128 Only,
130}
131
132pub enum GitRepo {
133 Rustc,
134 Llvm,
135}
136
137#[derive(Clone)]
148pub struct Build {
149 config: Config,
151
152 version: String,
154
155 src: PathBuf,
157 out: PathBuf,
158 bootstrap_out: PathBuf,
159 cargo_info: GitInfo,
160 rust_analyzer_info: GitInfo,
161 clippy_info: GitInfo,
162 miri_info: GitInfo,
163 rustfmt_info: GitInfo,
164 enzyme_info: GitInfo,
165 in_tree_llvm_info: GitInfo,
166 in_tree_gcc_info: GitInfo,
167 local_rebuild: bool,
168 fail_fast: bool,
169 doc_tests: DocTests,
170 verbosity: usize,
171
172 build: TargetSelection,
174 hosts: Vec<TargetSelection>,
176 targets: Vec<TargetSelection>,
178
179 initial_rustc: PathBuf,
180 initial_rustdoc: PathBuf,
181 initial_cargo: PathBuf,
182 initial_lld: PathBuf,
183 initial_relative_libdir: PathBuf,
184 initial_sysroot: PathBuf,
185
186 cc: RefCell<HashMap<TargetSelection, cc::Tool>>,
189 cxx: RefCell<HashMap<TargetSelection, cc::Tool>>,
190 ar: RefCell<HashMap<TargetSelection, PathBuf>>,
191 ranlib: RefCell<HashMap<TargetSelection, PathBuf>>,
192 crates: HashMap<String, Crate>,
195 crate_paths: HashMap<PathBuf, String>,
196 is_sudo: bool,
197 delayed_failures: RefCell<Vec<String>>,
198 prerelease_version: Cell<Option<u32>>,
199
200 #[cfg(feature = "build-metrics")]
201 metrics: crate::utils::metrics::BuildMetrics,
202}
203
204#[derive(Debug, Clone)]
205struct Crate {
206 name: String,
207 deps: HashSet<String>,
208 path: PathBuf,
209 features: Vec<String>,
210}
211
212impl Crate {
213 fn local_path(&self, build: &Build) -> PathBuf {
214 self.path.strip_prefix(&build.config.src).unwrap().into()
215 }
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
220pub enum DependencyType {
221 Host,
223 Target,
225 TargetSelfContained,
227}
228
229#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
234pub enum Mode {
235 Std,
237
238 Rustc,
240
241 Codegen,
243
244 ToolBootstrap,
251
252 ToolStd,
256
257 ToolRustc,
262}
263
264impl Mode {
265 pub fn is_tool(&self) -> bool {
266 matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd)
267 }
268
269 pub fn must_support_dlopen(&self) -> bool {
270 matches!(self, Mode::Std | Mode::Codegen)
271 }
272}
273
274#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
275pub enum CLang {
276 C,
277 Cxx,
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub enum FileType {
282 Executable,
284 NativeLibrary,
286 Script,
288 Regular,
290}
291
292impl FileType {
293 pub fn perms(self) -> u32 {
295 match self {
296 FileType::Executable | FileType::Script => 0o755,
297 FileType::Regular | FileType::NativeLibrary => 0o644,
298 }
299 }
300
301 pub fn could_have_split_debuginfo(self) -> bool {
302 match self {
303 FileType::Executable | FileType::NativeLibrary => true,
304 FileType::Script | FileType::Regular => false,
305 }
306 }
307}
308
309macro_rules! forward {
310 ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {
311 impl Build {
312 $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? {
313 self.config.$fn( $($param),* )
314 } )+
315 }
316 }
317}
318
319forward! {
320 verbose(f: impl Fn()),
321 is_verbose() -> bool,
322 create(path: &Path, s: &str),
323 remove(f: &Path),
324 tempdir() -> PathBuf,
325 llvm_link_shared() -> bool,
326 download_rustc() -> bool,
327 initial_rustfmt() -> Option<PathBuf>,
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_str =
367 config.initial_sysroot.join("lib/rustlib").join(config.build).join("lib");
368
369 let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap();
370 let initial_lld = initial_target_dir.join("bin").join("rust-lld");
371
372 let initial_relative_libdir = initial_target_dir
373 .ancestors()
374 .nth(2)
375 .unwrap()
376 .strip_prefix(&config.initial_sysroot)
377 .expect("Couldn’t determine initial relative libdir.")
378 .to_path_buf();
379
380 let version = std::fs::read_to_string(src.join("src").join("version"))
381 .expect("failed to read src/version");
382 let version = version.trim();
383
384 let mut bootstrap_out = std::env::current_exe()
385 .expect("could not determine path to running process")
386 .parent()
387 .unwrap()
388 .to_path_buf();
389 if bootstrap_out.ends_with("deps") {
392 bootstrap_out.pop();
393 }
394 if !bootstrap_out.join(exe("rustc", config.build)).exists() && !cfg!(test) {
395 panic!(
397 "`rustc` not found in {}, run `cargo build --bins` before `cargo run`",
398 bootstrap_out.display()
399 )
400 }
401
402 if rust_info.is_from_tarball() && config.description.is_none() {
403 config.description = Some("built from a source tarball".to_owned());
404 }
405
406 let mut build = Build {
407 initial_lld,
408 initial_relative_libdir,
409 initial_rustc: config.initial_rustc.clone(),
410 initial_rustdoc: config.initial_rustc.with_file_name(exe("rustdoc", config.build)),
411 initial_cargo: config.initial_cargo.clone(),
412 initial_sysroot: config.initial_sysroot.clone(),
413 local_rebuild: config.local_rebuild,
414 fail_fast: config.cmd.fail_fast(),
415 doc_tests: config.cmd.doc_tests(),
416 verbosity: config.verbose,
417
418 build: config.build,
419 hosts: config.hosts.clone(),
420 targets: config.targets.clone(),
421
422 config,
423 version: version.to_string(),
424 src,
425 out,
426 bootstrap_out,
427
428 cargo_info,
429 rust_analyzer_info,
430 clippy_info,
431 miri_info,
432 rustfmt_info,
433 enzyme_info,
434 in_tree_llvm_info,
435 in_tree_gcc_info,
436 cc: RefCell::new(HashMap::new()),
437 cxx: RefCell::new(HashMap::new()),
438 ar: RefCell::new(HashMap::new()),
439 ranlib: RefCell::new(HashMap::new()),
440 crates: HashMap::new(),
441 crate_paths: HashMap::new(),
442 is_sudo,
443 delayed_failures: RefCell::new(Vec::new()),
444 prerelease_version: Cell::new(None),
445
446 #[cfg(feature = "build-metrics")]
447 metrics: crate::utils::metrics::BuildMetrics::init(),
448 };
449
450 let local_version_verbose =
453 output(Command::new(&build.initial_rustc).arg("--version").arg("--verbose"));
454 let local_release = local_version_verbose
455 .lines()
456 .filter_map(|x| x.strip_prefix("release:"))
457 .next()
458 .unwrap()
459 .trim();
460 if local_release.split('.').take(2).eq(version.split('.').take(2)) {
461 build.verbose(|| println!("auto-detected local-rebuild {local_release}"));
462 build.local_rebuild = true;
463 }
464
465 build.verbose(|| println!("finding compilers"));
466 utils::cc_detect::find(&build);
467 if !matches!(build.config.cmd, Subcommand::Setup { .. }) {
473 build.verbose(|| println!("running sanity check"));
474 crate::core::sanity::check(&mut build);
475
476 let rust_submodules = ["library/backtrace", "library/stdarch"];
479 for s in rust_submodules {
480 build.require_submodule(
481 s,
482 Some(
483 "The submodule is required for the standard library \
484 and the main Cargo workspace.",
485 ),
486 );
487 }
488 build.update_existing_submodules();
490
491 build.verbose(|| println!("learning about cargo"));
492 crate::core::metadata::build(&mut build);
493 }
494
495 let build_triple = build.out.join(build.build);
497 t!(fs::create_dir_all(&build_triple));
498 let host = build.out.join("host");
499 if host.is_symlink() {
500 #[cfg(windows)]
503 t!(fs::remove_dir(&host));
504 #[cfg(not(windows))]
505 t!(fs::remove_file(&host));
506 }
507 t!(
508 symlink_dir(&build.config, &build_triple, &host),
509 format!("symlink_dir({} => {}) failed", host.display(), build_triple.display())
510 );
511
512 build
513 }
514
515 #[cfg_attr(
524 feature = "tracing",
525 instrument(
526 level = "trace",
527 name = "Build::require_submodule",
528 skip_all,
529 fields(submodule = submodule),
530 ),
531 )]
532 pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) {
533 if self.rust_info().is_from_tarball() {
534 return;
535 }
536
537 if cfg!(test) && !self.config.submodules() {
540 return;
541 }
542 self.config.update_submodule(submodule);
543 let absolute_path = self.config.src.join(submodule);
544 if !absolute_path.exists() || dir_is_empty(&absolute_path) {
545 let maybe_enable = if !self.config.submodules()
546 && self.config.rust_info.is_managed_git_subrepository()
547 {
548 "\nConsider setting `build.submodules = true` or manually initializing the submodules."
549 } else {
550 ""
551 };
552 let err_hint = err_hint.map_or_else(String::new, |e| format!("\n{e}"));
553 eprintln!(
554 "submodule {submodule} does not appear to be checked out, \
555 but it is required for this step{maybe_enable}{err_hint}"
556 );
557 exit!(1);
558 }
559 }
560
561 pub fn require_and_update_all_submodules(&self) {
564 for submodule in build_helper::util::parse_gitmodules(&self.src) {
565 self.require_submodule(submodule, None);
566 }
567 }
568
569 fn update_existing_submodules(&self) {
572 if !self.config.submodules() {
575 return;
576 }
577 let output = helpers::git(Some(&self.src))
578 .args(["config", "--file"])
579 .arg(".gitmodules")
580 .args(["--get-regexp", "path"])
581 .run_capture(self)
582 .stdout();
583 std::thread::scope(|s| {
584 for line in output.lines() {
587 let submodule = line.split_once(' ').unwrap().1;
588 let config = self.config.clone();
589 s.spawn(move || {
590 Self::update_existing_submodule(&config, submodule);
591 });
592 }
593 });
594 }
595
596 pub fn update_existing_submodule(config: &Config, submodule: &str) {
598 if !config.submodules() {
600 return;
601 }
602
603 if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
604 config.update_submodule(submodule);
605 }
606 }
607
608 #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))]
610 pub fn build(&mut self) {
611 trace!("setting up job management");
612 unsafe {
613 crate::utils::job::setup(self);
614 }
615
616 trace!("downloading rustfmt early");
618 let _ = &builder::Builder::new(self).initial_rustfmt();
619
620 {
622 #[cfg(feature = "tracing")]
623 let _hardcoded_span = span!(
624 tracing::Level::DEBUG,
625 "handling hardcoded subcommands (Format, Suggest, Perf)"
626 )
627 .entered();
628
629 match &self.config.cmd {
630 Subcommand::Format { check, all } => {
631 return core::build_steps::format::format(
632 &builder::Builder::new(self),
633 *check,
634 *all,
635 &self.config.paths,
636 );
637 }
638 Subcommand::Suggest { run } => {
639 return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
640 }
641 Subcommand::Perf(args) => {
642 return core::build_steps::perf::perf(&builder::Builder::new(self), args);
643 }
644 _cmd => {
645 debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling");
646 }
647 }
648
649 debug!("handling subcommand normally");
650 }
651
652 if !self.config.dry_run() {
653 #[cfg(feature = "tracing")]
654 let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered();
655
656 {
659 #[cfg(feature = "tracing")]
660 let _sanity_check_span =
661 span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered();
662 self.config.dry_run = DryRun::SelfCheck;
663 let builder = builder::Builder::new(self);
664 builder.execute_cli();
665 }
666
667 {
669 #[cfg(feature = "tracing")]
670 let _actual_run_span =
671 span!(tracing::Level::DEBUG, "(2) executing actual run").entered();
672 self.config.dry_run = DryRun::Disabled;
673 let builder = builder::Builder::new(self);
674 builder.execute_cli();
675 }
676 } else {
677 #[cfg(feature = "tracing")]
678 let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered();
679
680 let builder = builder::Builder::new(self);
681 builder.execute_cli();
682 }
683
684 #[cfg(feature = "tracing")]
685 debug!("checking for postponed test failures from `test --no-fail-fast`");
686
687 let failures = self.delayed_failures.borrow();
689 if !failures.is_empty() {
690 eprintln!("\n{} command(s) did not execute successfully:\n", failures.len());
691 for failure in failures.iter() {
692 eprintln!(" - {failure}\n");
693 }
694 exit!(1);
695 }
696
697 #[cfg(feature = "build-metrics")]
698 self.metrics.persist(self);
699 }
700
701 fn rust_info(&self) -> &GitInfo {
702 &self.config.rust_info
703 }
704
705 fn std_features(&self, target: TargetSelection) -> String {
708 let mut features: BTreeSet<&str> =
709 self.config.rust_std_features.iter().map(|s| s.as_str()).collect();
710
711 match self.config.llvm_libunwind(target) {
712 LlvmLibunwind::InTree => features.insert("llvm-libunwind"),
713 LlvmLibunwind::System => features.insert("system-llvm-libunwind"),
714 LlvmLibunwind::No => false,
715 };
716
717 if self.config.backtrace {
718 features.insert("backtrace");
719 }
720
721 if self.config.profiler_enabled(target) {
722 features.insert("profiler");
723 }
724
725 if target.contains("zkvm") {
727 features.insert("compiler-builtins-mem");
728 }
729
730 features.into_iter().collect::<Vec<_>>().join(" ")
731 }
732
733 fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {
735 let possible_features_by_crates: HashSet<_> = crates
736 .iter()
737 .flat_map(|krate| &self.crates[krate].features)
738 .map(std::ops::Deref::deref)
739 .collect();
740 let check = |feature: &str| -> bool {
741 crates.is_empty() || possible_features_by_crates.contains(feature)
742 };
743 let mut features = vec![];
744 if self.config.jemalloc(target) && check("jemalloc") {
745 features.push("jemalloc");
746 }
747 if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
748 features.push("llvm");
749 }
750 if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {
752 features.push("rustc_randomized_layouts");
753 }
754
755 if !self.config.rust_debug_logging && check("max_level_info") {
761 features.push("max_level_info");
762 }
763
764 features.join(" ")
765 }
766
767 fn cargo_dir(&self) -> &'static str {
770 if self.config.rust_optimize.is_release() { "release" } else { "debug" }
771 }
772
773 fn tools_dir(&self, compiler: Compiler) -> PathBuf {
774 let out = self.out.join(compiler.host).join(format!("stage{}-tools-bin", compiler.stage));
775 t!(fs::create_dir_all(&out));
776 out
777 }
778
779 fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
784 let suffix = match mode {
785 Mode::Std => "-std",
786 Mode::Rustc => "-rustc",
787 Mode::Codegen => "-codegen",
788 Mode::ToolBootstrap => "-bootstrap-tools",
789 Mode::ToolStd | Mode::ToolRustc => "-tools",
790 };
791 self.out.join(compiler.host).join(format!("stage{}{}", compiler.stage, suffix))
792 }
793
794 fn cargo_out(&self, compiler: Compiler, mode: Mode, target: TargetSelection) -> PathBuf {
798 self.stage_out(compiler, mode).join(target).join(self.cargo_dir())
799 }
800
801 fn llvm_out(&self, target: TargetSelection) -> PathBuf {
806 if self.config.llvm_from_ci && self.is_builder_target(target) {
807 self.config.ci_llvm_root()
808 } else {
809 self.out.join(target).join("llvm")
810 }
811 }
812
813 fn enzyme_out(&self, target: TargetSelection) -> PathBuf {
814 self.out.join(&*target.triple).join("enzyme")
815 }
816
817 fn gcc_out(&self, target: TargetSelection) -> PathBuf {
818 self.out.join(&*target.triple).join("gcc")
819 }
820
821 fn lld_out(&self, target: TargetSelection) -> PathBuf {
822 self.out.join(target).join("lld")
823 }
824
825 fn doc_out(&self, target: TargetSelection) -> PathBuf {
827 self.out.join(target).join("doc")
828 }
829
830 fn json_doc_out(&self, target: TargetSelection) -> PathBuf {
832 self.out.join(target).join("json-doc")
833 }
834
835 fn test_out(&self, target: TargetSelection) -> PathBuf {
836 self.out.join(target).join("test")
837 }
838
839 fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
841 self.out.join(target).join("compiler-doc")
842 }
843
844 fn md_doc_out(&self, target: TargetSelection) -> PathBuf {
846 self.out.join(target).join("md-doc")
847 }
848
849 fn vendored_crates_path(&self) -> Option<PathBuf> {
851 if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None }
852 }
853
854 fn is_system_llvm(&self, target: TargetSelection) -> bool {
859 match self.config.target_config.get(&target) {
860 Some(Target { llvm_config: Some(_), .. }) => {
861 let ci_llvm = self.config.llvm_from_ci && self.is_builder_target(target);
862 !ci_llvm
863 }
864 Some(Target { llvm_config: None, .. }) => false,
866 None => false,
867 }
868 }
869
870 fn is_rust_llvm(&self, target: TargetSelection) -> bool {
874 match self.config.target_config.get(&target) {
875 Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched,
879 _ => !self.is_system_llvm(target),
882 }
883 }
884
885 fn llvm_filecheck(&self, target: TargetSelection) -> PathBuf {
887 let target_config = self.config.target_config.get(&target);
888 if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
889 s.to_path_buf()
890 } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
891 let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(self).stdout();
892 let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target));
893 if filecheck.exists() {
894 filecheck
895 } else {
896 let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(self).stdout();
899 let lib_filecheck =
900 Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target));
901 if lib_filecheck.exists() {
902 lib_filecheck
903 } else {
904 filecheck
908 }
909 }
910 } else {
911 let base = self.llvm_out(target).join("build");
912 let base = if !self.ninja() && target.is_msvc() {
913 if self.config.llvm_optimize {
914 if self.config.llvm_release_debuginfo {
915 base.join("RelWithDebInfo")
916 } else {
917 base.join("Release")
918 }
919 } else {
920 base.join("Debug")
921 }
922 } else {
923 base
924 };
925 base.join("bin").join(exe("FileCheck", target))
926 }
927 }
928
929 fn native_dir(&self, target: TargetSelection) -> PathBuf {
931 self.out.join(target).join("native")
932 }
933
934 fn test_helpers_out(&self, target: TargetSelection) -> PathBuf {
937 self.native_dir(target).join("rust-test-helpers")
938 }
939
940 fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) {
942 if env::var_os("RUST_TEST_THREADS").is_none() {
943 cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
944 }
945 }
946
947 fn rustc_snapshot_libdir(&self) -> PathBuf {
949 self.rustc_snapshot_sysroot().join(libdir(self.config.build))
950 }
951
952 fn rustc_snapshot_sysroot(&self) -> &Path {
954 static SYSROOT_CACHE: OnceLock<PathBuf> = OnceLock::new();
955 SYSROOT_CACHE.get_or_init(|| {
956 let mut rustc = Command::new(&self.initial_rustc);
957 rustc.args(["--print", "sysroot"]);
958 output(&mut rustc).trim().into()
959 })
960 }
961
962 #[track_caller]
966 fn run(
967 &self,
968 command: &mut BootstrapCommand,
969 stdout: OutputMode,
970 stderr: OutputMode,
971 ) -> CommandOutput {
972 command.mark_as_executed();
973 if self.config.dry_run() && !command.run_always {
974 return CommandOutput::default();
975 }
976
977 #[cfg(feature = "tracing")]
978 let _run_span = trace_cmd!(command);
979
980 let created_at = command.get_created_location();
981 let executed_at = std::panic::Location::caller();
982
983 self.verbose(|| {
984 println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
985 });
986
987 let cmd = command.as_command_mut();
988 cmd.stdout(stdout.stdio());
989 cmd.stderr(stderr.stdio());
990
991 let output = cmd.output();
992
993 use std::fmt::Write;
994
995 let mut message = String::new();
996 let output: CommandOutput = match output {
997 Ok(output) if output.status.success() => {
999 CommandOutput::from_output(output, stdout, stderr)
1000 }
1001 Ok(output) => {
1003 writeln!(
1004 message,
1005 r#"
1006Command {command:?} did not execute successfully.
1007Expected success, got {}
1008Created at: {created_at}
1009Executed at: {executed_at}"#,
1010 output.status,
1011 )
1012 .unwrap();
1013
1014 let output: CommandOutput = CommandOutput::from_output(output, stdout, stderr);
1015
1016 if stdout.captures() {
1020 writeln!(message, "\nSTDOUT ----\n{}", output.stdout().trim()).unwrap();
1021 }
1022 if stderr.captures() {
1023 writeln!(message, "\nSTDERR ----\n{}", output.stderr().trim()).unwrap();
1024 }
1025 output
1026 }
1027 Err(e) => {
1029 writeln!(
1030 message,
1031 "\n\nCommand {command:?} did not execute successfully.\
1032 \nIt was not possible to execute the command: {e:?}"
1033 )
1034 .unwrap();
1035 CommandOutput::did_not_start(stdout, stderr)
1036 }
1037 };
1038
1039 let fail = |message: &str, output: CommandOutput| -> ! {
1040 if self.is_verbose() {
1041 println!("{message}");
1042 } else {
1043 let (stdout, stderr) = (output.stdout_if_present(), output.stderr_if_present());
1044 if stdout.is_some() || stderr.is_some() {
1048 if let Some(stdout) =
1049 output.stdout_if_present().take_if(|s| !s.trim().is_empty())
1050 {
1051 println!("STDOUT:\n{stdout}\n");
1052 }
1053 if let Some(stderr) =
1054 output.stderr_if_present().take_if(|s| !s.trim().is_empty())
1055 {
1056 println!("STDERR:\n{stderr}\n");
1057 }
1058 println!("Command {command:?} has failed. Rerun with -v to see more details.");
1059 } else {
1060 println!("Command has failed. Rerun with -v to see more details.");
1061 }
1062 }
1063 exit!(1);
1064 };
1065
1066 if !output.is_success() {
1067 match command.failure_behavior {
1068 BehaviorOnFailure::DelayFail => {
1069 if self.fail_fast {
1070 fail(&message, output);
1071 }
1072
1073 let mut failures = self.delayed_failures.borrow_mut();
1074 failures.push(message);
1075 }
1076 BehaviorOnFailure::Exit => {
1077 fail(&message, output);
1078 }
1079 BehaviorOnFailure::Ignore => {
1080 }
1084 }
1085 }
1086 output
1087 }
1088
1089 pub fn is_verbose_than(&self, level: usize) -> bool {
1091 self.verbosity > level
1092 }
1093
1094 fn verbose_than(&self, level: usize, f: impl Fn()) {
1096 if self.is_verbose_than(level) {
1097 f()
1098 }
1099 }
1100
1101 fn info(&self, msg: &str) {
1102 match self.config.dry_run {
1103 DryRun::SelfCheck => (),
1104 DryRun::Disabled | DryRun::UserSelected => {
1105 println!("{msg}");
1106 }
1107 }
1108 }
1109
1110 #[must_use = "Groups should not be dropped until the Step finishes running"]
1111 #[track_caller]
1112 fn msg_clippy(
1113 &self,
1114 what: impl Display,
1115 target: impl Into<Option<TargetSelection>>,
1116 ) -> Option<gha::Group> {
1117 self.msg(Kind::Clippy, self.config.stage, what, self.config.build, target)
1118 }
1119
1120 #[must_use = "Groups should not be dropped until the Step finishes running"]
1121 #[track_caller]
1122 fn msg_check(
1123 &self,
1124 what: impl Display,
1125 target: impl Into<Option<TargetSelection>>,
1126 ) -> Option<gha::Group> {
1127 self.msg(Kind::Check, self.config.stage, what, self.config.build, target)
1128 }
1129
1130 #[must_use = "Groups should not be dropped until the Step finishes running"]
1131 #[track_caller]
1132 fn msg_doc(
1133 &self,
1134 compiler: Compiler,
1135 what: impl Display,
1136 target: impl Into<Option<TargetSelection>> + Copy,
1137 ) -> Option<gha::Group> {
1138 self.msg(Kind::Doc, compiler.stage, what, compiler.host, target.into())
1139 }
1140
1141 #[must_use = "Groups should not be dropped until the Step finishes running"]
1142 #[track_caller]
1143 fn msg_build(
1144 &self,
1145 compiler: Compiler,
1146 what: impl Display,
1147 target: impl Into<Option<TargetSelection>>,
1148 ) -> Option<gha::Group> {
1149 self.msg(Kind::Build, compiler.stage, what, compiler.host, target)
1150 }
1151
1152 #[must_use = "Groups should not be dropped until the Step finishes running"]
1156 #[track_caller]
1157 fn msg(
1158 &self,
1159 action: impl Into<Kind>,
1160 stage: u32,
1161 what: impl Display,
1162 host: impl Into<Option<TargetSelection>>,
1163 target: impl Into<Option<TargetSelection>>,
1164 ) -> Option<gha::Group> {
1165 let action = action.into().description();
1166 let msg = |fmt| format!("{action} stage{stage} {what}{fmt}");
1167 let msg = if let Some(target) = target.into() {
1168 let host = host.into().unwrap();
1169 if host == target {
1170 msg(format_args!(" ({target})"))
1171 } else {
1172 msg(format_args!(" ({host} -> {target})"))
1173 }
1174 } else {
1175 msg(format_args!(""))
1176 };
1177 self.group(&msg)
1178 }
1179
1180 #[must_use = "Groups should not be dropped until the Step finishes running"]
1184 #[track_caller]
1185 fn msg_unstaged(
1186 &self,
1187 action: impl Into<Kind>,
1188 what: impl Display,
1189 target: TargetSelection,
1190 ) -> Option<gha::Group> {
1191 let action = action.into().description();
1192 let msg = format!("{action} {what} for {target}");
1193 self.group(&msg)
1194 }
1195
1196 #[must_use = "Groups should not be dropped until the Step finishes running"]
1197 #[track_caller]
1198 fn msg_sysroot_tool(
1199 &self,
1200 action: impl Into<Kind>,
1201 stage: u32,
1202 what: impl Display,
1203 host: TargetSelection,
1204 target: TargetSelection,
1205 ) -> Option<gha::Group> {
1206 let action = action.into().description();
1207 let msg = |fmt| format!("{action} {what} {fmt}");
1208 let msg = if host == target {
1209 msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1))
1210 } else {
1211 msg(format_args!("(stage{stage}:{host} -> stage{}:{target})", stage + 1))
1212 };
1213 self.group(&msg)
1214 }
1215
1216 #[track_caller]
1217 fn group(&self, msg: &str) -> Option<gha::Group> {
1218 match self.config.dry_run {
1219 DryRun::SelfCheck => None,
1220 DryRun::Disabled | DryRun::UserSelected => Some(gha::group(msg)),
1221 }
1222 }
1223
1224 fn jobs(&self) -> u32 {
1227 self.config.jobs.unwrap_or_else(|| {
1228 std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
1229 })
1230 }
1231
1232 fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
1233 if !self.config.rust_remap_debuginfo {
1234 return None;
1235 }
1236
1237 match which {
1238 GitRepo::Rustc => {
1239 let sha = self.rust_sha().unwrap_or(&self.version);
1240 Some(format!("/rustc/{sha}"))
1241 }
1242 GitRepo::Llvm => Some(String::from("/rustc/llvm")),
1243 }
1244 }
1245
1246 fn cc(&self, target: TargetSelection) -> PathBuf {
1248 if self.config.dry_run() {
1249 return PathBuf::new();
1250 }
1251 self.cc.borrow()[&target].path().into()
1252 }
1253
1254 fn cc_tool(&self, target: TargetSelection) -> Tool {
1256 self.cc.borrow()[&target].clone()
1257 }
1258
1259 fn cxx_tool(&self, target: TargetSelection) -> Tool {
1261 self.cxx.borrow()[&target].clone()
1262 }
1263
1264 fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec<String> {
1267 if self.config.dry_run() {
1268 return Vec::new();
1269 }
1270 let base = match c {
1271 CLang::C => self.cc.borrow()[&target].clone(),
1272 CLang::Cxx => self.cxx.borrow()[&target].clone(),
1273 };
1274
1275 base.args()
1278 .iter()
1279 .map(|s| s.to_string_lossy().into_owned())
1280 .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
1281 .collect::<Vec<String>>()
1282 }
1283
1284 fn cc_unhandled_cflags(
1286 &self,
1287 target: TargetSelection,
1288 which: GitRepo,
1289 c: CLang,
1290 ) -> Vec<String> {
1291 let mut base = Vec::new();
1292
1293 if matches!(c, CLang::Cxx) && target.contains("apple-darwin") {
1297 base.push("-stdlib=libc++".into());
1298 }
1299
1300 if &*target.triple == "i686-pc-windows-gnu" {
1304 base.push("-fno-omit-frame-pointer".into());
1305 }
1306
1307 if let Some(map_to) = self.debuginfo_map_to(which) {
1308 let map = format!("{}={}", self.src.display(), map_to);
1309 let cc = self.cc(target);
1310 if cc.ends_with("clang") || cc.ends_with("gcc") {
1311 base.push(format!("-fdebug-prefix-map={map}"));
1312 } else if cc.ends_with("clang-cl.exe") {
1313 base.push("-Xclang".into());
1314 base.push(format!("-fdebug-prefix-map={map}"));
1315 }
1316 }
1317 base
1318 }
1319
1320 fn ar(&self, target: TargetSelection) -> Option<PathBuf> {
1322 if self.config.dry_run() {
1323 return None;
1324 }
1325 self.ar.borrow().get(&target).cloned()
1326 }
1327
1328 fn ranlib(&self, target: TargetSelection) -> Option<PathBuf> {
1330 if self.config.dry_run() {
1331 return None;
1332 }
1333 self.ranlib.borrow().get(&target).cloned()
1334 }
1335
1336 fn cxx(&self, target: TargetSelection) -> Result<PathBuf, String> {
1338 if self.config.dry_run() {
1339 return Ok(PathBuf::new());
1340 }
1341 match self.cxx.borrow().get(&target) {
1342 Some(p) => Ok(p.path().into()),
1343 None => Err(format!("target `{target}` is not configured as a host, only as a target")),
1344 }
1345 }
1346
1347 fn linker(&self, target: TargetSelection) -> Option<PathBuf> {
1349 if self.config.dry_run() {
1350 return Some(PathBuf::new());
1351 }
1352 if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
1353 {
1354 Some(linker)
1355 } else if target.contains("vxworks") {
1356 Some(self.cxx.borrow()[&target].path().into())
1359 } else if !self.is_builder_target(target)
1360 && helpers::use_host_linker(target)
1361 && !target.is_msvc()
1362 {
1363 Some(self.cc(target))
1364 } else if self.config.lld_mode.is_used()
1365 && self.is_lld_direct_linker(target)
1366 && self.build == target
1367 {
1368 match self.config.lld_mode {
1369 LldMode::SelfContained => Some(self.initial_lld.clone()),
1370 LldMode::External => Some("lld".into()),
1371 LldMode::Unused => None,
1372 }
1373 } else {
1374 None
1375 }
1376 }
1377
1378 fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
1381 target.is_msvc()
1382 }
1383
1384 fn crt_static(&self, target: TargetSelection) -> Option<bool> {
1386 if target.contains("pc-windows-msvc") {
1387 Some(true)
1388 } else {
1389 self.config.target_config.get(&target).and_then(|t| t.crt_static)
1390 }
1391 }
1392
1393 fn musl_root(&self, target: TargetSelection) -> Option<&Path> {
1395 self.config
1396 .target_config
1397 .get(&target)
1398 .and_then(|t| t.musl_root.as_ref())
1399 .or(self.config.musl_root.as_ref())
1400 .map(|p| &**p)
1401 }
1402
1403 fn musl_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1405 let t = self.config.target_config.get(&target)?;
1406 if let libdir @ Some(_) = &t.musl_libdir {
1407 return libdir.clone();
1408 }
1409 self.musl_root(target).map(|root| root.join("lib"))
1410 }
1411
1412 fn wasi_libdir(&self, target: TargetSelection) -> Option<PathBuf> {
1419 let configured =
1420 self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p);
1421 if let Some(path) = configured {
1422 return Some(path.join("lib").join(target.to_string()));
1423 }
1424 let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?);
1425 env_root.push("share");
1426 env_root.push("wasi-sysroot");
1427 env_root.push("lib");
1428 env_root.push(target.to_string());
1429 Some(env_root)
1430 }
1431
1432 fn no_std(&self, target: TargetSelection) -> Option<bool> {
1434 self.config.target_config.get(&target).map(|t| t.no_std)
1435 }
1436
1437 fn remote_tested(&self, target: TargetSelection) -> bool {
1440 self.qemu_rootfs(target).is_some()
1441 || target.contains("android")
1442 || env::var_os("TEST_DEVICE_ADDR").is_some()
1443 }
1444
1445 fn runner(&self, target: TargetSelection) -> Option<String> {
1451 let configured_runner =
1452 self.config.target_config.get(&target).and_then(|t| t.runner.as_ref()).map(|p| &**p);
1453 if let Some(runner) = configured_runner {
1454 return Some(runner.to_owned());
1455 }
1456
1457 if target.starts_with("wasm") && target.contains("wasi") {
1458 self.default_wasi_runner(target)
1459 } else {
1460 None
1461 }
1462 }
1463
1464 fn default_wasi_runner(&self, target: TargetSelection) -> Option<String> {
1468 let mut finder = crate::core::sanity::Finder::new();
1469
1470 if let Some(path) = finder.maybe_have("wasmtime") {
1474 if let Ok(mut path) = path.into_os_string().into_string() {
1475 path.push_str(" run -C cache=n --dir .");
1476 path.push_str(" --env RUSTC_BOOTSTRAP");
1483
1484 if target.contains("wasip2") {
1485 path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
1486 }
1487
1488 return Some(path);
1489 }
1490 }
1491
1492 None
1493 }
1494
1495 fn tool_enabled(&self, tool: &str) -> bool {
1500 if !self.config.extended {
1501 return false;
1502 }
1503 match &self.config.tools {
1504 Some(set) => set.contains(tool),
1505 None => true,
1506 }
1507 }
1508
1509 fn qemu_rootfs(&self, target: TargetSelection) -> Option<&Path> {
1515 self.config.target_config.get(&target).and_then(|t| t.qemu_rootfs.as_ref()).map(|p| &**p)
1516 }
1517
1518 fn python(&self) -> &Path {
1520 if self.config.build.ends_with("apple-darwin") {
1521 Path::new("/usr/bin/python3")
1525 } else {
1526 self.config
1527 .python
1528 .as_ref()
1529 .expect("python is required for running LLDB or rustdoc tests")
1530 }
1531 }
1532
1533 fn extended_error_dir(&self) -> PathBuf {
1535 self.out.join("tmp/extended-error-metadata")
1536 }
1537
1538 fn force_use_stage1(&self, stage: u32, target: TargetSelection) -> bool {
1557 !self.config.full_bootstrap
1558 && !self.config.download_rustc()
1559 && stage >= 2
1560 && (self.hosts.iter().any(|h| *h == target) || target == self.build)
1561 }
1562
1563 fn force_use_stage2(&self, stage: u32) -> bool {
1569 self.config.download_rustc() && stage >= 2
1570 }
1571
1572 fn release(&self, num: &str) -> String {
1578 match &self.config.channel[..] {
1579 "stable" => num.to_string(),
1580 "beta" => {
1581 if !self.config.omit_git_hash {
1582 format!("{}-beta.{}", num, self.beta_prerelease_version())
1583 } else {
1584 format!("{num}-beta")
1585 }
1586 }
1587 "nightly" => format!("{num}-nightly"),
1588 _ => format!("{num}-dev"),
1589 }
1590 }
1591
1592 fn beta_prerelease_version(&self) -> u32 {
1593 fn extract_beta_rev_from_file<P: AsRef<Path>>(version_file: P) -> Option<String> {
1594 let version = fs::read_to_string(version_file).ok()?;
1595
1596 helpers::extract_beta_rev(&version)
1597 }
1598
1599 if let Some(s) = self.prerelease_version.get() {
1600 return s;
1601 }
1602
1603 let count = extract_beta_rev_from_file(self.src.join("version")).unwrap_or_else(|| {
1607 helpers::git(Some(&self.src))
1611 .arg("rev-list")
1612 .arg("--count")
1613 .arg("--merges")
1614 .arg(format!(
1615 "refs/remotes/origin/{}..HEAD",
1616 self.config.stage0_metadata.config.nightly_branch
1617 ))
1618 .run_always()
1619 .run_capture(self)
1620 .stdout()
1621 });
1622 let n = count.trim().parse().unwrap();
1623 self.prerelease_version.set(Some(n));
1624 n
1625 }
1626
1627 fn rust_release(&self) -> String {
1629 self.release(&self.version)
1630 }
1631
1632 fn package_vers(&self, num: &str) -> String {
1639 match &self.config.channel[..] {
1640 "stable" => num.to_string(),
1641 "beta" => "beta".to_string(),
1642 "nightly" => "nightly".to_string(),
1643 _ => format!("{num}-dev"),
1644 }
1645 }
1646
1647 fn rust_package_vers(&self) -> String {
1649 self.package_vers(&self.version)
1650 }
1651
1652 fn rust_version(&self) -> String {
1658 let mut version = self.rust_info().version(self, &self.version);
1659 if let Some(ref s) = self.config.description {
1660 if !s.is_empty() {
1661 version.push_str(" (");
1662 version.push_str(s);
1663 version.push(')');
1664 }
1665 }
1666 version
1667 }
1668
1669 fn rust_sha(&self) -> Option<&str> {
1671 self.rust_info().sha()
1672 }
1673
1674 fn release_num(&self, package: &str) -> String {
1676 let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml"));
1677 let toml = t!(fs::read_to_string(toml_file_name));
1678 for line in toml.lines() {
1679 if let Some(stripped) =
1680 line.strip_prefix("version = \"").and_then(|s| s.strip_suffix('"'))
1681 {
1682 return stripped.to_owned();
1683 }
1684 }
1685
1686 panic!("failed to find version in {package}'s Cargo.toml")
1687 }
1688
1689 fn unstable_features(&self) -> bool {
1692 !matches!(&self.config.channel[..], "stable" | "beta")
1693 }
1694
1695 fn in_tree_crates(&self, root: &str, target: Option<TargetSelection>) -> Vec<&Crate> {
1699 let mut ret = Vec::new();
1700 let mut list = vec![root.to_owned()];
1701 let mut visited = HashSet::new();
1702 while let Some(krate) = list.pop() {
1703 let krate = self
1704 .crates
1705 .get(&krate)
1706 .unwrap_or_else(|| panic!("metadata missing for {krate}: {:?}", self.crates));
1707 ret.push(krate);
1708 for dep in &krate.deps {
1709 if !self.crates.contains_key(dep) {
1710 continue;
1712 }
1713 if visited.insert(dep)
1719 && (dep != "profiler_builtins"
1720 || target
1721 .map(|t| self.config.profiler_enabled(t))
1722 .unwrap_or_else(|| self.config.any_profiler_enabled()))
1723 && (dep != "rustc_codegen_llvm"
1724 || self.config.hosts.iter().any(|host| self.config.llvm_enabled(*host)))
1725 {
1726 list.push(dep.clone());
1727 }
1728 }
1729 }
1730 ret.sort_unstable_by_key(|krate| krate.name.clone()); ret
1732 }
1733
1734 fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> {
1735 if self.config.dry_run() {
1736 return Vec::new();
1737 }
1738
1739 if !stamp.path().exists() {
1740 eprintln!(
1741 "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
1742 stamp.path().display()
1743 );
1744 crate::exit!(1);
1745 }
1746
1747 let mut paths = Vec::new();
1748 let contents = t!(fs::read(stamp.path()), stamp.path());
1749 for part in contents.split(|b| *b == 0) {
1752 if part.is_empty() {
1753 continue;
1754 }
1755 let dependency_type = match part[0] as char {
1756 'h' => DependencyType::Host,
1757 's' => DependencyType::TargetSelfContained,
1758 't' => DependencyType::Target,
1759 _ => unreachable!(),
1760 };
1761 let path = PathBuf::from(t!(str::from_utf8(&part[1..])));
1762 paths.push((path, dependency_type));
1763 }
1764 paths
1765 }
1766
1767 pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) {
1772 self.copy_link_internal(src, dst, true);
1773 }
1774
1775 pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
1780 self.copy_link_internal(src, dst, false);
1781
1782 if file_type.could_have_split_debuginfo() {
1783 if let Some(dbg_file) = split_debuginfo(src) {
1784 self.copy_link_internal(
1785 &dbg_file,
1786 &dst.with_extension(dbg_file.extension().unwrap()),
1787 false,
1788 );
1789 }
1790 }
1791 }
1792
1793 fn copy_link_internal(&self, src: &Path, dst: &Path, dereference_symlinks: bool) {
1794 if self.config.dry_run() {
1795 return;
1796 }
1797 self.verbose_than(1, || println!("Copy/Link {src:?} to {dst:?}"));
1798 if src == dst {
1799 return;
1800 }
1801 if let Err(e) = fs::remove_file(dst) {
1802 if cfg!(windows) && e.kind() != io::ErrorKind::NotFound {
1803 let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
1806 let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
1807 }
1808 }
1809 let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
1810 let mut src = src.to_path_buf();
1811 if metadata.file_type().is_symlink() {
1812 if dereference_symlinks {
1813 src = t!(fs::canonicalize(src));
1814 } else {
1815 let link = t!(fs::read_link(src));
1816 t!(self.symlink_file(link, dst));
1817 return;
1818 }
1819 }
1820 if let Ok(()) = fs::hard_link(&src, dst) {
1821 } else {
1824 if let Err(e) = fs::copy(&src, dst) {
1825 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
1826 }
1827 t!(fs::set_permissions(dst, metadata.permissions()));
1828
1829 let file_times = fs::FileTimes::new()
1832 .set_accessed(t!(metadata.accessed()))
1833 .set_modified(t!(metadata.modified()));
1834 t!(set_file_times(dst, file_times));
1835 }
1836 }
1837
1838 pub fn cp_link_r(&self, src: &Path, dst: &Path) {
1842 if self.config.dry_run() {
1843 return;
1844 }
1845 for f in self.read_dir(src) {
1846 let path = f.path();
1847 let name = path.file_name().unwrap();
1848 let dst = dst.join(name);
1849 if t!(f.file_type()).is_dir() {
1850 t!(fs::create_dir_all(&dst));
1851 self.cp_link_r(&path, &dst);
1852 } else {
1853 self.copy_link(&path, &dst, FileType::Regular);
1854 }
1855 }
1856 }
1857
1858 pub fn cp_link_filtered(&self, src: &Path, dst: &Path, filter: &dyn Fn(&Path) -> bool) {
1864 self.cp_link_filtered_recurse(src, dst, Path::new(""), filter)
1866 }
1867
1868 fn cp_link_filtered_recurse(
1870 &self,
1871 src: &Path,
1872 dst: &Path,
1873 relative: &Path,
1874 filter: &dyn Fn(&Path) -> bool,
1875 ) {
1876 for f in self.read_dir(src) {
1877 let path = f.path();
1878 let name = path.file_name().unwrap();
1879 let dst = dst.join(name);
1880 let relative = relative.join(name);
1881 if filter(&relative) {
1883 if t!(f.file_type()).is_dir() {
1884 let _ = fs::remove_dir_all(&dst);
1885 self.create_dir(&dst);
1886 self.cp_link_filtered_recurse(&path, &dst, &relative, filter);
1887 } else {
1888 let _ = fs::remove_file(&dst);
1889 self.copy_link(&path, &dst, FileType::Regular);
1890 }
1891 }
1892 }
1893 }
1894
1895 fn copy_link_to_folder(&self, src: &Path, dest_folder: &Path) {
1896 let file_name = src.file_name().unwrap();
1897 let dest = dest_folder.join(file_name);
1898 self.copy_link(src, &dest, FileType::Regular);
1899 }
1900
1901 fn install(&self, src: &Path, dstdir: &Path, file_type: FileType) {
1902 if self.config.dry_run() {
1903 return;
1904 }
1905 let dst = dstdir.join(src.file_name().unwrap());
1906 self.verbose_than(1, || println!("Install {src:?} to {dst:?}"));
1907 t!(fs::create_dir_all(dstdir));
1908 if !src.exists() {
1909 panic!("ERROR: File \"{}\" not found!", src.display());
1910 }
1911
1912 self.copy_link_internal(src, &dst, true);
1913 chmod(&dst, file_type.perms());
1914
1915 if file_type.could_have_split_debuginfo() {
1917 if let Some(dbg_file) = split_debuginfo(src) {
1918 self.install(&dbg_file, dstdir, FileType::Regular);
1919 }
1920 }
1921 }
1922
1923 fn read(&self, path: &Path) -> String {
1924 if self.config.dry_run() {
1925 return String::new();
1926 }
1927 t!(fs::read_to_string(path))
1928 }
1929
1930 fn create_dir(&self, dir: &Path) {
1931 if self.config.dry_run() {
1932 return;
1933 }
1934 t!(fs::create_dir_all(dir))
1935 }
1936
1937 fn remove_dir(&self, dir: &Path) {
1938 if self.config.dry_run() {
1939 return;
1940 }
1941 t!(fs::remove_dir_all(dir))
1942 }
1943
1944 fn read_dir(&self, dir: &Path) -> impl Iterator<Item = fs::DirEntry> {
1945 let iter = match fs::read_dir(dir) {
1946 Ok(v) => v,
1947 Err(_) if self.config.dry_run() => return vec![].into_iter(),
1948 Err(err) => panic!("could not read dir {dir:?}: {err:?}"),
1949 };
1950 iter.map(|e| t!(e)).collect::<Vec<_>>().into_iter()
1951 }
1952
1953 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, link: Q) -> io::Result<()> {
1954 #[cfg(unix)]
1955 use std::os::unix::fs::symlink as symlink_file;
1956 #[cfg(windows)]
1957 use std::os::windows::fs::symlink_file;
1958 if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
1959 }
1960
1961 fn ninja(&self) -> bool {
1964 let mut cmd_finder = crate::core::sanity::Finder::new();
1965
1966 if self.config.ninja_in_file {
1967 if cmd_finder.maybe_have("ninja-build").is_none()
1970 && cmd_finder.maybe_have("ninja").is_none()
1971 {
1972 eprintln!(
1973 "
1974Couldn't find required command: ninja (or ninja-build)
1975
1976You should install ninja as described at
1977<https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages>,
1978or set `ninja = false` in the `[llvm]` section of `bootstrap.toml`.
1979Alternatively, set `download-ci-llvm = true` in that `[llvm]` section
1980to download LLVM rather than building it.
1981"
1982 );
1983 exit!(1);
1984 }
1985 }
1986
1987 if !self.config.ninja_in_file
1995 && self.config.build.is_msvc()
1996 && cmd_finder.maybe_have("ninja").is_some()
1997 {
1998 return true;
1999 }
2000
2001 self.config.ninja_in_file
2002 }
2003
2004 pub fn colored_stdout<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
2005 self.colored_stream_inner(StandardStream::stdout, self.config.stdout_is_tty, f)
2006 }
2007
2008 pub fn colored_stderr<R, F: FnOnce(&mut dyn WriteColor) -> R>(&self, f: F) -> R {
2009 self.colored_stream_inner(StandardStream::stderr, self.config.stderr_is_tty, f)
2010 }
2011
2012 fn colored_stream_inner<R, F, C>(&self, constructor: C, is_tty: bool, f: F) -> R
2013 where
2014 C: Fn(ColorChoice) -> StandardStream,
2015 F: FnOnce(&mut dyn WriteColor) -> R,
2016 {
2017 let choice = match self.config.color {
2018 flags::Color::Always => ColorChoice::Always,
2019 flags::Color::Never => ColorChoice::Never,
2020 flags::Color::Auto if !is_tty => ColorChoice::Never,
2021 flags::Color::Auto => ColorChoice::Auto,
2022 };
2023 let mut stream = constructor(choice);
2024 let result = f(&mut stream);
2025 stream.reset().unwrap();
2026 result
2027 }
2028
2029 fn is_builder_target(&self, target: TargetSelection) -> bool {
2031 self.config.build == target
2032 }
2033}
2034
2035#[cfg(unix)]
2036fn chmod(path: &Path, perms: u32) {
2037 use std::os::unix::fs::*;
2038 t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
2039}
2040#[cfg(windows)]
2041fn chmod(_path: &Path, _perms: u32) {}
2042
2043impl Compiler {
2044 pub fn new(stage: u32, host: TargetSelection) -> Self {
2045 Self { stage, host, forced_compiler: false }
2046 }
2047
2048 pub fn forced_compiler(&mut self, forced_compiler: bool) {
2049 self.forced_compiler = forced_compiler;
2050 }
2051
2052 pub fn with_stage(mut self, stage: u32) -> Compiler {
2053 self.stage = stage;
2054 self
2055 }
2056
2057 pub fn is_snapshot(&self, build: &Build) -> bool {
2059 self.stage == 0 && self.host == build.build
2060 }
2061
2062 pub fn is_forced_compiler(&self) -> bool {
2064 self.forced_compiler
2065 }
2066}
2067
2068fn envify(s: &str) -> String {
2069 s.chars()
2070 .map(|c| match c {
2071 '-' => '_',
2072 c => c,
2073 })
2074 .flat_map(|c| c.to_uppercase())
2075 .collect()
2076}
2077
2078pub fn prepare_behaviour_dump_dir(build: &Build) {
2080 static INITIALIZED: OnceLock<bool> = OnceLock::new();
2081
2082 let dump_path = build.out.join("bootstrap-shims-dump");
2083
2084 let initialized = INITIALIZED.get().unwrap_or(&false);
2085 if !initialized {
2086 if dump_path.exists() {
2088 t!(fs::remove_dir_all(&dump_path));
2089 }
2090
2091 t!(fs::create_dir_all(&dump_path));
2092
2093 t!(INITIALIZED.set(true));
2094 }
2095}