@@ -378,6 +378,7 @@ def __init__(self):
378378 self .verbose = False
379379 self .git_version = None
380380 self .nix_deps_dir = None
381+ self .rustc_commit = None
381382
382383 def download_stage0 (self ):
383384 """Fetch the build system for Rust, written in Rust
@@ -394,20 +395,27 @@ def download_stage0(self):
394395
395396 if self .rustc ().startswith (self .bin_root ()) and \
396397 (not os .path .exists (self .rustc ()) or
397- self .program_out_of_date (self .rustc_stamp (), self .date )):
398+ self .program_out_of_date (self .rustc_stamp (), self .date + str ( self . rustc_commit ) )):
398399 if os .path .exists (self .bin_root ()):
399400 shutil .rmtree (self .bin_root ())
401+ download_rustc = self .rustc_commit is not None
400402 tarball_suffix = '.tar.xz' if support_xz () else '.tar.gz'
401403 filename = "rust-std-{}-{}{}" .format (
402404 rustc_channel , self .build , tarball_suffix )
403405 pattern = "rust-std-{}" .format (self .build )
404- self ._download_stage0_helper (filename , pattern , tarball_suffix )
406+ self ._download_component_helper (filename , pattern , tarball_suffix , download_rustc )
405407 filename = "rustc-{}-{}{}" .format (rustc_channel , self .build ,
406408 tarball_suffix )
407- self ._download_stage0_helper (filename , "rustc" , tarball_suffix )
409+ self ._download_component_helper (filename , "rustc" , tarball_suffix , download_rustc )
408410 filename = "cargo-{}-{}{}" .format (rustc_channel , self .build ,
409411 tarball_suffix )
410- self ._download_stage0_helper (filename , "cargo" , tarball_suffix )
412+ self ._download_component_helper (filename , "cargo" , tarball_suffix )
413+ if self .rustc_commit is not None :
414+ filename = "rustc-dev-{}-{}{}" .format (rustc_channel , self .build , tarball_suffix )
415+ self ._download_component_helper (
416+ filename , "rustc-dev" , tarball_suffix , download_rustc
417+ )
418+
411419 self .fix_bin_or_dylib ("{}/bin/rustc" .format (self .bin_root ()))
412420 self .fix_bin_or_dylib ("{}/bin/rustdoc" .format (self .bin_root ()))
413421 self .fix_bin_or_dylib ("{}/bin/cargo" .format (self .bin_root ()))
@@ -416,7 +424,7 @@ def download_stage0(self):
416424 if lib .endswith (".so" ):
417425 self .fix_bin_or_dylib (os .path .join (lib_dir , lib ), rpath_libz = True )
418426 with output (self .rustc_stamp ()) as rust_stamp :
419- rust_stamp .write (self .date )
427+ rust_stamp .write (self .date + str ( self . rustc_commit ) )
420428
421429 if self .rustfmt () and self .rustfmt ().startswith (self .bin_root ()) and (
422430 not os .path .exists (self .rustfmt ())
@@ -426,7 +434,9 @@ def download_stage0(self):
426434 tarball_suffix = '.tar.xz' if support_xz () else '.tar.gz'
427435 [channel , date ] = rustfmt_channel .split ('-' , 1 )
428436 filename = "rustfmt-{}-{}{}" .format (channel , self .build , tarball_suffix )
429- self ._download_stage0_helper (filename , "rustfmt-preview" , tarball_suffix , date )
437+ self ._download_component_helper (
438+ filename , "rustfmt-preview" , tarball_suffix , key = date
439+ )
430440 self .fix_bin_or_dylib ("{}/bin/rustfmt" .format (self .bin_root ()))
431441 self .fix_bin_or_dylib ("{}/bin/cargo-fmt" .format (self .bin_root ()))
432442 with output (self .rustfmt_stamp ()) as rustfmt_stamp :
@@ -482,18 +492,27 @@ def downloading_llvm(self):
482492 return opt == "true" \
483493 or (opt == "if-available" and self .build in supported_platforms )
484494
485- def _download_stage0_helper (self , filename , pattern , tarball_suffix , date = None ):
486- if date is None :
487- date = self .date
495+ def _download_component_helper (
496+ self , filename , pattern , tarball_suffix , download_rustc = False , key = None
497+ ):
498+ if key is None :
499+ if download_rustc :
500+ key = self .rustc_commit
501+ else :
502+ key = self .date
488503 cache_dst = os .path .join (self .build_dir , "cache" )
489- rustc_cache = os .path .join (cache_dst , date )
504+ rustc_cache = os .path .join (cache_dst , key )
490505 if not os .path .exists (rustc_cache ):
491506 os .makedirs (rustc_cache )
492507
493- url = "{}/dist/{}" .format (self ._download_url , date )
508+ if download_rustc :
509+ url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}" .format (self .rustc_commit )
510+ else :
511+ url = "{}/dist/{}" .format (self ._download_url , key )
494512 tarball = os .path .join (rustc_cache , filename )
495513 if not os .path .exists (tarball ):
496- get ("{}/{}" .format (url , filename ), tarball , verbose = self .verbose )
514+ do_verify = not download_rustc
515+ get ("{}/{}" .format (url , filename ), tarball , verbose = self .verbose , do_verify = do_verify )
497516 unpack (tarball , tarball_suffix , self .bin_root (), match = pattern , verbose = self .verbose )
498517
499518 def _download_ci_llvm (self , llvm_sha , llvm_assertions ):
@@ -613,6 +632,46 @@ def fix_bin_or_dylib(self, fname, rpath_libz=False):
613632 print ("warning: failed to call patchelf:" , reason )
614633 return
615634
635+ # Return the stage1 compiler to download, if any.
636+ def maybe_download_rustc (self ):
637+ # If `download-rustc` is not set, default to rebuilding.
638+ if self .get_toml ("download-rustc" , section = "rust" ) != "true" :
639+ return None
640+ # Look for a version to compare to based on the current commit.
641+ # There are a few different cases to handle.
642+ # 1. This commit is a fast-forward from master: `master - * - * - HEAD`
643+ # 2. This commit and master have diverged:
644+ # ```
645+ # Y - * - HEAD
646+ # /
647+ # X - * - master
648+ # ```
649+ # In this case, we should compare to `X`.
650+ # 3. `master` and `HEAD` are radically different (>100 commits, or similar). This probably
651+ # means that `master` does *not* correspond to the version we want to compare to, e.g. a
652+ # fork. Instead, we want to compare to `rust-lang/rust:master`, which this has to share a
653+ # recent merge base with.
654+
655+ # Find which remote corresponds to `rust-lang/rust`.
656+ remotes = subprocess .check_output (["git" , "remote" , "-v" ], universal_newlines = True )
657+ # e.g. `origin https://github.com//rust-lang/rust (fetch)`
658+ rust_lang_remote = next (line for line in remotes .splitlines () if "rust-lang/rust" in line )
659+ rust_lang_remote = rust_lang_remote .split ()[0 ]
660+
661+ # Find which commit to compare to
662+ merge_base = ["git" , "merge-base" , "HEAD" , "{}/master" .format (rust_lang_remote )]
663+ commit = subprocess .check_output (merge_base , universal_newlines = True ).strip ()
664+
665+ # Warn if there were changes to the compiler since the ancestor commit.
666+ rev_parse = ["git" , "rev-parse" , "--show-toplevel" ]
667+ top_level = subprocess .check_output (rev_parse , universal_newlines = True ).strip ()
668+ compiler = "{}/compiler/" .format (top_level )
669+ status = subprocess .call (["git" , "diff-index" , "--quiet" , commit , "--" , compiler ])
670+ if status != 0 :
671+ print ("warning: `download-rustc` is enabled, but there are changes to compiler/" )
672+
673+ return commit
674+
616675 def rustc_stamp (self ):
617676 """Return the path for .rustc-stamp
618677
@@ -1090,6 +1149,13 @@ def bootstrap(help_triggered):
10901149 build .update_submodules ()
10911150
10921151 # Fetch/build the bootstrap
1152+ build .rustc_commit = build .maybe_download_rustc ()
1153+ if build .rustc_commit is not None :
1154+ if build .verbose :
1155+ commit = build .rustc_commit
1156+ print ("using downloaded stage1 artifacts from CI (commit {})" .format (commit ))
1157+ # FIXME: support downloading artifacts from the beta channel
1158+ build .rustc_channel = "nightly"
10931159 build .download_stage0 ()
10941160 sys .stdout .flush ()
10951161 build .ensure_vendored ()
0 commit comments