1use std::env;
2use std::ffi::OsString;
3use std::fs::{self, File};
4use std::io::{BufRead, BufReader, BufWriter, ErrorKind, Write};
5use std::path::{Path, PathBuf};
6use std::process::{Command, Stdio};
7use std::sync::OnceLock;
8
9use xz2::bufread::XzDecoder;
10
11use crate::core::config::BUILDER_CONFIG_FILENAME;
12use crate::utils::build_stamp::BuildStamp;
13use crate::utils::exec::{BootstrapCommand, command};
14use crate::utils::helpers::{check_run, exe, hex_encode, move_file};
15use crate::{Config, t};
16
17static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock<bool> = OnceLock::new();
18
19fn try_run(config: &Config, cmd: &mut Command) -> Result<(), ()> {
21 #[expect(deprecated)]
22 config.try_run(cmd)
23}
24
25fn extract_curl_version(out: &[u8]) -> semver::Version {
26 let out = String::from_utf8_lossy(out);
27 out.lines()
29 .next()
30 .and_then(|line| line.split(" ").nth(1))
31 .and_then(|version| semver::Version::parse(version).ok())
32 .unwrap_or(semver::Version::new(1, 0, 0))
33}
34
35fn curl_version() -> semver::Version {
36 let mut curl = Command::new("curl");
37 curl.arg("-V");
38 let Ok(out) = curl.output() else { return semver::Version::new(1, 0, 0) };
39 let out = out.stdout;
40 extract_curl_version(&out)
41}
42
43impl Config {
45 pub fn is_verbose(&self) -> bool {
46 self.verbose > 0
47 }
48
49 pub(crate) fn create<P: AsRef<Path>>(&self, path: P, s: &str) {
50 if self.dry_run() {
51 return;
52 }
53 t!(fs::write(path, s));
54 }
55
56 pub(crate) fn remove(&self, f: &Path) {
57 if self.dry_run() {
58 return;
59 }
60 fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f));
61 }
62
63 pub(crate) fn tempdir(&self) -> PathBuf {
68 let tmp = self.out.join("tmp");
69 t!(fs::create_dir_all(&tmp));
70 tmp
71 }
72
73 pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
77 if self.dry_run() && !cmd.run_always {
78 return true;
79 }
80 self.verbose(|| println!("running: {cmd:?}"));
81 check_run(cmd, self.is_verbose())
82 }
83
84 fn should_fix_bins_and_dylibs(&self) -> bool {
87 let val = *SHOULD_FIX_BINS_AND_DYLIBS.get_or_init(|| {
88 match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
89 Err(_) => return false,
90 Ok(output) if !output.status.success() => return false,
91 Ok(output) => {
92 let mut os_name = output.stdout;
93 if os_name.last() == Some(&b'\n') {
94 os_name.pop();
95 }
96 if os_name != b"Linux" {
97 return false;
98 }
99 }
100 }
101
102 if let Some(explicit_value) = self.patch_binaries_for_nix {
108 return explicit_value;
109 }
110
111 let is_nixos = match File::open("/etc/os-release") {
114 Err(e) if e.kind() == ErrorKind::NotFound => false,
115 Err(e) => panic!("failed to access /etc/os-release: {}", e),
116 Ok(os_release) => BufReader::new(os_release).lines().any(|l| {
117 let l = l.expect("reading /etc/os-release");
118 matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"")
119 }),
120 };
121 if !is_nixos {
122 let in_nix_shell = env::var("IN_NIX_SHELL");
123 if let Ok(in_nix_shell) = in_nix_shell {
124 eprintln!(
125 "The IN_NIX_SHELL environment variable is `{in_nix_shell}`; \
126 you may need to set `patch-binaries-for-nix=true` in bootstrap.toml"
127 );
128 }
129 }
130 is_nixos
131 });
132 if val {
133 eprintln!("INFO: You seem to be using Nix.");
134 }
135 val
136 }
137
138 fn fix_bin_or_dylib(&self, fname: &Path) {
146 assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true));
147 println!("attempting to patch {}", fname.display());
148
149 static NIX_DEPS_DIR: OnceLock<PathBuf> = OnceLock::new();
151 let mut nix_build_succeeded = true;
152 let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
153 let nix_deps_dir = self.out.join(".nix-deps");
165 const NIX_EXPR: &str = "
166 with (import <nixpkgs> {});
167 symlinkJoin {
168 name = \"rust-stage0-dependencies\";
169 paths = [
170 zlib
171 patchelf
172 stdenv.cc.bintools
173 ];
174 }
175 ";
176 nix_build_succeeded = try_run(
177 self,
178 Command::new("nix-build").args([
179 Path::new("-E"),
180 Path::new(NIX_EXPR),
181 Path::new("-o"),
182 &nix_deps_dir,
183 ]),
184 )
185 .is_ok();
186 nix_deps_dir
187 });
188 if !nix_build_succeeded {
189 return;
190 }
191
192 let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
193 patchelf.args(&[
194 OsString::from("--add-rpath"),
195 OsString::from(t!(fs::canonicalize(nix_deps_dir)).join("lib")),
196 ]);
197 if !path_is_dylib(fname) {
198 let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
200 let dynamic_linker = t!(fs::read_to_string(dynamic_linker_path));
201 patchelf.args(["--set-interpreter", dynamic_linker.trim_end()]);
202 }
203
204 let _ = try_run(self, patchelf.arg(fname));
205 }
206
207 fn download_file(&self, url: &str, dest_path: &Path, help_on_error: &str) {
208 self.verbose(|| println!("download {url}"));
209 let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
211 match url.split_once("://").map(|(proto, _)| proto) {
215 Some("http") | Some("https") => {
216 self.download_http_with_retries(&tempfile, url, help_on_error)
217 }
218 Some(other) => panic!("unsupported protocol {other} in {url}"),
219 None => panic!("no protocol in {url}"),
220 }
221 t!(
222 move_file(&tempfile, dest_path),
223 format!("failed to rename {tempfile:?} to {dest_path:?}")
224 );
225 }
226
227 fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
228 println!("downloading {url}");
229 let mut curl = command("curl");
234 curl.args([
235 "--location",
237 "--speed-time",
239 "30",
240 "--speed-limit",
241 "10",
242 "--connect-timeout",
244 "30",
245 "--output",
247 tempfile.to_str().unwrap(),
248 "--continue-at",
251 "-",
252 "--retry",
255 "3",
256 "--show-error",
258 "--remote-time",
260 "--fail",
262 ]);
263 if self.is_running_on_ci {
265 curl.arg("--silent");
266 } else {
267 curl.arg("--progress-bar");
268 }
269 if curl_version() >= semver::Version::new(7, 71, 0) {
271 curl.arg("--retry-all-errors");
272 }
273 curl.arg(url);
274 if !self.check_run(&mut curl) {
275 if self.build.contains("windows-msvc") {
276 eprintln!("Fallback to PowerShell");
277 for _ in 0..3 {
278 if try_run(self, Command::new("PowerShell.exe").args([
279 "/nologo",
280 "-Command",
281 "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
282 &format!(
283 "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
284 url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"),
285 ),
286 ])).is_err() {
287 return;
288 }
289 eprintln!("\nspurious failure, trying again");
290 }
291 }
292 if !help_on_error.is_empty() {
293 eprintln!("{help_on_error}");
294 }
295 crate::exit!(1);
296 }
297 }
298
299 fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
300 eprintln!("extracting {} to {}", tarball.display(), dst.display());
301 if !dst.exists() {
302 t!(fs::create_dir_all(dst));
303 }
304
305 let uncompressed_filename =
308 Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
309 let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
310
311 let data = t!(File::open(tarball), format!("file {} not found", tarball.display()));
313 let decompressor = XzDecoder::new(BufReader::new(data));
314
315 let mut tar = tar::Archive::new(decompressor);
316
317 let is_ci_rustc = dst.ends_with("ci-rustc");
318 let is_ci_llvm = dst.ends_with("ci-llvm");
319
320 let mut recorded_entries = if is_ci_rustc { recorded_entries(dst, pattern) } else { None };
324
325 for member in t!(tar.entries()) {
326 let mut member = t!(member);
327 let original_path = t!(member.path()).into_owned();
328 if original_path == directory_prefix {
330 continue;
331 }
332 let mut short_path = t!(original_path.strip_prefix(directory_prefix));
333 let is_builder_config = short_path.to_str() == Some(BUILDER_CONFIG_FILENAME);
334
335 if !(short_path.starts_with(pattern)
336 || ((is_ci_rustc || is_ci_llvm) && is_builder_config))
337 {
338 continue;
339 }
340 short_path = short_path.strip_prefix(pattern).unwrap_or(short_path);
341 let dst_path = dst.join(short_path);
342 self.verbose(|| {
343 println!("extracting {} to {}", original_path.display(), dst.display())
344 });
345 if !t!(member.unpack_in(dst)) {
346 panic!("path traversal attack ??");
347 }
348 if let Some(record) = &mut recorded_entries {
349 t!(writeln!(record, "{}", short_path.to_str().unwrap()));
350 }
351 let src_path = dst.join(original_path);
352 if src_path.is_dir() && dst_path.exists() {
353 continue;
354 }
355 t!(move_file(src_path, dst_path));
356 }
357 let dst_dir = dst.join(directory_prefix);
358 if dst_dir.exists() {
359 t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display()));
360 }
361 }
362
363 pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool {
365 use sha2::Digest;
366
367 self.verbose(|| println!("verifying {}", path.display()));
368
369 if self.dry_run() {
370 return false;
371 }
372
373 let mut hasher = sha2::Sha256::new();
374
375 let file = t!(File::open(path));
376 let mut reader = BufReader::new(file);
377
378 loop {
379 let buffer = t!(reader.fill_buf());
380 let l = buffer.len();
381 if l == 0 {
383 break;
384 }
385 hasher.update(buffer);
386 reader.consume(l);
387 }
388
389 let checksum = hex_encode(hasher.finalize().as_slice());
390 let verified = checksum == expected;
391
392 if !verified {
393 println!(
394 "invalid checksum: \n\
395 found: {checksum}\n\
396 expected: {expected}",
397 );
398 }
399
400 verified
401 }
402}
403
404fn recorded_entries(dst: &Path, pattern: &str) -> Option<BufWriter<File>> {
405 let name = if pattern == "rustc-dev" {
406 ".rustc-dev-contents"
407 } else if pattern.starts_with("rust-std") {
408 ".rust-std-contents"
409 } else {
410 return None;
411 };
412 Some(BufWriter::new(t!(File::create(dst.join(name)))))
413}
414
415enum DownloadSource {
416 CI,
417 Dist,
418}
419
420impl Config {
422 pub(crate) fn download_clippy(&self) -> PathBuf {
423 self.verbose(|| println!("downloading stage0 clippy artifacts"));
424
425 let date = &self.stage0_metadata.compiler.date;
426 let version = &self.stage0_metadata.compiler.version;
427 let host = self.build;
428
429 let clippy_stamp =
430 BuildStamp::new(&self.initial_sysroot).with_prefix("clippy").add_stamp(date);
431 let cargo_clippy = self.initial_sysroot.join("bin").join(exe("cargo-clippy", host));
432 if cargo_clippy.exists() && clippy_stamp.is_up_to_date() {
433 return cargo_clippy;
434 }
435
436 let filename = format!("clippy-{version}-{host}.tar.xz");
437 self.download_component(DownloadSource::Dist, filename, "clippy-preview", date, "stage0");
438 if self.should_fix_bins_and_dylibs() {
439 self.fix_bin_or_dylib(&cargo_clippy);
440 self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host)));
441 }
442
443 t!(clippy_stamp.write());
444 cargo_clippy
445 }
446
447 #[cfg(test)]
448 pub(crate) fn maybe_download_rustfmt(&self) -> Option<PathBuf> {
449 None
450 }
451
452 #[cfg(not(test))]
455 pub(crate) fn maybe_download_rustfmt(&self) -> Option<PathBuf> {
456 use build_helper::stage0_parser::VersionMetadata;
457
458 let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?;
459 let channel = format!("{version}-{date}");
460
461 let host = self.build;
462 let bin_root = self.out.join(host).join("rustfmt");
463 let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host));
464 let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel);
465 if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() {
466 return Some(rustfmt_path);
467 }
468
469 self.download_component(
470 DownloadSource::Dist,
471 format!("rustfmt-{version}-{build}.tar.xz", build = host.triple),
472 "rustfmt-preview",
473 date,
474 "rustfmt",
475 );
476 self.download_component(
477 DownloadSource::Dist,
478 format!("rustc-{version}-{build}.tar.xz", build = host.triple),
479 "rustc",
480 date,
481 "rustfmt",
482 );
483
484 if self.should_fix_bins_and_dylibs() {
485 self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt"));
486 self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt"));
487 let lib_dir = bin_root.join("lib");
488 for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
489 let lib = t!(lib);
490 if path_is_dylib(&lib.path()) {
491 self.fix_bin_or_dylib(&lib.path());
492 }
493 }
494 }
495
496 t!(rustfmt_stamp.write());
497 Some(rustfmt_path)
498 }
499
500 pub(crate) fn ci_rust_std_contents(&self) -> Vec<String> {
501 self.ci_component_contents(".rust-std-contents")
502 }
503
504 pub(crate) fn ci_rustc_dev_contents(&self) -> Vec<String> {
505 self.ci_component_contents(".rustc-dev-contents")
506 }
507
508 fn ci_component_contents(&self, stamp_file: &str) -> Vec<String> {
509 assert!(self.download_rustc());
510 if self.dry_run() {
511 return vec![];
512 }
513
514 let ci_rustc_dir = self.ci_rustc_dir();
515 let stamp_file = ci_rustc_dir.join(stamp_file);
516 let contents_file = t!(File::open(&stamp_file), stamp_file.display().to_string());
517 t!(BufReader::new(contents_file).lines().collect())
518 }
519
520 pub(crate) fn download_ci_rustc(&self, commit: &str) {
521 self.verbose(|| println!("using downloaded stage2 artifacts from CI (commit {commit})"));
522
523 let version = self.artifact_version_part(commit);
524 let extra_components = ["rustc-dev"];
527
528 self.download_toolchain(
529 &version,
530 "ci-rustc",
531 &format!("{commit}-{}", self.llvm_assertions),
532 &extra_components,
533 Self::download_ci_component,
534 );
535 }
536
537 #[cfg(test)]
538 pub(crate) fn download_beta_toolchain(&self) {}
539
540 #[cfg(not(test))]
541 pub(crate) fn download_beta_toolchain(&self) {
542 self.verbose(|| println!("downloading stage0 beta artifacts"));
543
544 let date = &self.stage0_metadata.compiler.date;
545 let version = &self.stage0_metadata.compiler.version;
546 let extra_components = ["cargo"];
547
548 let download_beta_component = |config: &Config, filename, prefix: &_, date: &_| {
549 config.download_component(DownloadSource::Dist, filename, prefix, date, "stage0")
550 };
551
552 self.download_toolchain(
553 version,
554 "stage0",
555 date,
556 &extra_components,
557 download_beta_component,
558 );
559 }
560
561 fn download_toolchain(
562 &self,
563 version: &str,
564 sysroot: &str,
565 stamp_key: &str,
566 extra_components: &[&str],
567 download_component: fn(&Config, String, &str, &str),
568 ) {
569 let host = self.build.triple;
570 let bin_root = self.out.join(host).join(sysroot);
571 let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key);
572
573 if !bin_root.join("bin").join(exe("rustc", self.build)).exists()
574 || !rustc_stamp.is_up_to_date()
575 {
576 if bin_root.exists() {
577 t!(fs::remove_dir_all(&bin_root));
578 }
579 let filename = format!("rust-std-{version}-{host}.tar.xz");
580 let pattern = format!("rust-std-{host}");
581 download_component(self, filename, &pattern, stamp_key);
582 let filename = format!("rustc-{version}-{host}.tar.xz");
583 download_component(self, filename, "rustc", stamp_key);
584
585 for component in extra_components {
586 let filename = format!("{component}-{version}-{host}.tar.xz");
587 download_component(self, filename, component, stamp_key);
588 }
589
590 if self.should_fix_bins_and_dylibs() {
591 self.fix_bin_or_dylib(&bin_root.join("bin").join("rustc"));
592 self.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc"));
593 self.fix_bin_or_dylib(
594 &bin_root.join("libexec").join("rust-analyzer-proc-macro-srv"),
595 );
596 let lib_dir = bin_root.join("lib");
597 for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
598 let lib = t!(lib);
599 if path_is_dylib(&lib.path()) {
600 self.fix_bin_or_dylib(&lib.path());
601 }
602 }
603 }
604
605 t!(rustc_stamp.write());
606 }
607 }
608
609 fn download_ci_component(&self, filename: String, prefix: &str, commit_with_assertions: &str) {
612 Self::download_component(
613 self,
614 DownloadSource::CI,
615 filename,
616 prefix,
617 commit_with_assertions,
618 "ci-rustc",
619 )
620 }
621
622 fn download_component(
623 &self,
624 mode: DownloadSource,
625 filename: String,
626 prefix: &str,
627 key: &str,
628 destination: &str,
629 ) {
630 if self.dry_run() {
631 return;
632 }
633
634 let cache_dst =
635 self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
636
637 let cache_dir = cache_dst.join(key);
638 if !cache_dir.exists() {
639 t!(fs::create_dir_all(&cache_dir));
640 }
641
642 let bin_root = self.out.join(self.build).join(destination);
643 let tarball = cache_dir.join(&filename);
644 let (base_url, url, should_verify) = match mode {
645 DownloadSource::CI => {
646 let dist_server = if self.llvm_assertions {
647 self.stage0_metadata.config.artifacts_with_llvm_assertions_server.clone()
648 } else {
649 self.stage0_metadata.config.artifacts_server.clone()
650 };
651 let url = format!(
652 "{}/{filename}",
653 key.strip_suffix(&format!("-{}", self.llvm_assertions)).unwrap()
654 );
655 (dist_server, url, false)
656 }
657 DownloadSource::Dist => {
658 let dist_server = env::var("RUSTUP_DIST_SERVER")
659 .unwrap_or(self.stage0_metadata.config.dist_server.to_string());
660 (dist_server, format!("dist/{key}/{filename}"), true)
662 }
663 };
664
665 let checksum = if should_verify {
667 let error = format!(
668 "src/stage0 doesn't contain a checksum for {url}. \
669 Pre-built artifacts might not be available for this \
670 target at this time, see https://doc.rust-lang.org/nightly\
671 /rustc/platform-support.html for more information."
672 );
673 let sha256 = self.stage0_metadata.checksums_sha256.get(&url).expect(&error);
674 if tarball.exists() {
675 if self.verify(&tarball, sha256) {
676 self.unpack(&tarball, &bin_root, prefix);
677 return;
678 } else {
679 self.verbose(|| {
680 println!(
681 "ignoring cached file {} due to failed verification",
682 tarball.display()
683 )
684 });
685 self.remove(&tarball);
686 }
687 }
688 Some(sha256)
689 } else if tarball.exists() {
690 self.unpack(&tarball, &bin_root, prefix);
691 return;
692 } else {
693 None
694 };
695
696 let mut help_on_error = "";
697 if destination == "ci-rustc" {
698 help_on_error = "ERROR: failed to download pre-built rustc from CI
699
700NOTE: old builds get deleted after a certain time
701HELP: if trying to compile an old commit of rustc, disable `download-rustc` in bootstrap.toml:
702
703[rust]
704download-rustc = false
705";
706 }
707 self.download_file(&format!("{base_url}/{url}"), &tarball, help_on_error);
708 if let Some(sha256) = checksum {
709 if !self.verify(&tarball, sha256) {
710 panic!("failed to verify {}", tarball.display());
711 }
712 }
713
714 self.unpack(&tarball, &bin_root, prefix);
715 }
716
717 #[cfg(test)]
718 pub(crate) fn maybe_download_ci_llvm(&self) {}
719
720 #[cfg(not(test))]
721 pub(crate) fn maybe_download_ci_llvm(&self) {
722 use build_helper::exit;
723
724 use crate::core::build_steps::llvm::detect_llvm_sha;
725 use crate::core::config::check_incompatible_options_for_ci_llvm;
726
727 if !self.llvm_from_ci {
728 return;
729 }
730
731 let llvm_root = self.ci_llvm_root();
732 let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository());
733 let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions);
734 let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key);
735 if !llvm_stamp.is_up_to_date() && !self.dry_run() {
736 self.download_ci_llvm(&llvm_sha);
737
738 if self.should_fix_bins_and_dylibs() {
739 for entry in t!(fs::read_dir(llvm_root.join("bin"))) {
740 self.fix_bin_or_dylib(&t!(entry).path());
741 }
742 }
743
744 let now = std::time::SystemTime::now();
753 let file_times = fs::FileTimes::new().set_accessed(now).set_modified(now);
754
755 let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build));
756 t!(crate::utils::helpers::set_file_times(llvm_config, file_times));
757
758 if self.should_fix_bins_and_dylibs() {
759 let llvm_lib = llvm_root.join("lib");
760 for entry in t!(fs::read_dir(llvm_lib)) {
761 let lib = t!(entry).path();
762 if path_is_dylib(&lib) {
763 self.fix_bin_or_dylib(&lib);
764 }
765 }
766 }
767
768 t!(llvm_stamp.write());
769 }
770
771 if let Some(config_path) = &self.config {
772 let current_config_toml = Self::get_toml(config_path).unwrap();
773
774 match self.get_builder_toml("ci-llvm") {
775 Ok(ci_config_toml) => {
776 t!(check_incompatible_options_for_ci_llvm(current_config_toml, ci_config_toml));
777 }
778 Err(e) if e.to_string().contains("unknown field") => {
779 println!(
780 "WARNING: CI LLVM has some fields that are no longer supported in bootstrap; download-ci-llvm will be disabled."
781 );
782 println!("HELP: Consider rebasing to a newer commit if available.");
783 }
784 Err(e) => {
785 eprintln!("ERROR: Failed to parse CI LLVM bootstrap.toml: {e}");
786 exit!(2);
787 }
788 };
789 };
790 }
791
792 #[cfg(not(test))]
793 fn download_ci_llvm(&self, llvm_sha: &str) {
794 let llvm_assertions = self.llvm_assertions;
795
796 let cache_prefix = format!("llvm-{llvm_sha}-{llvm_assertions}");
797 let cache_dst =
798 self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
799
800 let rustc_cache = cache_dst.join(cache_prefix);
801 if !rustc_cache.exists() {
802 t!(fs::create_dir_all(&rustc_cache));
803 }
804 let base = if llvm_assertions {
805 &self.stage0_metadata.config.artifacts_with_llvm_assertions_server
806 } else {
807 &self.stage0_metadata.config.artifacts_server
808 };
809 let version = self.artifact_version_part(llvm_sha);
810 let filename = format!("rust-dev-{}-{}.tar.xz", version, self.build.triple);
811 let tarball = rustc_cache.join(&filename);
812 if !tarball.exists() {
813 let help_on_error = "ERROR: failed to download llvm from ci
814
815 HELP: There could be two reasons behind this:
816 1) The host triple is not supported for `download-ci-llvm`.
817 2) Old builds get deleted after a certain time.
818 HELP: In either case, disable `download-ci-llvm` in your bootstrap.toml:
819
820 [llvm]
821 download-ci-llvm = false
822 ";
823 self.download_file(&format!("{base}/{llvm_sha}/{filename}"), &tarball, help_on_error);
824 }
825 let llvm_root = self.ci_llvm_root();
826 self.unpack(&tarball, &llvm_root, "rust-dev");
827 }
828
829 pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) {
830 let cache_prefix = format!("gcc-{gcc_sha}");
831 let cache_dst =
832 self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
833
834 let gcc_cache = cache_dst.join(cache_prefix);
835 if !gcc_cache.exists() {
836 t!(fs::create_dir_all(&gcc_cache));
837 }
838 let base = &self.stage0_metadata.config.artifacts_server;
839 let filename = format!("gcc-nightly-{}.tar.xz", self.build.triple);
840 let tarball = gcc_cache.join(&filename);
841 if !tarball.exists() {
842 let help_on_error = "ERROR: failed to download gcc from ci
843
844 HELP: There could be two reasons behind this:
845 1) The host triple is not supported for `download-ci-gcc`.
846 2) Old builds get deleted after a certain time.
847 HELP: In either case, disable `download-ci-gcc` in your bootstrap.toml:
848
849 [gcc]
850 download-ci-gcc = false
851 ";
852 self.download_file(&format!("{base}/{gcc_sha}/{filename}"), &tarball, help_on_error);
853 }
854 self.unpack(&tarball, root_dir, "gcc");
855 }
856}
857
858fn path_is_dylib(path: &Path) -> bool {
859 path.to_str().is_some_and(|path| path.contains(".so"))
861}
862
863pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: bool) -> bool {
865 const SUPPORTED_PLATFORMS: &[&str] = &[
867 "aarch64-apple-darwin",
868 "aarch64-pc-windows-msvc",
869 "aarch64-unknown-linux-gnu",
870 "aarch64-unknown-linux-musl",
871 "arm-unknown-linux-gnueabi",
872 "arm-unknown-linux-gnueabihf",
873 "armv7-unknown-linux-gnueabihf",
874 "i686-pc-windows-gnu",
875 "i686-pc-windows-msvc",
876 "i686-unknown-linux-gnu",
877 "loongarch64-unknown-linux-gnu",
878 "powerpc-unknown-linux-gnu",
879 "powerpc64-unknown-linux-gnu",
880 "powerpc64le-unknown-linux-gnu",
881 "riscv64gc-unknown-linux-gnu",
882 "s390x-unknown-linux-gnu",
883 "x86_64-apple-darwin",
884 "x86_64-pc-windows-gnu",
885 "x86_64-pc-windows-msvc",
886 "x86_64-unknown-freebsd",
887 "x86_64-unknown-illumos",
888 "x86_64-unknown-linux-gnu",
889 "x86_64-unknown-linux-musl",
890 "x86_64-unknown-netbsd",
891 ];
892
893 const SUPPORTED_PLATFORMS_WITH_ASSERTIONS: &[&str] =
894 &["x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"];
895
896 if llvm_assertions {
897 SUPPORTED_PLATFORMS_WITH_ASSERTIONS.contains(&target_triple)
898 } else {
899 SUPPORTED_PLATFORMS.contains(&target_triple)
900 }
901}