about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoshua Nelson <jnelson@cloudflare.com>2022-09-14 18:19:28 -0500
committerJoshua Nelson <jnelson@cloudflare.com>2022-09-14 18:27:04 -0500
commit55c040e52964e4ad81b4b2af3c91068a80b92f11 (patch)
tree9e5a712c17665309287d6d387203af6517e53724
parent63f6289db2d90be05acdf615f8039383c52c4ddc (diff)
downloadrust-55c040e52964e4ad81b4b2af3c91068a80b92f11.tar.gz
rust-55c040e52964e4ad81b4b2af3c91068a80b92f11.zip
Make it possible to run bootstrap on a different machine than the one it was built
- Default to trying git rev-parse for the root directory

  CARGO_MANIFEST_DIR is a path on the build machine, not the running machine.
  Don't require this to succeed, to allow building from a tarball; in that case fall back to CARGO_MANIFEST_DIR.

- Set `initial_rustc` to a path based on the path of the running executable, not CARGO_MANIFEST_DIR.

  We only reset `initial_rustc` if we're sure this isn't the working tree bootstrap was originally built in,
  since I'm paranoid that setting this in other cases will cause things to break;
  it's not clear to me when $RUSTC differs from `build/$TARGET/stage0/bin/rustc` (maybe never? but better to be sure).

  Instead, only set this when
  a) We are not using a custom rustc. If someone has specified a custom rustc we should respect their wishes.
  b) We are in a checkout of rust-lang/rust other than the one bootstrap was built in.
-rw-r--r--src/bootstrap/config.rs48
1 files changed, 44 insertions, 4 deletions
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 7c062460c4f..b8e776485e6 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -772,21 +772,20 @@ impl Config {
 
         // set by build.rs
         config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
+
         let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
         // Undo `src/bootstrap`
         config.src = manifest_dir.parent().unwrap().parent().unwrap().to_owned();
         config.out = PathBuf::from("build");
 
-        config.initial_cargo = PathBuf::from(env!("CARGO"));
-        config.initial_rustc = PathBuf::from(env!("RUSTC"));
-
         config
     }
 
     pub fn parse(args: &[String]) -> Config {
         let flags = Flags::parse(&args);
-
         let mut config = Config::default_opts();
+
+        // Set flags.
         config.exclude = flags.exclude.into_iter().map(|path| TaskPath::parse(path)).collect();
         config.include_default_paths = flags.include_default_paths;
         config.rustc_error_format = flags.rustc_error_format;
@@ -805,7 +804,41 @@ impl Config {
         config.llvm_profile_use = flags.llvm_profile_use;
         config.llvm_profile_generate = flags.llvm_profile_generate;
 
+        // Infer the rest of the configuration.
+
+        // Infer the source directory. This is non-trivial because we want to support a downloaded bootstrap binary,
+        // running on a completely machine from where it was compiled.
+        let mut cmd = Command::new("git");
+        // NOTE: we cannot support running from outside the repository because the only path we have available
+        // is set at compile time, which can be wrong if bootstrap was downloaded from source.
+        // We still support running outside the repository if we find we aren't in a git directory.
+        cmd.arg("rev-parse").arg("--show-toplevel");
+        // Discard stderr because we expect this to fail when building from a tarball.
+        let output = cmd
+            .stderr(std::process::Stdio::null())
+            .output()
+            .ok()
+            .and_then(|output| if output.status.success() { Some(output) } else { None });
+        if let Some(output) = output {
+            let git_root = String::from_utf8(output.stdout).unwrap();
+            config.src = PathBuf::from(git_root.trim().to_owned())
+        } else {
+            // We're building from a tarball, not git sources.
+            // We don't support pre-downloaded bootstrap in this case.
+        }
+
+        if cfg!(test) {
+            // Use the build directory of the original x.py invocation, so that we can set `initial_rustc` properly.
+            config.out = Path::new(
+                &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"),
+            )
+            .parent()
+            .unwrap()
+            .to_path_buf();
+        }
+
         let stage0_json = t!(std::fs::read(&config.src.join("src").join("stage0.json")));
+
         config.stage0_metadata = t!(serde_json::from_slice::<Stage0Metadata>(&stage0_json));
 
         #[cfg(test)]
@@ -860,6 +893,7 @@ impl Config {
         config.config = toml_path;
 
         let build = toml.build.unwrap_or_default();
+        let has_custom_rustc = build.rustc.is_some();
 
         set(&mut config.initial_rustc, build.rustc.map(PathBuf::from));
         set(&mut config.out, flags.build_dir.or_else(|| build.build_dir.map(PathBuf::from)));
@@ -870,6 +904,12 @@ impl Config {
             config.out = crate::util::absolute(&config.out);
         }
 
+        if !has_custom_rustc && !config.initial_rustc.starts_with(&config.out) {
+            config.initial_rustc = config.out.join(config.build.triple).join("stage0/bin/rustc");
+            config.initial_cargo = config.out.join(config.build.triple).join("stage0/bin/cargo");
+        }
+
+        // NOTE: it's important this comes *after* we set `initial_rustc` just above.
         if config.dry_run {
             let dir = config.out.join("tmp-dry-run");
             t!(fs::create_dir_all(&dir));