Skip to content

Commit 295ee5e

Browse files
committed
Implement downloading GCC from CI
1 parent 24a20f8 commit 295ee5e

File tree

2 files changed

+108
-20
lines changed

2 files changed

+108
-20
lines changed

src/bootstrap/src/core/build_steps/gcc.rs

+80-20
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use std::path::{Path, PathBuf};
1313
use std::sync::OnceLock;
1414

1515
use build_helper::ci::CiEnv;
16+
use build_helper::git::get_closest_merge_commit;
1617

1718
use crate::Config;
1819
use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
19-
use crate::core::config::TargetSelection;
20+
use crate::core::config::{GccCiMode, TargetSelection};
2021
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
2122
use crate::utils::exec::command;
2223
use crate::utils::helpers::{self, t};
@@ -89,17 +90,39 @@ pub enum GccBuildStatus {
8990
ShouldBuild(Meta),
9091
}
9192

92-
/// This returns whether we've already previously built GCC.
93+
/// Tries to download GCC from CI if it is enabled and GCC artifacts
94+
/// are available for the given target.
95+
/// Returns a path to the libgccjit.so file.
96+
fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<PathBuf> {
97+
// Try to download GCC from CI if configured and available
98+
if !matches!(builder.config.gcc_ci_mode, GccCiMode::DownloadFromCi) {
99+
return None;
100+
}
101+
if target != "x86_64-unknown-linux-gnu" {
102+
eprintln!("GCC CI download is only available for the `x86_64-unknown-linux-gnu` target");
103+
return None;
104+
}
105+
let sha =
106+
detect_gcc_sha(&builder.config, builder.config.rust_info.is_managed_git_subrepository());
107+
let root = ci_gcc_root(&builder.config);
108+
let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&sha);
109+
if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() {
110+
builder.config.download_ci_gcc(&sha, &root);
111+
t!(gcc_stamp.write());
112+
}
113+
// FIXME: put libgccjit.so into a lib directory in dist::Gcc
114+
Some(root.join("libgccjit.so"))
115+
}
116+
117+
/// This returns information about whether GCC should be built or if it's already built.
118+
/// It transparently handles downloading GCC from CI if needed.
93119
///
94120
/// It's used to avoid busting caches during x.py check -- if we've already built
95121
/// GCC, it's fine for us to not try to avoid doing so.
96122
pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
97-
// Initialize the gcc submodule if not initialized already.
98-
builder.config.update_submodule("src/gcc");
99-
100-
let root = builder.src.join("src/gcc");
101-
let out_dir = builder.gcc_out(target).join("build");
102-
let install_dir = builder.gcc_out(target).join("install");
123+
if let Some(path) = try_download_gcc(builder, target) {
124+
return GccBuildStatus::AlreadyBuilt(path);
125+
}
103126

104127
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
105128
let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
@@ -110,6 +133,13 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G
110133
)
111134
});
112135

136+
// Initialize the gcc submodule if not initialized already.
137+
builder.config.update_submodule("src/gcc");
138+
139+
let root = builder.src.join("src/gcc");
140+
let out_dir = builder.gcc_out(target).join("build");
141+
let install_dir = builder.gcc_out(target).join("install");
142+
113143
let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash);
114144

115145
if stamp.is_up_to_date() {
@@ -142,7 +172,7 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
142172
install_dir.join("lib/libgccjit.so")
143173
}
144174

145-
fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
175+
fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
146176
let Meta { stamp: _, out_dir, install_dir, root } = metadata;
147177

148178
t!(fs::create_dir_all(out_dir));
@@ -202,21 +232,51 @@ fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
202232
}
203233
configure_cmd.run(builder);
204234

205-
command("make")
206-
.current_dir(&out_dir)
207-
.arg("--silent")
208-
.arg(format!("-j{}", builder.jobs()))
209-
.run_capture_stdout(builder);
210-
command("make")
211-
.current_dir(&out_dir)
212-
.arg("--silent")
213-
.arg("install")
214-
.run_capture_stdout(builder);
215-
}
235+
command("make")
236+
.current_dir(&out_dir)
237+
.arg("--silent")
238+
.arg(format!("-j{}", builder.jobs()))
239+
.run_capture_stdout(builder);
240+
command("make")
241+
.current_dir(&out_dir)
242+
.arg("--silent")
243+
.arg("install")
244+
.run_capture_stdout(builder);
216245
}
217246

218247
/// Configures a Cargo invocation so that it can build the GCC codegen backend.
219248
pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) {
220249
// Add the path to libgccjit.so to the linker search paths.
221250
cargo.rustflag(&format!("-L{}", gcc.libgccjit.parent().unwrap().to_str().unwrap()));
222251
}
252+
253+
/// The absolute path to the downloaded GCC artifacts.
254+
fn ci_gcc_root(config: &Config) -> PathBuf {
255+
config.out.join(config.build).join("ci-gcc")
256+
}
257+
258+
/// This retrieves the GCC sha we *want* to use, according to git history.
259+
fn detect_gcc_sha(config: &Config, is_git: bool) -> String {
260+
let gcc_sha = if is_git {
261+
get_closest_merge_commit(
262+
Some(&config.src),
263+
&config.git_config(),
264+
&[config.src.join("src/gcc"), config.src.join("src/bootstrap/download-ci-gcc-stamp")],
265+
)
266+
.unwrap()
267+
} else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
268+
info.sha.trim().to_owned()
269+
} else {
270+
"".to_owned()
271+
};
272+
273+
if gcc_sha.is_empty() {
274+
eprintln!("error: could not find commit hash for downloading GCC");
275+
eprintln!("HELP: maybe your repository history is too shallow?");
276+
eprintln!("HELP: consider disabling `download-ci-gcc`");
277+
eprintln!("HELP: or fetch enough history to include one upstream commit");
278+
panic!();
279+
}
280+
281+
gcc_sha
282+
}

src/bootstrap/src/core/download.rs

+28
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,34 @@ download-rustc = false
826826
let llvm_root = self.ci_llvm_root();
827827
self.unpack(&tarball, &llvm_root, "rust-dev");
828828
}
829+
830+
pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) {
831+
let cache_prefix = format!("gcc-{gcc_sha}");
832+
let cache_dst =
833+
self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
834+
835+
let gcc_cache = cache_dst.join(cache_prefix);
836+
if !gcc_cache.exists() {
837+
t!(fs::create_dir_all(&gcc_cache));
838+
}
839+
let base = &self.stage0_metadata.config.artifacts_server;
840+
let filename = format!("gcc-nightly-{}.tar.xz", self.build.triple);
841+
let tarball = gcc_cache.join(&filename);
842+
if !tarball.exists() {
843+
let help_on_error = "ERROR: failed to download gcc from ci
844+
845+
HELP: There could be two reasons behind this:
846+
1) The host triple is not supported for `download-ci-gcc`.
847+
2) Old builds get deleted after a certain time.
848+
HELP: In either case, disable `download-ci-gcc` in your config.toml:
849+
850+
[gcc]
851+
download-ci-gcc = false
852+
";
853+
self.download_file(&format!("{base}/{gcc_sha}/{filename}"), &tarball, help_on_error);
854+
}
855+
self.unpack(&tarball, root_dir, "gcc");
856+
}
829857
}
830858

831859
fn path_is_dylib(path: &Path) -> bool {

0 commit comments

Comments
 (0)