Skip to content

Commit ca9cbea

Browse files
authored
Rollup merge of #82739 - jyn514:separate-stage0-stage1, r=Mark-Simulacrum
Use the beta compiler for building bootstrap tools when `download-rustc` is set ## Motivation This avoids having to rebuild bootstrap and tidy each time you rebase over master. In particular, it makes rebasing and running `x.py fmt` on each commit in a branch significantly faster. It also avoids having to rebuild bootstrap after setting `download-rustc = true`. ## Implementation Instead of extracting the CI artifacts directly to `stage0/`, extract them to `ci-rustc/` instead. Continue to copy them to the proper sysroots as necessary for all stages except stage 0. This also requires `bootstrap.py` to download both stage0 and CI artifacts and distinguish between the two when checking stamp files. Note that since tools have to be built by the same compiler that built `rustc-dev` and the standard library, the downloaded artifacts can't be reused when building with the beta compiler. To make sure this is still a good user experience, warn when building with the beta compiler, and default to building with stage 2. I tested this by rebasing this PR from edeee91 over 1c77a1f and confirming that only the bootstrap library itself had to be rebuilt, not any dependencies and not `tidy`. I also tested that a clean build with `x.py build` builds rustdoc exactly once and does no other work, and that `touch src/librustdoc/lib.rs && x.py build` works. `x.py check` still behaves as before (checks using the beta compiler, even if there are changes to `compiler/`). Helps with #81930. r? `@Mark-Simulacrum`
2 parents e64dbb1 + 14406df commit ca9cbea

File tree

4 files changed

+159
-118
lines changed

4 files changed

+159
-118
lines changed

src/bootstrap/bootstrap.py

+83-68
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ def __init__(self):
383383
self.nix_deps_dir = None
384384
self.rustc_commit = None
385385

386-
def download_stage0(self):
386+
def download_toolchain(self, stage0=True, rustc_channel=None):
387387
"""Fetch the build system for Rust, written in Rust
388388
389389
This method will build a cache directory, then it will fetch the
@@ -393,43 +393,47 @@ def download_stage0(self):
393393
Each downloaded tarball is extracted, after that, the script
394394
will move all the content to the right place.
395395
"""
396-
rustc_channel = self.rustc_channel
396+
if rustc_channel is None:
397+
rustc_channel = self.rustc_channel
397398
rustfmt_channel = self.rustfmt_channel
398-
399-
if self.rustc().startswith(self.bin_root()) and \
400-
(not os.path.exists(self.rustc()) or
401-
self.program_out_of_date(self.rustc_stamp(), self.date + str(self.rustc_commit))):
402-
if os.path.exists(self.bin_root()):
403-
shutil.rmtree(self.bin_root())
404-
download_rustc = self.rustc_commit is not None
399+
bin_root = self.bin_root(stage0)
400+
401+
key = self.date
402+
if not stage0:
403+
key += str(self.rustc_commit)
404+
if self.rustc(stage0).startswith(bin_root) and \
405+
(not os.path.exists(self.rustc(stage0)) or
406+
self.program_out_of_date(self.rustc_stamp(stage0), key)):
407+
if os.path.exists(bin_root):
408+
shutil.rmtree(bin_root)
405409
tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz'
406410
filename = "rust-std-{}-{}{}".format(
407411
rustc_channel, self.build, tarball_suffix)
408412
pattern = "rust-std-{}".format(self.build)
409-
self._download_component_helper(filename, pattern, tarball_suffix, download_rustc)
413+
self._download_component_helper(filename, pattern, tarball_suffix, stage0)
410414
filename = "rustc-{}-{}{}".format(rustc_channel, self.build,
411415
tarball_suffix)
412-
self._download_component_helper(filename, "rustc", tarball_suffix, download_rustc)
416+
self._download_component_helper(filename, "rustc", tarball_suffix, stage0)
413417
filename = "cargo-{}-{}{}".format(rustc_channel, self.build,
414418
tarball_suffix)
415419
self._download_component_helper(filename, "cargo", tarball_suffix)
416-
if self.rustc_commit is not None:
420+
if not stage0:
417421
filename = "rustc-dev-{}-{}{}".format(rustc_channel, self.build, tarball_suffix)
418422
self._download_component_helper(
419-
filename, "rustc-dev", tarball_suffix, download_rustc
423+
filename, "rustc-dev", tarball_suffix, stage0
420424
)
421425

422-
self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root()))
423-
self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root()))
424-
self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root()))
425-
lib_dir = "{}/lib".format(self.bin_root())
426+
self.fix_bin_or_dylib("{}/bin/rustc".format(bin_root))
427+
self.fix_bin_or_dylib("{}/bin/rustdoc".format(bin_root))
428+
self.fix_bin_or_dylib("{}/bin/cargo".format(bin_root))
429+
lib_dir = "{}/lib".format(bin_root)
426430
for lib in os.listdir(lib_dir):
427431
if lib.endswith(".so"):
428432
self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True)
429-
with output(self.rustc_stamp()) as rust_stamp:
430-
rust_stamp.write(self.date + str(self.rustc_commit))
433+
with output(self.rustc_stamp(stage0)) as rust_stamp:
434+
rust_stamp.write(key)
431435

432-
if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and (
436+
if self.rustfmt() and self.rustfmt().startswith(bin_root) and (
433437
not os.path.exists(self.rustfmt())
434438
or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel)
435439
):
@@ -440,12 +444,13 @@ def download_stage0(self):
440444
self._download_component_helper(
441445
filename, "rustfmt-preview", tarball_suffix, key=date
442446
)
443-
self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root()))
444-
self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root()))
447+
self.fix_bin_or_dylib("{}/bin/rustfmt".format(bin_root))
448+
self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(bin_root))
445449
with output(self.rustfmt_stamp()) as rustfmt_stamp:
446450
rustfmt_stamp.write(self.rustfmt_channel)
447451

448-
if self.downloading_llvm():
452+
# Avoid downloading LLVM twice (once for stage0 and once for the master rustc)
453+
if self.downloading_llvm() and stage0:
449454
# We want the most recent LLVM submodule update to avoid downloading
450455
# LLVM more often than necessary.
451456
#
@@ -498,27 +503,26 @@ def downloading_llvm(self):
498503
or (opt == "if-available" and self.build in supported_platforms)
499504

500505
def _download_component_helper(
501-
self, filename, pattern, tarball_suffix, download_rustc=False, key=None
506+
self, filename, pattern, tarball_suffix, stage0=True, key=None
502507
):
503508
if key is None:
504-
if download_rustc:
505-
key = self.rustc_commit
506-
else:
509+
if stage0:
507510
key = self.date
511+
else:
512+
key = self.rustc_commit
508513
cache_dst = os.path.join(self.build_dir, "cache")
509514
rustc_cache = os.path.join(cache_dst, key)
510515
if not os.path.exists(rustc_cache):
511516
os.makedirs(rustc_cache)
512517

513-
if download_rustc:
514-
url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit)
515-
else:
518+
if stage0:
516519
url = "{}/dist/{}".format(self._download_url, key)
520+
else:
521+
url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(self.rustc_commit)
517522
tarball = os.path.join(rustc_cache, filename)
518523
if not os.path.exists(tarball):
519-
do_verify = not download_rustc
520-
get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=do_verify)
521-
unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
524+
get("{}/{}".format(url, filename), tarball, verbose=self.verbose, do_verify=stage0)
525+
unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose)
522526

523527
def _download_ci_llvm(self, llvm_sha, llvm_assertions):
524528
cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions)
@@ -576,10 +580,10 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False):
576580
nix_os_msg = "info: you seem to be running NixOS. Attempting to patch"
577581
print(nix_os_msg, fname)
578582

579-
# Only build `stage0/.nix-deps` once.
583+
# Only build `.nix-deps` once.
580584
nix_deps_dir = self.nix_deps_dir
581585
if not nix_deps_dir:
582-
nix_deps_dir = "{}/.nix-deps".format(self.bin_root())
586+
nix_deps_dir = ".nix-deps"
583587
if not os.path.exists(nix_deps_dir):
584588
os.makedirs(nix_deps_dir)
585589

@@ -637,8 +641,8 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False):
637641
print("warning: failed to call patchelf:", reason)
638642
return
639643

640-
# Return the stage1 compiler to download, if any.
641-
def maybe_download_rustc(self):
644+
# If `download-rustc` is set, download the most recent commit with CI artifacts
645+
def maybe_download_ci_toolchain(self):
642646
# If `download-rustc` is not set, default to rebuilding.
643647
if self.get_toml("download-rustc", section="rust") != "true":
644648
return None
@@ -658,17 +662,23 @@ def maybe_download_rustc(self):
658662
if status != 0:
659663
print("warning: `download-rustc` is enabled, but there are changes to compiler/")
660664

661-
return commit
665+
if self.verbose:
666+
print("using downloaded stage1 artifacts from CI (commit {})".format(commit))
667+
self.rustc_commit = commit
668+
# FIXME: support downloading artifacts from the beta channel
669+
self.download_toolchain(False, "nightly")
662670

663-
def rustc_stamp(self):
664-
"""Return the path for .rustc-stamp
671+
def rustc_stamp(self, stage0):
672+
"""Return the path for .rustc-stamp at the given stage
665673
666674
>>> rb = RustBuild()
667675
>>> rb.build_dir = "build"
668-
>>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
676+
>>> rb.rustc_stamp(True) == os.path.join("build", "stage0", ".rustc-stamp")
677+
True
678+
>>> rb.rustc_stamp(False) == os.path.join("build", "ci-rustc", ".rustc-stamp")
669679
True
670680
"""
671-
return os.path.join(self.bin_root(), '.rustc-stamp')
681+
return os.path.join(self.bin_root(stage0), '.rustc-stamp')
672682

673683
def rustfmt_stamp(self):
674684
"""Return the path for .rustfmt-stamp
@@ -678,7 +688,7 @@ def rustfmt_stamp(self):
678688
>>> rb.rustfmt_stamp() == os.path.join("build", "stage0", ".rustfmt-stamp")
679689
True
680690
"""
681-
return os.path.join(self.bin_root(), '.rustfmt-stamp')
691+
return os.path.join(self.bin_root(True), '.rustfmt-stamp')
682692

683693
def llvm_stamp(self):
684694
"""Return the path for .rustfmt-stamp
@@ -698,21 +708,27 @@ def program_out_of_date(self, stamp_path, key):
698708
with open(stamp_path, 'r') as stamp:
699709
return key != stamp.read()
700710

701-
def bin_root(self):
702-
"""Return the binary root directory
711+
def bin_root(self, stage0):
712+
"""Return the binary root directory for the given stage
703713
704714
>>> rb = RustBuild()
705715
>>> rb.build_dir = "build"
706-
>>> rb.bin_root() == os.path.join("build", "stage0")
716+
>>> rb.bin_root(True) == os.path.join("build", "stage0")
717+
True
718+
>>> rb.bin_root(False) == os.path.join("build", "ci-rustc")
707719
True
708720
709721
When the 'build' property is given should be a nested directory:
710722
711723
>>> rb.build = "devel"
712-
>>> rb.bin_root() == os.path.join("build", "devel", "stage0")
724+
>>> rb.bin_root(True) == os.path.join("build", "devel", "stage0")
713725
True
714726
"""
715-
return os.path.join(self.build_dir, self.build, "stage0")
727+
if stage0:
728+
subdir = "stage0"
729+
else:
730+
subdir = "ci-rustc"
731+
return os.path.join(self.build_dir, self.build, subdir)
716732

717733
def llvm_root(self):
718734
"""Return the CI LLVM root directory
@@ -775,33 +791,37 @@ def cargo(self):
775791
"""Return config path for cargo"""
776792
return self.program_config('cargo')
777793

778-
def rustc(self):
794+
def rustc(self, stage0):
779795
"""Return config path for rustc"""
780-
return self.program_config('rustc')
796+
return self.program_config('rustc', stage0)
781797

782798
def rustfmt(self):
783799
"""Return config path for rustfmt"""
784800
if not self.rustfmt_channel:
785801
return None
786802
return self.program_config('rustfmt')
787803

788-
def program_config(self, program):
789-
"""Return config path for the given program
804+
def program_config(self, program, stage0=True):
805+
"""Return config path for the given program at the given stage
790806
791807
>>> rb = RustBuild()
792808
>>> rb.config_toml = 'rustc = "rustc"\\n'
793809
>>> rb.program_config('rustc')
794810
'rustc'
795811
>>> rb.config_toml = ''
796-
>>> cargo_path = rb.program_config('cargo')
797-
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(),
812+
>>> cargo_path = rb.program_config('cargo', True)
813+
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(True),
814+
... "bin", "cargo")
815+
True
816+
>>> cargo_path = rb.program_config('cargo', False)
817+
>>> cargo_path.rstrip(".exe") == os.path.join(rb.bin_root(False),
798818
... "bin", "cargo")
799819
True
800820
"""
801821
config = self.get_toml(program)
802822
if config:
803823
return os.path.expanduser(config)
804-
return os.path.join(self.bin_root(), "bin", "{}{}".format(
824+
return os.path.join(self.bin_root(stage0), "bin", "{}{}".format(
805825
program, self.exe_suffix()))
806826

807827
@staticmethod
@@ -856,14 +876,14 @@ def build_bootstrap(self):
856876
if "CARGO_BUILD_TARGET" in env:
857877
del env["CARGO_BUILD_TARGET"]
858878
env["CARGO_TARGET_DIR"] = build_dir
859-
env["RUSTC"] = self.rustc()
860-
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
879+
env["RUSTC"] = self.rustc(True)
880+
env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
861881
(os.pathsep + env["LD_LIBRARY_PATH"]) \
862882
if "LD_LIBRARY_PATH" in env else ""
863-
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
883+
env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
864884
(os.pathsep + env["DYLD_LIBRARY_PATH"]) \
865885
if "DYLD_LIBRARY_PATH" in env else ""
866-
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
886+
env["LIBRARY_PATH"] = os.path.join(self.bin_root(True), "lib") + \
867887
(os.pathsep + env["LIBRARY_PATH"]) \
868888
if "LIBRARY_PATH" in env else ""
869889
# preserve existing RUSTFLAGS
@@ -886,7 +906,7 @@ def build_bootstrap(self):
886906
if self.get_toml("deny-warnings", "rust") != "false":
887907
env["RUSTFLAGS"] += " -Dwarnings"
888908

889-
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
909+
env["PATH"] = os.path.join(self.bin_root(True), "bin") + \
890910
os.pathsep + env["PATH"]
891911
if not os.path.isfile(self.cargo()):
892912
raise Exception("no cargo executable found at `{}`".format(
@@ -1137,14 +1157,9 @@ def bootstrap(help_triggered):
11371157
build.update_submodules()
11381158

11391159
# Fetch/build the bootstrap
1140-
build.rustc_commit = build.maybe_download_rustc()
1141-
if build.rustc_commit is not None:
1142-
if build.verbose:
1143-
commit = build.rustc_commit
1144-
print("using downloaded stage1 artifacts from CI (commit {})".format(commit))
1145-
# FIXME: support downloading artifacts from the beta channel
1146-
build.rustc_channel = "nightly"
1147-
build.download_stage0()
1160+
build.download_toolchain()
1161+
# Download the master compiler if `download-rustc` is set
1162+
build.maybe_download_ci_toolchain()
11481163
sys.stdout.flush()
11491164
build.ensure_vendored()
11501165
build.build_bootstrap()

src/bootstrap/compile.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ impl Step for Std {
6565

6666
// These artifacts were already copied (in `impl Step for Sysroot`).
6767
// Don't recompile them.
68-
if builder.config.download_rustc {
68+
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
69+
// so its artifacts can't be reused.
70+
if builder.config.download_rustc && compiler.stage != 0 {
6971
return;
7072
}
7173

@@ -513,7 +515,9 @@ impl Step for Rustc {
513515
let compiler = self.compiler;
514516
let target = self.target;
515517

516-
if builder.config.download_rustc {
518+
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
519+
// so its artifacts can't be reused.
520+
if builder.config.download_rustc && compiler.stage != 0 {
517521
// Copy the existing artifacts instead of rebuilding them.
518522
// NOTE: this path is only taken for tools linking to rustc-dev.
519523
builder.ensure(Sysroot { compiler });
@@ -934,14 +938,15 @@ impl Step for Sysroot {
934938
t!(fs::create_dir_all(&sysroot));
935939

936940
// If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0.
937-
if builder.config.download_rustc {
941+
if builder.config.download_rustc && compiler.stage != 0 {
938942
assert_eq!(
939943
builder.config.build, compiler.host,
940944
"Cross-compiling is not yet supported with `download-rustc`",
941945
);
942946
// Copy the compiler into the correct sysroot.
943-
let stage0_dir = builder.config.out.join(&*builder.config.build.triple).join("stage0");
944-
builder.cp_r(&stage0_dir, &sysroot);
947+
let ci_rustc_dir =
948+
builder.config.out.join(&*builder.config.build.triple).join("ci-rustc");
949+
builder.cp_r(&ci_rustc_dir, &sysroot);
945950
return INTERNER.intern_path(sysroot);
946951
}
947952

0 commit comments

Comments
 (0)