diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 3924a6d714e3b..a9db0377a507b 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -545,23 +545,28 @@ impl Build { .args(["--get-regexp", "path"]) .run_capture(self) .stdout(); - for line in output.lines() { + std::thread::scope(|s| { // Look for `submodule.$name.path = $path` // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer` - let submodule = line.split_once(' ').unwrap().1; - self.update_existing_submodule(submodule); - } + for line in output.lines() { + let submodule = line.split_once(' ').unwrap().1; + let config = self.config.clone(); + s.spawn(move || { + Self::update_existing_submodule(&config, submodule); + }); + } + }); } /// Updates the given submodule only if it's initialized already; nothing happens otherwise. - pub fn update_existing_submodule(&self, submodule: &str) { + pub fn update_existing_submodule(config: &Config, submodule: &str) { // Avoid running git when there isn't a git checkout. - if !self.config.submodules() { + if !config.submodules() { return; } if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() { - self.config.update_submodule(submodule); + config.update_submodule(submodule); } } diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs index c361abb9c9e13..4a9ecc7a4f8a1 100644 --- a/src/bootstrap/src/utils/channel.rs +++ b/src/bootstrap/src/utils/channel.rs @@ -10,7 +10,7 @@ use std::path::Path; use super::helpers; use crate::Build; -use crate::utils::helpers::{output, t}; +use crate::utils::helpers::{start_process, t}; #[derive(Clone, Default)] pub enum GitInfo { @@ -56,7 +56,7 @@ impl GitInfo { } // Ok, let's scrape some info - let ver_date = output( + let ver_date = start_process( helpers::git(Some(dir)) .arg("log") .arg("-1") @@ -65,14 +65,14 @@ impl GitInfo { .as_command_mut(), ); let ver_hash = - output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut()); - let short_ver_hash = output( + start_process(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut()); + let short_ver_hash = start_process( helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").as_command_mut(), ); GitInfo::Present(Some(Info { - commit_date: ver_date.trim().to_string(), - sha: ver_hash.trim().to_string(), - short_sha: short_ver_hash.trim().to_string(), + commit_date: ver_date().trim().to_string(), + sha: ver_hash().trim().to_string(), + short_sha: short_ver_hash().trim().to_string(), })) } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 2519ace92b9e0..7162007e9f0f7 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -288,6 +288,33 @@ pub fn output(cmd: &mut Command) -> String { String::from_utf8(output.stdout).unwrap() } +/// Spawn a process and return a closure that will wait for the process +/// to finish and then return its output. This allows the spawned process +/// to do work without immediately blocking bootstrap. +#[track_caller] +pub fn start_process(cmd: &mut Command) -> impl FnOnce() -> String { + let child = match cmd.stderr(Stdio::inherit()).stdout(Stdio::piped()).spawn() { + Ok(child) => child, + Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")), + }; + + let command = format!("{:?}", cmd); + + move || { + let output = child.wait_with_output().unwrap(); + + if !output.status.success() { + panic!( + "command did not execute successfully: {}\n\ + expected success, got: {}", + command, output.status + ); + } + + String::from_utf8(output.stdout).unwrap() + } +} + /// Returns the last-modified time for `path`, or zero if it doesn't exist. pub fn mtime(path: &Path) -> SystemTime { fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)