Skip to content

Commit 895fe51

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

File tree

1 file changed

+155
-26
lines changed

1 file changed

+155
-26
lines changed

src/bootstrap/bootstrap.py

+155-26
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,11 @@ 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
(
5958
"src/stage0 doesn't contain a checksum for {}. "
@@ -62,30 +61,32 @@ def get(base, url, path, checksums, verbose=False):
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
@@ -297,7 +304,7 @@ def default_build_triple(verbose):
297304
"detected default triple {} from pre-installed rustc".format(triple)
298305
)
299306
return triple
300-
except Exception as e:
307+
except Exception:
301308
if verbose:
302309
eprint("pre-installed rustc not detected: {}".format(e))
303310
eprint("falling back to auto-detect")
@@ -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,119 @@ 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+
os.makedirs(os.path.dirname(stamp), exist_ok=True)
761+
with open(stamp, "w") as f:
762+
f.write(last_commit)
763+
self.is_precompiled_bootstrap = True
764+
765+
except Exception:
766+
return
767+
768+
if not self.is_precompiled_bootstrap:
769+
self.build_bootstrap()
770+
771+
def download_bootstrap(self, commit_hash):
772+
filename = f"bootstrap-nightly-{self.build_triple()}.tar.xz"
773+
tarball_suffix = ".tar.xz"
774+
key = commit_hash
775+
pattern = "bootstrap"
776+
777+
bootstrap_path = os.path.join(
778+
self.bin_root(),
779+
f"nightly-{self.build_triple()}",
780+
"bootstrap",
781+
"bootstrap",
782+
"bin",
783+
"bootstrap",
784+
)
785+
786+
if os.path.exists(bootstrap_path):
787+
return True
788+
789+
cache_dir = os.path.join(self.build_dir, "cache")
790+
tarball_path = os.path.join(cache_dir, filename)
791+
base_url = self.stage0_data.get("artifacts_server")
792+
download_url = f"{key}/{filename}"
793+
794+
if not os.path.exists(tarball_path):
795+
try:
796+
get(
797+
base_url,
798+
download_url,
799+
tarball_path,
800+
self.stage0_data,
801+
verbose=self.verbose,
802+
verify_checksum=False,
803+
)
804+
except Exception:
805+
return False
806+
807+
try:
808+
unpack(
809+
tarball_path,
810+
tarball_suffix,
811+
self.bin_root(),
812+
match=pattern,
813+
verbose=self.verbose,
814+
)
815+
except Exception:
816+
return False
817+
818+
return True
819+
820+
def last_bootstrap_commit(self):
821+
merge_email = self.stage0_data.get("git_merge_commit_email")
822+
if not merge_email:
823+
return None
824+
825+
cmd = [
826+
"git",
827+
"log",
828+
"-1",
829+
f"--author={merge_email}",
830+
"--pretty=format:%H",
831+
"src/bootstrap",
832+
]
833+
834+
try:
835+
commit_hash = output_cmd(cmd)
836+
except subprocess.CalledProcessError:
837+
return None
838+
839+
return commit_hash.strip() if commit_hash else None
840+
841+
def bootstrap_out_of_date(self, commit: str):
842+
stamp_path = os.path.join(self.bin_root(), "bootstrap", ".bootstrap-stamp")
843+
if not os.path.exists(stamp_path):
844+
return True
845+
with open(stamp_path, "r") as f:
846+
stamp_commit = f.read().strip()
847+
return stamp_commit != commit
848+
727849
def should_fix_bins_and_dylibs(self):
728850
"""Whether or not `fix_bin_or_dylib` needs to be run; can only be True
729851
on NixOS or if bootstrap.toml has `build.patch-binaries-for-nix` set.
@@ -995,7 +1117,11 @@ def bootstrap_binary(self):
9951117
... "debug", "bootstrap")
9961118
True
9971119
"""
998-
return os.path.join(self.bootstrap_out(), "debug", "bootstrap")
1120+
if self.is_precompiled_bootstrap:
1121+
root = self.bin_root()
1122+
return os.path.join(root, "bootstrap", "bin", "bootstrap")
1123+
else:
1124+
return os.path.join(self.bootstrap_out(), "debug", "bootstrap")
9991125

10001126
def build_bootstrap(self):
10011127
"""Build bootstrap"""
@@ -1330,13 +1456,16 @@ def bootstrap(args):
13301456
build = RustBuild(config_toml, args)
13311457
build.check_vendored_status()
13321458

1459+
build_dir = args.build_dir or build.get_toml("build_dir", "build") or "build"
1460+
build.build_dir = os.path.abspath(build_dir)
1461+
13331462
if not os.path.exists(build.build_dir):
13341463
os.makedirs(os.path.realpath(build.build_dir))
13351464

1336-
# Fetch/build the bootstrap
13371465
build.download_toolchain()
13381466
sys.stdout.flush()
1339-
build.build_bootstrap()
1467+
1468+
build.download_or_build_bootstrap()
13401469
sys.stdout.flush()
13411470

13421471
# Run the bootstrap
@@ -1362,8 +1491,8 @@ def main():
13621491
# process has to happen before anything is printed out.
13631492
if help_triggered:
13641493
eprint(
1365-
"INFO: Downloading and building bootstrap before processing --help command.\n"
1366-
" See src/bootstrap/README.md for help with common commands."
1494+
"INFO: Checking if bootstrap needs to be downloaded or built before processing"
1495+
"--help command.\n See src/bootstrap/README.md for help with common commands."
13671496
)
13681497

13691498
exit_code = 0

0 commit comments

Comments
 (0)