提交 53bf24c8 编写于 作者: J Joshua Nelson

Move download-rustc from bootstrap.py to rustbuild

- Remove download-rustc handling from bootstrap.py
- Allow a custom `pattern` in `builder.unpack()`
- Only download rustc once another part of bootstrap depends on it.

  This is somewhat necessary since the download functions rely on having a full
  `Builder`, which isn't available until after config parsing finishes.
上级 61dd278a
......@@ -446,7 +446,7 @@ class RustBuild(object):
self.nix_deps_dir = None
self.rustc_commit = None
def download_toolchain(self, stage0=True, rustc_channel=None):
def download_toolchain(self, rustc_channel=None):
"""Fetch the build system for Rust, written in Rust
This method will build a cache directory, then it will fetch the
......@@ -458,35 +458,26 @@ class RustBuild(object):
"""
if rustc_channel is None:
rustc_channel = self.stage0_compiler.version
bin_root = self.bin_root(stage0)
bin_root = self.bin_root()
key = self.stage0_compiler.date
if not stage0:
key += str(self.rustc_commit)
if self.rustc(stage0).startswith(bin_root) and \
(not os.path.exists(self.rustc(stage0)) or
self.program_out_of_date(self.rustc_stamp(stage0), key)):
if self.rustc().startswith(bin_root) and \
(not os.path.exists(self.rustc()) or
self.program_out_of_date(self.rustc_stamp(), key)):
if os.path.exists(bin_root):
shutil.rmtree(bin_root)
tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
filename = "rust-std-{}-{}{}".format(
rustc_channel, self.build, tarball_suffix)
pattern = "rust-std-{}".format(self.build)
self._download_component_helper(filename, pattern, tarball_suffix, stage0)
self._download_component_helper(filename, pattern, tarball_suffix)
filename = "rustc-{}-{}{}".format(rustc_channel, self.build,
tarball_suffix)
self._download_component_helper(filename, "rustc", tarball_suffix, stage0)
# download-rustc doesn't need its own cargo, it can just use beta's.
if stage0:
filename = "cargo-{}-{}{}".format(rustc_channel, self.build,
tarball_suffix)
self._download_component_helper(filename, "cargo", tarball_suffix)
self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root))
else:
filename = "rustc-dev-{}-{}{}".format(rustc_channel, self.build, tarball_suffix)
self._download_component_helper(
filename, "rustc-dev", tarball_suffix, stage0
)
self._download_component_helper(filename, "rustc", tarball_suffix)
filename = "cargo-{}-{}{}".format(rustc_channel, self.build,
tarball_suffix)
self._download_component_helper(filename, "cargo", tarball_suffix)
self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root))
self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root))
self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root))
......@@ -494,7 +485,7 @@ class RustBuild(object):
for lib in os.listdir(lib_dir):
if lib.endswith(".so"):
self.fix_bin_or_dylib(os.path.join(lib_dir, lib))
with output(self.rustc_stamp(stage0)) as rust_stamp:
with output(self.rustc_stamp()) as rust_stamp:
rust_stamp.write(key)
if self.rustfmt() and self.rustfmt().startswith(bin_root) and (
......@@ -518,24 +509,17 @@ class RustBuild(object):
rustfmt_stamp.write(self.stage0_rustfmt.channel())
def _download_component_helper(
self, filename, pattern, tarball_suffix, stage0=True, key=None
self, filename, pattern, tarball_suffix, key=None
):
if key is None:
if stage0:
key = self.stage0_compiler.date
else:
key = self.rustc_commit
key = self.stage0_compiler.date
cache_dst = os.path.join(self.build_dir, "cache")
rustc_cache = os.path.join(cache_dst, key)
if not os.path.exists(rustc_cache):
os.makedirs(rustc_cache)
if stage0:
base = self._download_url
url = "dist/{}".format(key)
else:
base = "https://ci-artifacts.rust-lang.org"
url = "rustc-builds/{}".format(self.rustc_commit)
base = self._download_url
url = "dist/{}".format(key)
tarball = os.path.join(rustc_cache, filename)
if not os.path.exists(tarball):
get(
......@@ -544,9 +528,9 @@ class RustBuild(object):
tarball,
self.checksums_sha256,
verbose=self.verbose,
do_verify=stage0,
do_verify=True,
)
unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose)
unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
def fix_bin_or_dylib(self, fname):
"""Modifies the interpreter section of 'fname' to fix the dynamic linker,
......@@ -689,17 +673,15 @@ class RustBuild(object):
# FIXME: support downloading artifacts from the beta channel
self.download_toolchain(False, "nightly")
def rustc_stamp(self, stage0):
def rustc_stamp(self):
"""Return the path for .rustc-stamp at the given stage
>>> rb = RustBuild()
>>> rb.build_dir = "build"
>>> rb.rustc_stamp(True) == os.path.join("build", "stage0", ".rustc-stamp")
True
>>> rb.rustc_stamp(False) == os.path.join("build", "ci-rustc", ".rustc-stamp")
>>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
True
"""
return os.path.join(self.bin_root(stage0), '.rustc-stamp')
return os.path.join(self.bin_root(), '.rustc-stamp')
def rustfmt_stamp(self):
"""Return the path for .rustfmt-stamp
......@@ -709,7 +691,7 @@ class RustBuild(object):
>>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp")
True
"""
return os.path.join(self.bin_root(True), '.rustfmt-stamp')
return os.path.join(self.bin_root(), '.rustfmt-stamp')
def program_out_of_date(self, stamp_path, key):
"""Check if the given program stamp is out of date"""
......@@ -718,26 +700,21 @@ class RustBuild(object):
with open(stamp_path, 'r') as stamp:
return key != stamp.read()
def bin_root(self, stage0):
def bin_root(self):
"""Return the binary root directory for the given stage
>>> rb = RustBuild()
>>> rb.build_dir = "build"
>>> rb.bin_root(True) == os.path.join("build", "stage0")
True
>>> rb.bin_root(False) == os.path.join("build", "ci-rustc")
>>> rb.bin_root() == os.path.join("build", "stage0")
True
When the 'build' property is given should be a nested directory:
>>> rb.build = "devel"
>>> rb.bin_root(True) == os.path.join("build", "devel", "stage0")
>>> rb.bin_root() == os.path.join("build", "devel", "stage0")
True
"""
if stage0:
subdir = "stage0"
else:
subdir = "ci-rustc"
subdir = "stage0"
return os.path.join(self.build_dir, self.build, subdir)
def get_toml(self, key, section=None):
......@@ -785,9 +762,9 @@ class RustBuild(object):
"""Return config path for cargo"""
return self.program_config('cargo')
def rustc(self, stage0):
def rustc(self):
"""Return config path for rustc"""
return self.program_config('rustc', stage0)
return self.program_config('rustc')
def rustfmt(self):
"""Return config path for rustfmt"""
......@@ -795,7 +772,7 @@ class RustBuild(object):
return None
return self.program_config('rustfmt')
def program_config(self, program, stage0=True):
def program_config(self, program):
"""Return config path for the given program at the given stage
>>> rb = RustBuild()
......@@ -803,19 +780,15 @@ class RustBuild(object):
>>> rb.program_config('rustc')
'rustc'
>>> rb.config_toml = ''
>>> cargo_path = rb.program_config('cargo', True)
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(True),
... "bin", "cargo")
True
>>> cargo_path = rb.program_config('cargo', False)
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(False),
>>> cargo_path = rb.program_config('cargo')
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
... "bin", "cargo")
True
"""
config = self.get_toml(program)
if config:
return os.path.expanduser(config)
return os.path.join(self.bin_root(stage0), "bin", "{}{}".format(
return os.path.join(self.bin_root(), "bin", "{}{}".format(
program, self.exe_suffix()))
@staticmethod
......@@ -871,14 +844,14 @@ class RustBuild(object):
if "CARGO_BUILD_TARGET" in env:
del env["CARGO_BUILD_TARGET"]
env["CARGO_TARGET_DIR"] = build_dir
env["RUSTC"] = self.rustc(True)
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
env["RUSTC"] = self.rustc()
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
(os.pathsep + env["LD_LIBRARY_PATH"]) \
if "LD_LIBRARY_PATH" in env else ""
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
(os.pathsep + env["DYLD_LIBRARY_PATH"]) \
if "DYLD_LIBRARY_PATH" in env else ""
env["LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
(os.pathsep + env["LIBRARY_PATH"]) \
if "LIBRARY_PATH" in env else ""
......@@ -900,7 +873,7 @@ class RustBuild(object):
if self.get_toml("deny-warnings", "rust") != "false":
env["RUSTFLAGS"] += " -Dwarnings"
env["PATH"] = os.path.join(self.bin_root(True), "bin") + \
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
os.pathsep + env["PATH"]
if not os.path.isfile(self.cargo()):
raise Exception("no cargo executable found at `{}`".format(
......@@ -1172,7 +1145,7 @@ def bootstrap(help_triggered):
# Fetch/build the bootstrap
build.download_toolchain()
# Download the master compiler if `download-rustc` is set
build.maybe_download_ci_toolchain()
# build.maybe_download_ci_toolchain()
sys.stdout.flush()
build.ensure_vendored()
build.build_bootstrap()
......
......@@ -915,15 +915,12 @@ fn download_with_retries(&self, tempfile: &str, url: &str) {
}
}
pub(crate) fn unpack(&self, tarball: &Path, dst: &Path) {
pub(crate) fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
println!("extracting {} to {}", tarball.display(), dst.display());
if !dst.exists() {
t!(fs::create_dir_all(dst));
}
// FIXME: will need to be a parameter once `download-rustc` is moved to rustbuild
const MATCH: &str = "rust-dev";
// `tarball` ends with `.tar.xz`; strip that suffix
// example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
let uncompressed_filename =
......@@ -943,10 +940,10 @@ pub(crate) fn unpack(&self, tarball: &Path, dst: &Path) {
continue;
}
let mut short_path = t!(original_path.strip_prefix(directory_prefix));
if !short_path.starts_with(MATCH) {
if !short_path.starts_with(pattern) {
continue;
}
short_path = t!(short_path.strip_prefix(MATCH));
short_path = t!(short_path.strip_prefix(pattern));
let dst_path = dst.join(short_path);
self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
if !t!(member.unpack_in(dst)) {
......@@ -1022,7 +1019,7 @@ fn run(self, builder: &Builder<'_>) -> Interned<PathBuf> {
.join("lib");
// Avoid deleting the rustlib/ directory we just copied
// (in `impl Step for Sysroot`).
if !builder.config.download_rustc {
if !builder.download_rustc() {
let _ = fs::remove_dir_all(&sysroot);
t!(fs::create_dir_all(&sysroot));
}
......@@ -1179,6 +1176,10 @@ pub(crate) fn llvm_link_shared(&self) -> bool {
Config::llvm_link_shared(self)
}
pub(crate) fn download_rustc(&self) -> bool {
Config::download_rustc(self)
}
/// Prepares an invocation of `cargo` to be run.
///
/// This will create a `Command` that represents a pending execution of
......
......@@ -42,8 +42,10 @@ impl Step for Std {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
// When downloading stage1, the standard library has already been copied to the sysroot, so
// there's no need to rebuild it.
let download_rustc = run.builder.config.download_rustc;
run.all_krates("test").path("library").default_condition(!download_rustc)
let builder = run.builder;
run.all_krates("test")
.path("library")
.lazy_default_condition(Box::new(|| !builder.download_rustc()))
}
fn make_run(run: RunConfig<'_>) {
......@@ -66,7 +68,7 @@ fn run(self, builder: &Builder<'_>) {
// Don't recompile them.
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
// so its artifacts can't be reused.
if builder.config.download_rustc && compiler.stage != 0 {
if builder.download_rustc() && compiler.stage != 0 {
return;
}
......@@ -551,7 +553,7 @@ fn run(self, builder: &Builder<'_>) {
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
// so its artifacts can't be reused.
if builder.config.download_rustc && compiler.stage != 0 {
if builder.download_rustc() && compiler.stage != 0 {
// Copy the existing artifacts instead of rebuilding them.
// NOTE: this path is only taken for tools linking to rustc-dev.
builder.ensure(Sysroot { compiler });
......@@ -995,7 +997,7 @@ fn run(self, builder: &Builder<'_>) -> Interned<PathBuf> {
t!(fs::create_dir_all(&sysroot));
// If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0.
if builder.config.download_rustc && compiler.stage != 0 {
if builder.download_rustc() && compiler.stage != 0 {
assert_eq!(
builder.config.build, compiler.host,
"Cross-compiling is not yet supported with `download-rustc`",
......@@ -1090,7 +1092,7 @@ fn run(self, builder: &Builder<'_>) -> Compiler {
let build_compiler = builder.compiler(target_compiler.stage - 1, builder.config.build);
// If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0.
if builder.config.download_rustc {
if builder.download_rustc() {
builder.ensure(Sysroot { compiler: target_compiler });
return target_compiler;
}
......
......@@ -7,9 +7,11 @@
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::env;
use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};
use std::str::FromStr;
use crate::builder::{Builder, TaskPath};
......@@ -17,7 +19,8 @@
use crate::channel::GitInfo;
pub use crate::flags::Subcommand;
use crate::flags::{Color, Flags};
use crate::util::{exe, t};
use crate::util::{exe, output, program_out_of_date, t};
use once_cell::sync::OnceCell;
use serde::{Deserialize, Deserializer};
macro_rules! check_ci_llvm {
......@@ -81,7 +84,11 @@ pub struct Config {
pub cmd: Subcommand,
pub incremental: bool,
pub dry_run: bool,
pub download_rustc: bool,
/// `None` if we shouldn't download CI compiler artifacts, or the commit to download if we should.
#[cfg(not(test))]
download_rustc_commit: Option<String>,
#[cfg(test)]
pub download_rustc_commit: Option<String>,
pub deny_warnings: bool,
pub backtrace_on_ice: bool,
......@@ -1080,7 +1087,8 @@ pub fn parse(args: &[String]) -> Config {
config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
config.download_rustc = env::var("BOOTSTRAP_DOWNLOAD_RUSTC").as_deref() == Ok("1");
config.download_rustc_commit =
download_ci_rustc_commit(rust.download_rustc, config.verbose > 0);
} else {
config.rust_profile_use = flags.rust_profile_use;
config.rust_profile_generate = flags.rust_profile_generate;
......@@ -1192,7 +1200,7 @@ pub fn parse(args: &[String]) -> Config {
let default = config.channel == "dev";
config.ignore_git = ignore_git.unwrap_or(default);
let download_rustc = config.download_rustc;
let download_rustc = config.download_rustc_commit.is_some();
// See https://github.com/rust-lang/compiler-team/issues/326
config.stage = match config.cmd {
Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0),
......@@ -1309,6 +1317,23 @@ pub(crate) fn llvm_link_shared(builder: &Builder<'_>) -> bool {
llvm_link_shared
}
/// Return whether we will use a downloaded, pre-compiled version of rustc, or just build from source.
pub(crate) fn download_rustc(builder: &Builder<'_>) -> bool {
static DOWNLOAD_RUSTC: OnceCell<bool> = OnceCell::new();
if builder.config.dry_run && DOWNLOAD_RUSTC.get().is_none() {
// avoid trying to actually download the commit
return false;
}
*DOWNLOAD_RUSTC.get_or_init(|| match &builder.config.download_rustc_commit {
None => false,
Some(commit) => {
download_ci_rustc(builder, commit);
true
}
})
}
pub fn verbose(&self) -> bool {
self.verbose > 0
}
......@@ -1358,3 +1383,116 @@ fn threads_from_config(v: u32) -> u32 {
n => n,
}
}
/// Returns the commit to download, or `None` if we shouldn't download CI artifacts.
fn download_ci_rustc_commit(download_rustc: Option<StringOrBool>, verbose: bool) -> Option<String> {
// If `download-rustc` is not set, default to rebuilding.
let if_unchanged = match download_rustc {
None | Some(StringOrBool::Bool(false)) => return None,
Some(StringOrBool::Bool(true)) => false,
Some(StringOrBool::String(s)) if s == "if-unchanged" => true,
Some(StringOrBool::String(other)) => {
panic!("unrecognized option for download-rustc: {}", other)
}
};
// Handle running from a directory other than the top level
let top_level = output(Command::new("git").args(&["rev-parse", "--show-toplevel"]));
let top_level = top_level.trim_end();
let compiler = format!("{top_level}/compiler/");
let library = format!("{top_level}/library/");
// Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts.
let merge_base = output(Command::new("git").args(&[
"rev-list",
"--author=bors@rust-lang.org",
"-n1",
"--first-parent",
"HEAD",
]));
let commit = merge_base.trim_end();
if commit.is_empty() {
println!("error: could not find commit hash for downloading rustc");
println!("help: maybe your repository history is too shallow?");
println!("help: consider disabling `download-rustc`");
println!("help: or fetch enough history to include one upstream commit");
exit(1);
}
// Warn if there were changes to the compiler or standard library since the ancestor commit.
let has_changes = !t!(Command::new("git")
.args(&["diff-index", "--quiet", &commit, "--", &compiler, &library])
.status())
.success();
if has_changes {
if if_unchanged {
if verbose {
println!(
"warning: saw changes to compiler/ or library/ since {commit}; \
ignoring `download-rustc`"
);
}
return None;
}
println!(
"warning: `download-rustc` is enabled, but there are changes to \
compiler/ or library/"
);
}
Some(commit.to_string())
}
fn download_ci_rustc(builder: &Builder<'_>, commit: &str) {
builder.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})"));
// FIXME: support downloading artifacts from the beta channel
const CHANNEL: &str = "nightly";
let host = builder.config.build.triple;
let bin_root = builder.out.join(host).join("ci-rustc");
let rustc_stamp = bin_root.join(".rustc-stamp");
if !bin_root.join("bin").join("rustc").exists() || program_out_of_date(&rustc_stamp, commit) {
if bin_root.exists() {
t!(fs::remove_dir_all(&bin_root));
}
let filename = format!("rust-std-{CHANNEL}-{host}.tar.xz");
let pattern = format!("rust-std-{host}");
download_component(builder, filename, &pattern, commit);
let filename = format!("rustc-{CHANNEL}-{host}.tar.xz");
download_component(builder, filename, "rustc", commit);
// download-rustc doesn't need its own cargo, it can just use beta's.
let filename = format!("rustc-dev-{CHANNEL}-{host}.tar.xz");
download_component(builder, filename, "rustc-dev", commit);
builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustc"));
builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc"));
let lib_dir = bin_root.join("lib");
for lib in t!(fs::read_dir(lib_dir)) {
let lib = t!(lib);
if lib.path().extension() == Some(OsStr::new("so")) {
builder.fix_bin_or_dylib(&lib.path());
}
}
t!(fs::write(rustc_stamp, commit));
}
}
/// Download a single component of a CI-built toolchain (not necessarily a published nightly).
// NOTE: intentionally takes an owned string to avoid downloading multiple times by accident
fn download_component(builder: &Builder<'_>, filename: String, prefix: &str, commit: &str) {
let cache_dst = builder.out.join("cache");
let rustc_cache = cache_dst.join(commit);
if !rustc_cache.exists() {
t!(fs::create_dir_all(&rustc_cache));
}
let base = "https://ci-artifacts.rust-lang.org";
let url = format!("rustc-builds/{commit}");
let tarball = rustc_cache.join(&filename);
if !tarball.exists() {
builder.download_component(base, &format!("{url}/{filename}"), &tarball);
}
let bin_root = builder.out.join(builder.config.build.triple).join("ci-rustc");
builder.unpack(&tarball, &bin_root, prefix)
}
......@@ -182,7 +182,7 @@ fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
builder.download_component(base, &format!("{}/{}", url, filename), &tarball);
}
let llvm_root = builder.config.ci_llvm_root();
builder.unpack(&tarball, &llvm_root);
builder.unpack(&tarball, &llvm_root, "rust-dev");
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
......
......@@ -2058,7 +2058,7 @@ fn run(self, builder: &Builder<'_>) {
let test_kind = self.test_kind;
let target = self.host;
let compiler = if builder.config.download_rustc {
let compiler = if builder.download_rustc() {
builder.compiler(builder.top_stage, target)
} else {
// Use the previous stage compiler to reuse the artifacts that are
......@@ -2127,7 +2127,7 @@ fn run(self, builder: &Builder<'_>) {
// with.
//
// Note that this set the host libdir for `download_rustc`, which uses a normal rust distribution.
let libdir = if builder.config.download_rustc {
let libdir = if builder.download_rustc() {
builder.rustc_libdir(compiler)
} else {
builder.sysroot_libdir(compiler, target).to_path_buf()
......
......@@ -516,7 +516,7 @@ fn run(self, builder: &Builder<'_>) -> PathBuf {
builder.ensure(compile::Rustc { compiler: build_compiler, target: target_compiler.host });
// NOTE: this implies that `download-rustc` is pretty useless when compiling with the stage0
// compiler, since you do just as much work.
if !builder.config.dry_run && builder.config.download_rustc && build_compiler.stage == 0 {
if !builder.config.dry_run && builder.download_rustc() && build_compiler.stage == 0 {
println!(
"warning: `download-rustc` does nothing when building stage1 tools; consider using `--stage 2` instead"
);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册