Skip to content

Commit fb471de

Browse files
committed
Make all download functions need only Config, not Builder
This also adds a new `mod download` instead of scattering the download code across `config.rs` and `native.rs`.
1 parent 7b513af commit fb471de

11 files changed

+663
-637
lines changed

src/bootstrap/builder.rs

+5-256
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ use std::any::{type_name, Any};
22
use std::cell::{Cell, RefCell};
33
use std::collections::BTreeSet;
44
use std::env;
5-
use std::ffi::{OsStr, OsString};
5+
use std::ffi::OsStr;
66
use std::fmt::{Debug, Write};
7-
use std::fs::{self, File};
7+
use std::fs::{self};
88
use std::hash::Hash;
9-
use std::io::{BufRead, BufReader, ErrorKind};
109
use std::ops::Deref;
1110
use std::path::{Component, Path, PathBuf};
12-
use std::process::{Command, Stdio};
11+
use std::process::Command;
1312
use std::time::{Duration, Instant};
1413

1514
use crate::cache::{Cache, Interned, INTERNER};
@@ -24,14 +23,12 @@ use crate::test;
2423
use crate::tool::{self, SourceType};
2524
use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
2625
use crate::EXTRA_CHECK_CFGS;
27-
use crate::{check, Config};
28-
use crate::{compile, Crate};
26+
use crate::{check, compile, Crate};
2927
use crate::{Build, CLang, DocTests, GitRepo, Mode};
3028

3129
pub use crate::Compiler;
3230
// FIXME: replace with std::lazy after it gets stabilized and reaches beta
33-
use once_cell::sync::{Lazy, OnceCell};
34-
use xz2::bufread::XzDecoder;
31+
use once_cell::sync::Lazy;
3532

3633
pub struct Builder<'a> {
3734
pub build: &'a Build,
@@ -853,241 +850,6 @@ impl<'a> Builder<'a> {
853850
StepDescription::run(v, self, paths);
854851
}
855852

856-
/// Modifies the interpreter section of 'fname' to fix the dynamic linker,
857-
/// or the RPATH section, to fix the dynamic library search path
858-
///
859-
/// This is only required on NixOS and uses the PatchELF utility to
860-
/// change the interpreter/RPATH of ELF executables.
861-
///
862-
/// Please see https://nixos.org/patchelf.html for more information
863-
pub(crate) fn fix_bin_or_dylib(&self, fname: &Path) {
864-
// FIXME: cache NixOS detection?
865-
match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
866-
Err(_) => return,
867-
Ok(output) if !output.status.success() => return,
868-
Ok(output) => {
869-
let mut s = output.stdout;
870-
if s.last() == Some(&b'\n') {
871-
s.pop();
872-
}
873-
if s != b"Linux" {
874-
return;
875-
}
876-
}
877-
}
878-
879-
// If the user has asked binaries to be patched for Nix, then
880-
// don't check for NixOS or `/lib`, just continue to the patching.
881-
// NOTE: this intentionally comes after the Linux check:
882-
// - patchelf only works with ELF files, so no need to run it on Mac or Windows
883-
// - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc.
884-
if !self.config.patch_binaries_for_nix {
885-
// Use `/etc/os-release` instead of `/etc/NIXOS`.
886-
// The latter one does not exist on NixOS when using tmpfs as root.
887-
const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
888-
let os_release = match File::open("/etc/os-release") {
889-
Err(e) if e.kind() == ErrorKind::NotFound => return,
890-
Err(e) => panic!("failed to access /etc/os-release: {}", e),
891-
Ok(f) => f,
892-
};
893-
if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
894-
return;
895-
}
896-
if Path::new("/lib").exists() {
897-
return;
898-
}
899-
}
900-
901-
// At this point we're pretty sure the user is running NixOS or using Nix
902-
println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());
903-
904-
// Only build `.nix-deps` once.
905-
static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
906-
let mut nix_build_succeeded = true;
907-
let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
908-
// Run `nix-build` to "build" each dependency (which will likely reuse
909-
// the existing `/nix/store` copy, or at most download a pre-built copy).
910-
//
911-
// Importantly, we create a gc-root called `.nix-deps` in the `build/`
912-
// directory, but still reference the actual `/nix/store` path in the rpath
913-
// as it makes it significantly more robust against changes to the location of
914-
// the `.nix-deps` location.
915-
//
916-
// bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
917-
// zlib: Needed as a system dependency of `libLLVM-*.so`.
918-
// patchelf: Needed for patching ELF binaries (see doc comment above).
919-
let nix_deps_dir = self.out.join(".nix-deps");
920-
const NIX_EXPR: &str = "
921-
with (import <nixpkgs> {});
922-
symlinkJoin {
923-
name = \"rust-stage0-dependencies\";
924-
paths = [
925-
zlib
926-
patchelf
927-
stdenv.cc.bintools
928-
];
929-
}
930-
";
931-
nix_build_succeeded = self.try_run(Command::new("nix-build").args(&[
932-
Path::new("-E"),
933-
Path::new(NIX_EXPR),
934-
Path::new("-o"),
935-
&nix_deps_dir,
936-
]));
937-
nix_deps_dir
938-
});
939-
if !nix_build_succeeded {
940-
return;
941-
}
942-
943-
let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
944-
let rpath_entries = {
945-
// ORIGIN is a relative default, all binary and dynamic libraries we ship
946-
// appear to have this (even when `../lib` is redundant).
947-
// NOTE: there are only two paths here, delimited by a `:`
948-
let mut entries = OsString::from("$ORIGIN/../lib:");
949-
entries.push(t!(fs::canonicalize(nix_deps_dir)));
950-
entries.push("/lib");
951-
entries
952-
};
953-
patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
954-
if !fname.extension().map_or(false, |ext| ext == "so") {
955-
// Finally, set the correct .interp for binaries
956-
let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
957-
// FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
958-
let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
959-
patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
960-
}
961-
962-
self.try_run(patchelf.arg(fname));
963-
}
964-
965-
pub(crate) fn download_component(&self, url: &str, dest_path: &Path, help_on_error: &str) {
966-
self.verbose(&format!("download {url}"));
967-
// Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
968-
let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
969-
// While bootstrap itself only supports http and https downloads, downstream forks might
970-
// need to download components from other protocols. The match allows them adding more
971-
// protocols without worrying about merge conflicts if we change the HTTP implementation.
972-
match url.split_once("://").map(|(proto, _)| proto) {
973-
Some("http") | Some("https") => {
974-
self.download_http_with_retries(&tempfile, url, help_on_error)
975-
}
976-
Some(other) => panic!("unsupported protocol {other} in {url}"),
977-
None => panic!("no protocol in {url}"),
978-
}
979-
t!(std::fs::rename(&tempfile, dest_path));
980-
}
981-
982-
fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
983-
println!("downloading {}", url);
984-
// Try curl. If that fails and we are on windows, fallback to PowerShell.
985-
let mut curl = Command::new("curl");
986-
curl.args(&[
987-
"-#",
988-
"-y",
989-
"30",
990-
"-Y",
991-
"10", // timeout if speed is < 10 bytes/sec for > 30 seconds
992-
"--connect-timeout",
993-
"30", // timeout if cannot connect within 30 seconds
994-
"--retry",
995-
"3",
996-
"-Sf",
997-
"-o",
998-
]);
999-
curl.arg(tempfile);
1000-
curl.arg(url);
1001-
if !self.check_run(&mut curl) {
1002-
if self.build.build.contains("windows-msvc") {
1003-
println!("Fallback to PowerShell");
1004-
for _ in 0..3 {
1005-
if self.try_run(Command::new("PowerShell.exe").args(&[
1006-
"/nologo",
1007-
"-Command",
1008-
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
1009-
&format!(
1010-
"(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
1011-
url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"),
1012-
),
1013-
])) {
1014-
return;
1015-
}
1016-
println!("\nspurious failure, trying again");
1017-
}
1018-
}
1019-
if !help_on_error.is_empty() {
1020-
eprintln!("{}", help_on_error);
1021-
}
1022-
crate::detail_exit(1);
1023-
}
1024-
}
1025-
1026-
pub(crate) fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
1027-
println!("extracting {} to {}", tarball.display(), dst.display());
1028-
if !dst.exists() {
1029-
t!(fs::create_dir_all(dst));
1030-
}
1031-
1032-
// `tarball` ends with `.tar.xz`; strip that suffix
1033-
// example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
1034-
let uncompressed_filename =
1035-
Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
1036-
let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
1037-
1038-
// decompress the file
1039-
let data = t!(File::open(tarball));
1040-
let decompressor = XzDecoder::new(BufReader::new(data));
1041-
1042-
let mut tar = tar::Archive::new(decompressor);
1043-
for member in t!(tar.entries()) {
1044-
let mut member = t!(member);
1045-
let original_path = t!(member.path()).into_owned();
1046-
// skip the top-level directory
1047-
if original_path == directory_prefix {
1048-
continue;
1049-
}
1050-
let mut short_path = t!(original_path.strip_prefix(directory_prefix));
1051-
if !short_path.starts_with(pattern) {
1052-
continue;
1053-
}
1054-
short_path = t!(short_path.strip_prefix(pattern));
1055-
let dst_path = dst.join(short_path);
1056-
self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
1057-
if !t!(member.unpack_in(dst)) {
1058-
panic!("path traversal attack ??");
1059-
}
1060-
let src_path = dst.join(original_path);
1061-
if src_path.is_dir() && dst_path.exists() {
1062-
continue;
1063-
}
1064-
t!(fs::rename(src_path, dst_path));
1065-
}
1066-
t!(fs::remove_dir_all(dst.join(directory_prefix)));
1067-
}
1068-
1069-
/// Returns whether the SHA256 checksum of `path` matches `expected`.
1070-
pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool {
1071-
use sha2::Digest;
1072-
1073-
self.verbose(&format!("verifying {}", path.display()));
1074-
let mut hasher = sha2::Sha256::new();
1075-
// FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components.
1076-
// Consider using streaming IO instead?
1077-
let contents = if self.config.dry_run() { vec![] } else { t!(fs::read(path)) };
1078-
hasher.update(&contents);
1079-
let found = hex::encode(hasher.finalize().as_slice());
1080-
let verified = found == expected;
1081-
if !verified && !self.config.dry_run() {
1082-
println!(
1083-
"invalid checksum: \n\
1084-
found: {found}\n\
1085-
expected: {expected}",
1086-
);
1087-
}
1088-
return verified;
1089-
}
1090-
1091853
/// Obtain a compiler at a given stage and for a given host. Explicitly does
1092854
/// not take `Compiler` since all `Compiler` instances are meant to be
1093855
/// obtained through this function, since it ensures that they are valid
@@ -1301,19 +1063,6 @@ impl<'a> Builder<'a> {
13011063
None
13021064
}
13031065

1304-
/// Convenience wrapper to allow `builder.llvm_link_shared()` instead of `builder.config.llvm_link_shared(&builder)`.
1305-
pub(crate) fn llvm_link_shared(&self) -> bool {
1306-
Config::llvm_link_shared(self)
1307-
}
1308-
1309-
pub(crate) fn download_rustc(&self) -> bool {
1310-
Config::download_rustc(self)
1311-
}
1312-
1313-
pub(crate) fn initial_rustfmt(&self) -> Option<PathBuf> {
1314-
Config::initial_rustfmt(self)
1315-
}
1316-
13171066
/// Prepares an invocation of `cargo` to be run.
13181067
///
13191068
/// This will create a `Command` that represents a pending execution of

src/bootstrap/channel.rs

+3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ use crate::util::output;
1313
use crate::util::t;
1414
use crate::Build;
1515

16+
#[derive(Clone, Default)]
1617
pub enum GitInfo {
1718
/// This is not a git repository.
19+
#[default]
1820
Absent,
1921
/// This is a git repository.
2022
/// If the info should be used (`ignore_git` is false), this will be
@@ -25,6 +27,7 @@ pub enum GitInfo {
2527
RecordedForTarball(Info),
2628
}
2729

30+
#[derive(Clone)]
2831
pub struct Info {
2932
pub commit_date: String,
3033
pub sha: String,

src/bootstrap/compile.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -763,10 +763,10 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
763763

764764
cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
765765

766-
if let Some(ref ver_date) = builder.rust_info.commit_date() {
766+
if let Some(ref ver_date) = builder.rust_info().commit_date() {
767767
cargo.env("CFG_VER_DATE", ver_date);
768768
}
769-
if let Some(ref ver_hash) = builder.rust_info.sha() {
769+
if let Some(ref ver_hash) = builder.rust_info().sha() {
770770
cargo.env("CFG_VER_HASH", ver_hash);
771771
}
772772
if !builder.unstable_features() {

0 commit comments

Comments
 (0)