Skip to content

Commit 86dde9b

Browse files
authored
Auto merge of #36062 - japaric:smarter-submodules, r=alexcrichton
rustbuild: smarter `git submodule`-ing With this commit, if one bootstraps rust against system llvm then the src/llvm submodule is not updated/checked-out. This saves considerable network bandwith when starting from a fresh clone of rust-lang/rust as the llvm submodule is never cloned. cc #30107 r? @alexcrichton cc @petevine ~~We could also avoid updating the jemalloc submodule if --disable-jemalloc is used. It just hasn't been implemented.~~ Done This probably doesn't handle "recursive" submodules correctly but I think we don't have any of those right now. I'm still testing a bootstrap but already confirmed that the llvm submodule doesn't get updated when `--llvm-root` is passed to `configure`.
2 parents acd3f79 + 4b5007a commit 86dde9b

File tree

1 file changed

+79
-11
lines changed

1 file changed

+79
-11
lines changed

src/bootstrap/lib.rs

+79-11
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use std::cell::RefCell;
3232
use std::collections::HashMap;
3333
use std::env;
3434
use std::fs::{self, File};
35-
use std::path::{PathBuf, Path};
35+
use std::path::{Component, PathBuf, Path};
3636
use std::process::Command;
3737

3838
use build_helper::{run_silent, output};
@@ -477,12 +477,32 @@ impl Build {
477477
/// This will detect if any submodules are out of date an run the necessary
478478
/// commands to sync them all with upstream.
479479
fn update_submodules(&self) {
480+
struct Submodule<'a> {
481+
path: &'a Path,
482+
state: State,
483+
}
484+
485+
enum State {
486+
// The submodule may have staged/unstaged changes
487+
MaybeDirty,
488+
// Or could be initialized but never updated
489+
NotInitialized,
490+
// The submodule, itself, has extra commits but those changes haven't been commited to
491+
// the (outer) git repository
492+
OutOfSync,
493+
}
494+
480495
if !self.config.submodules {
481496
return
482497
}
483498
if fs::metadata(self.src.join(".git")).is_err() {
484499
return
485500
}
501+
let git = || {
502+
let mut cmd = Command::new("git");
503+
cmd.current_dir(&self.src);
504+
return cmd
505+
};
486506
let git_submodule = || {
487507
let mut cmd = Command::new("git");
488508
cmd.current_dir(&self.src).arg("submodule");
@@ -494,19 +514,67 @@ impl Build {
494514
// of detecting whether we need to run all the submodule commands
495515
// below.
496516
let out = output(git_submodule().arg("status"));
497-
if !out.lines().any(|l| l.starts_with("+") || l.starts_with("-")) {
498-
return
517+
let mut submodules = vec![];
518+
for line in out.lines() {
519+
// NOTE `git submodule status` output looks like this:
520+
//
521+
// -5066b7dcab7e700844b0e2ba71b8af9dc627a59b src/liblibc
522+
// +b37ef24aa82d2be3a3cc0fe89bf82292f4ca181c src/compiler-rt (remotes/origin/..)
523+
// e058ca661692a8d01f8cf9d35939dfe3105ce968 src/jemalloc (3.6.0-533-ge058ca6)
524+
//
525+
// The first character can be '-', '+' or ' ' and denotes the `State` of the submodule
526+
// Right next to this character is the SHA-1 of the submodule HEAD
527+
// And after that comes the path to the submodule
528+
let path = Path::new(line[1..].split(' ').skip(1).next().unwrap());
529+
let state = if line.starts_with('-') {
530+
State::NotInitialized
531+
} else if line.starts_with('*') {
532+
State::OutOfSync
533+
} else if line.starts_with(' ') {
534+
State::MaybeDirty
535+
} else {
536+
panic!("unexpected git submodule state: {:?}", line.chars().next());
537+
};
538+
539+
submodules.push(Submodule { path: path, state: state })
499540
}
500541

501542
self.run(git_submodule().arg("sync"));
502-
self.run(git_submodule().arg("init"));
503-
self.run(git_submodule().arg("update"));
504-
self.run(git_submodule().arg("update").arg("--recursive"));
505-
self.run(git_submodule().arg("status").arg("--recursive"));
506-
self.run(git_submodule().arg("foreach").arg("--recursive")
507-
.arg("git").arg("clean").arg("-fdx"));
508-
self.run(git_submodule().arg("foreach").arg("--recursive")
509-
.arg("git").arg("checkout").arg("."));
543+
544+
for submodule in submodules {
545+
// If using llvm-root then don't touch the llvm submodule.
546+
if submodule.path.components().any(|c| c == Component::Normal("llvm".as_ref())) &&
547+
self.config.target_config.get(&self.config.build)
548+
.and_then(|c| c.llvm_config.as_ref()).is_some()
549+
{
550+
continue
551+
}
552+
553+
if submodule.path.components().any(|c| c == Component::Normal("jemalloc".as_ref())) &&
554+
!self.config.use_jemalloc
555+
{
556+
continue
557+
}
558+
559+
match submodule.state {
560+
State::MaybeDirty => {
561+
// drop staged changes
562+
self.run(git().arg("-C").arg(submodule.path).args(&["reset", "--hard"]));
563+
// drops unstaged changes
564+
self.run(git().arg("-C").arg(submodule.path).args(&["clean", "-fdx"]));
565+
},
566+
State::NotInitialized => {
567+
self.run(git_submodule().arg("init").arg(submodule.path));
568+
self.run(git_submodule().arg("update").arg(submodule.path));
569+
},
570+
State::OutOfSync => {
571+
// drops submodule commits that weren't reported to the (outer) git repository
572+
self.run(git_submodule().arg("update").arg(submodule.path));
573+
self.run(git().arg("-C").arg(submodule.path).args(&["reset", "--hard"]));
574+
self.run(git().arg("-C").arg(submodule.path).args(&["clean", "-fdx"]));
575+
},
576+
}
577+
}
510578
}
511579

512580
/// Clear out `dir` if `input` is newer.

0 commit comments

Comments
 (0)