Skip to content

Commit 9c3a2a5

Browse files
committed
Auto merge of #81601 - jyn514:llvm-on-demand, r=Mark-Simulacrum
Move llvm submodule updates to rustbuild This enables better caching, since LLVM is only updated when needed, not whenever x.py is run. Before, bootstrap.py had to use heuristics to guess if LLVM would be needed, and updated the module more often than necessary as a result. This syncs the LLVM submodule only just before building the compiler, so people working on the standard library never have to worry about it. Example output: ``` Copying stage0 std from stage0 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu / x86_64-unknown-linux-gnu) Updating submodule src/llvm-project Submodule 'src/llvm-project' (https://github.com/rust-lang/llvm-project.git) registered for path 'src/llvm-project' Submodule path 'src/llvm-project': checked out 'f9a8d70b6e0365ac2172ca6b7f1de0341297458d' ``` Implements #76653 (comment). This could be easily extended to other submodules, like `rust-by-example` and `rustc-dev-guide`, which aren't needed for cargo's workspace resolution.
2 parents e4ca166 + 0be4046 commit 9c3a2a5

File tree

4 files changed

+98
-13
lines changed

4 files changed

+98
-13
lines changed

src/bootstrap/bootstrap.py

+4-12
Original file line numberDiff line numberDiff line change
@@ -991,28 +991,20 @@ def update_submodules(self):
991991
).decode(default_encoding).splitlines()]
992992
filtered_submodules = []
993993
submodules_names = []
994-
llvm_checked_out = os.path.exists(os.path.join(self.rust_root, "src/llvm-project/.git"))
995-
external_llvm_provided = self.get_toml('llvm-config') or self.downloading_llvm()
996-
llvm_needed = not self.get_toml('codegen-backends', 'rust') \
997-
or "llvm" in self.get_toml('codegen-backends', 'rust')
998994
for module in submodules:
995+
# This is handled by native::Llvm in rustbuild, not here
999996
if module.endswith("llvm-project"):
1000-
# Don't sync the llvm-project submodule if an external LLVM was
1001-
# provided, if we are downloading LLVM or if the LLVM backend is
1002-
# not being built. Also, if the submodule has been initialized
1003-
# already, sync it anyways so that it doesn't mess up contributor
1004-
# pull requests.
1005-
if external_llvm_provided or not llvm_needed:
1006-
if self.get_toml('lld') != 'true' and not llvm_checked_out:
1007-
continue
997+
continue
1008998
check = self.check_submodule(module, slow_submodules)
1009999
filtered_submodules.append((module, check))
10101000
submodules_names.append(module)
10111001
recorded = subprocess.Popen(["git", "ls-tree", "HEAD"] + submodules_names,
10121002
cwd=self.rust_root, stdout=subprocess.PIPE)
10131003
recorded = recorded.communicate()[0].decode(default_encoding).strip().splitlines()
1004+
# { filename: hash }
10141005
recorded_submodules = {}
10151006
for data in recorded:
1007+
# [mode, kind, hash, filename]
10161008
data = data.split()
10171009
recorded_submodules[data[3]] = data[2]
10181010
for module in filtered_submodules:

src/bootstrap/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,22 @@ impl Build {
472472
slice::from_ref(&self.build.triple)
473473
}
474474

475+
/// If the LLVM submodule has been initialized already, sync it unconditionally. This avoids
476+
/// contributors checking in a submodule change by accident.
477+
pub fn maybe_update_llvm_submodule(&self) {
478+
if self.in_tree_llvm_info.is_git() {
479+
native::update_llvm_submodule(self);
480+
}
481+
}
482+
475483
/// Executes the entire build, as configured by the flags and configuration.
476484
pub fn build(&mut self) {
477485
unsafe {
478486
job::setup(self);
479487
}
480488

489+
self.maybe_update_llvm_submodule();
490+
481491
if let Subcommand::Format { check, paths } = &self.config.cmd {
482492
return format::format(self, *check, &paths);
483493
}

src/bootstrap/native.rs

+83-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use build_helper::{output, t};
2121
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
2222
use crate::config::TargetSelection;
2323
use crate::util::{self, exe};
24-
use crate::GitRepo;
24+
use crate::{Build, GitRepo};
2525
use build_helper::up_to_date;
2626

2727
pub struct Meta {
@@ -91,6 +91,85 @@ pub fn prebuilt_llvm_config(
9191
Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
9292
}
9393

94+
// modified from `check_submodule` and `update_submodule` in bootstrap.py
95+
pub(crate) fn update_llvm_submodule(build: &Build) {
96+
let llvm_project = &Path::new("src").join("llvm-project");
97+
98+
fn dir_is_empty(dir: &Path) -> bool {
99+
t!(std::fs::read_dir(dir)).next().is_none()
100+
}
101+
102+
// NOTE: The check for the empty directory is here because when running x.py
103+
// the first time, the llvm submodule won't be checked out. Check it out
104+
// now so we can build it.
105+
if !build.in_tree_llvm_info.is_git() && !dir_is_empty(&build.config.src.join(llvm_project)) {
106+
return;
107+
}
108+
109+
// check_submodule
110+
let checked_out = if build.config.fast_submodules {
111+
Some(output(
112+
Command::new("git")
113+
.args(&["rev-parse", "HEAD"])
114+
.current_dir(build.config.src.join(llvm_project)),
115+
))
116+
} else {
117+
None
118+
};
119+
120+
// update_submodules
121+
let recorded = output(
122+
Command::new("git")
123+
.args(&["ls-tree", "HEAD"])
124+
.arg(llvm_project)
125+
.current_dir(&build.config.src),
126+
);
127+
let hash =
128+
recorded.split(' ').nth(2).unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
129+
130+
// update_submodule
131+
if let Some(llvm_hash) = checked_out {
132+
if hash == llvm_hash {
133+
// already checked out
134+
return;
135+
}
136+
}
137+
138+
println!("Updating submodule {}", llvm_project.display());
139+
build.run(
140+
Command::new("git")
141+
.args(&["submodule", "-q", "sync"])
142+
.arg(llvm_project)
143+
.current_dir(&build.config.src),
144+
);
145+
146+
// Try passing `--progress` to start, then run git again without if that fails.
147+
let update = |progress: bool| {
148+
let mut git = Command::new("git");
149+
git.args(&["submodule", "update", "--init", "--recursive"]);
150+
if progress {
151+
git.arg("--progress");
152+
}
153+
git.arg(llvm_project).current_dir(&build.config.src);
154+
git
155+
};
156+
// NOTE: doesn't use `try_run` because this shouldn't print an error if it fails.
157+
if !update(true).status().map_or(false, |status| status.success()) {
158+
build.run(&mut update(false));
159+
}
160+
161+
build.run(
162+
Command::new("git")
163+
.args(&["reset", "-q", "--hard"])
164+
.current_dir(build.config.src.join(llvm_project)),
165+
);
166+
build.run(
167+
Command::new("git")
168+
.args(&["clean", "-qdfx"])
169+
.current_dir(build.config.src.join(llvm_project)),
170+
);
171+
}
172+
94173
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
95174
pub struct Llvm {
96175
pub target: TargetSelection,
@@ -128,6 +207,9 @@ impl Step for Llvm {
128207
Err(m) => m,
129208
};
130209

210+
if !builder.config.dry_run {
211+
update_llvm_submodule(builder);
212+
}
131213
if builder.config.llvm_link_shared
132214
&& (target.contains("windows") || target.contains("apple-darwin"))
133215
{

src/build_helper/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub fn make(host: &str) -> PathBuf {
130130
}
131131
}
132132

133+
#[track_caller]
133134
pub fn output(cmd: &mut Command) -> String {
134135
let output = match cmd.stderr(Stdio::inherit()).output() {
135136
Ok(status) => status,

0 commit comments

Comments
 (0)