Skip to content

Commit c04a7bc

Browse files
committed
download bootstrap binary from ci
1 parent 49e5e4e commit c04a7bc

File tree

1 file changed

+144
-26
lines changed

1 file changed

+144
-26
lines changed

Diff for: src/bootstrap/bootstrap.py

+144-26
Original file line numberDiff line numberDiff line change
@@ -48,44 +48,45 @@ def eprint(*args, **kwargs):
4848
print(*args, **kwargs)
4949

5050

51-
def get(base, url, path, checksums, verbose=False):
51+
def get(base, url, path, checksums, verbose=False, verify_checksum=True):
5252
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
5353
temp_path = temp_file.name
54-
5554
try:
56-
if url not in checksums:
55+
if url not in checksums and verify_checksum:
5756
raise RuntimeError(
5857
(
59-
"src/stage0 doesn't contain a checksum for {}. "
58+
"src/stage0.json doesn't contain a checksum for {}. "
6059
"Pre-built artifacts might not be available for this "
6160
"target at this time, see https://doc.rust-lang.org/nightly"
6261
"/rustc/platform-support.html for more information."
6362
).format(url)
6463
)
65-
sha256 = checksums[url]
66-
if os.path.exists(path):
67-
if verify(path, sha256, False):
68-
if verbose:
69-
eprint("using already-download file", path)
70-
return
71-
else:
72-
if verbose:
73-
eprint(
74-
"ignoring already-download file",
75-
path,
76-
"due to failed verification",
77-
)
78-
os.unlink(path)
64+
if verify_checksum:
65+
sha256 = checksums[url]
66+
if os.path.exists(path):
67+
if verify(path, sha256, False):
68+
if verbose:
69+
print("using already-download file", path, file=sys.stderr)
70+
return
71+
else:
72+
if verbose:
73+
print(
74+
"ignoring already-download file",
75+
path,
76+
"due to failed verification",
77+
file=sys.stderr,
78+
)
79+
os.unlink(path)
7980
download(temp_path, "{}/{}".format(base, url), True, verbose)
80-
if not verify(temp_path, sha256, verbose):
81+
if verify_checksum and not verify(temp_path, checksums[url], verbose):
8182
raise RuntimeError("failed verification")
8283
if verbose:
83-
eprint("moving {} to {}".format(temp_path, path))
84+
print("moving {} to {}".format(temp_path, path), file=sys.stderr)
8485
shutil.move(temp_path, path)
8586
finally:
8687
if os.path.isfile(temp_path):
8788
if verbose:
88-
eprint("removing", temp_path)
89+
print("removing", temp_path, file=sys.stderr)
8990
os.unlink(temp_path)
9091

9192

@@ -267,6 +268,12 @@ def require(cmd, exit=True, exception=False):
267268
return None
268269

269270

271+
def output_cmd(cmd):
272+
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
273+
output = p.communicate()[0].strip('"').strip()
274+
return output
275+
276+
270277
def format_build_time(duration):
271278
"""Return a nicer format for build time
272279
@@ -531,6 +538,7 @@ class FakeArgs:
531538
"""Used for unit tests to avoid updating all call sites"""
532539

533540
def __init__(self):
541+
self.is_precompiled_bootstrap = False
534542
self.build = ""
535543
self.build_dir = ""
536544
self.clean = False
@@ -558,6 +566,7 @@ def __init__(self, config_toml="", args=None):
558566
self.verbose = args.verbose
559567
self.color = args.color
560568
self.warnings = args.warnings
569+
self.is_precompiled_bootstrap = False
561570

562571
config_verbose_count = self.get_toml("verbose", "build")
563572
if config_verbose_count is not None:
@@ -724,6 +733,105 @@ def download_toolchain(self):
724733
with output(self.rustc_stamp()) as rust_stamp:
725734
rust_stamp.write(key)
726735

736+
def is_bootstrap_modified(self):
737+
cmd = ["git", "status", "--porcelain", "src/bootstrap"]
738+
try:
739+
output = output_cmd(cmd)
740+
return bool(output)
741+
except subprocess.CalledProcessError:
742+
return False
743+
744+
def download_or_build_bootstrap(self):
745+
try:
746+
if self.is_bootstrap_modified():
747+
self.is_precompiled_bootstrap = False
748+
self.build_bootstrap()
749+
return
750+
last_commit = self.last_bootstrap_commit()
751+
if last_commit is None:
752+
self.build_bootstrap()
753+
return
754+
if self.bootstrap_out_of_date(last_commit):
755+
success = self.download_bootstrap(last_commit)
756+
if success:
757+
stamp = os.path.join(
758+
self.build_dir, "bootstrap", ".bootstrap-stamp"
759+
)
760+
with open(stamp, "w") as f:
761+
f.write(last_commit)
762+
self.is_precompiled_bootstrap = True
763+
764+
except Exception as e:
765+
return
766+
767+
if not self.is_precompiled_bootstrap:
768+
self.build_bootstrap()
769+
770+
def download_bootstrap(self, commit_hash):
771+
filename = f"bootstrap-nightly-{self.build_triple()}.tar.gz"
772+
tarball_suffix = "tar.gz"
773+
key = commit_hash
774+
pattern = "bootstrap"
775+
776+
bootstrap_path = os.path.join(
777+
self.bin_root(),
778+
f"nightly-{self.build_triple()}",
779+
"bootstrap",
780+
"bootstrap",
781+
"bin",
782+
"bootstrap",
783+
)
784+
785+
if os.path.exists(bootstrap_path):
786+
return True
787+
788+
cache_dir = os.path.join(self.build_dir, "cache")
789+
tarball_path = os.path.join(cache_dir, filename)
790+
base_url = self.stage0_data.get("artifacts_server")
791+
download_url = f"{key}/{filename}"
792+
793+
if not os.path.exists(tarball_path):
794+
try:
795+
get(
796+
base_url,
797+
download_url,
798+
tarball_path,
799+
self.stage0_data,
800+
verbose=self.verbose,
801+
verify_checksum=False,
802+
)
803+
except Exception as e:
804+
return False
805+
806+
try:
807+
unpack(
808+
tarball_path,
809+
tarball_suffix,
810+
self.bin_root(),
811+
match=pattern,
812+
verbose=self.verbose,
813+
)
814+
except Exception as e:
815+
return False
816+
817+
return True
818+
819+
def last_bootstrap_commit(self):
820+
cmd = ["git", "log", "-1", "--pretty=format:%H", "src/bootstrap"]
821+
try:
822+
commit_hash = output_cmd(cmd)
823+
return commit_hash.strip() if commit_hash else None
824+
except subprocess.CalledProcessError:
825+
return None
826+
827+
def bootstrap_out_of_date(self, commit: str):
828+
stamp_path = os.path.join(self.bin_root(), "bootstrap", ".bootstrap-stamp")
829+
if not os.path.exists(stamp_path):
830+
return True
831+
with open(stamp_path, "r") as f:
832+
stamp_commit = f.read().strip()
833+
return stamp_commit != commit
834+
727835
def should_fix_bins_and_dylibs(self):
728836
"""Whether or not `fix_bin_or_dylib` needs to be run; can only be True
729837
on NixOS or if bootstrap.toml has `build.patch-binaries-for-nix` set.
@@ -995,7 +1103,14 @@ def bootstrap_binary(self):
9951103
... "debug", "bootstrap")
9961104
True
9971105
"""
998-
return os.path.join(self.bootstrap_out(), "debug", "bootstrap")
1106+
if self.is_precompiled_bootstrap:
1107+
root = self.bin_root()
1108+
subfolder = "nightly-{}".format(self.build_triple())
1109+
return os.path.join(
1110+
root, subfolder, "bootstrap", "bootstrap", "bin", "bootstrap"
1111+
)
1112+
else:
1113+
return os.path.join(self.bootstrap_out(), "debug", "bootstrap")
9991114

10001115
def build_bootstrap(self):
10011116
"""Build bootstrap"""
@@ -1330,13 +1445,16 @@ def bootstrap(args):
13301445
build = RustBuild(config_toml, args)
13311446
build.check_vendored_status()
13321447

1448+
build_dir = args.build_dir or build.get_toml("build_dir", "build") or "build"
1449+
build.build_dir = os.path.abspath(build_dir)
1450+
13331451
if not os.path.exists(build.build_dir):
13341452
os.makedirs(os.path.realpath(build.build_dir))
13351453

1336-
# Fetch/build the bootstrap
13371454
build.download_toolchain()
13381455
sys.stdout.flush()
1339-
build.build_bootstrap()
1456+
1457+
build.download_or_build_bootstrap()
13401458
sys.stdout.flush()
13411459

13421460
# Run the bootstrap
@@ -1362,8 +1480,8 @@ def main():
13621480
# process has to happen before anything is printed out.
13631481
if help_triggered:
13641482
eprint(
1365-
"INFO: Downloading and building bootstrap before processing --help command.\n"
1366-
" See src/bootstrap/README.md for help with common commands."
1483+
"INFO: Checking if bootstrap needs to be downloaded or built before processing"
1484+
"--help command.\n See src/bootstrap/README.md for help with common commands."
13671485
)
13681486

13691487
exit_code = 0

0 commit comments

Comments
 (0)