Implement downloading GCC from CI

This commit is contained in:
Jakub Beránek 2025-03-05 11:38:05 +01:00
parent 3de10b0756
commit bc6302ca6d
2 changed files with 104 additions and 20 deletions

View file

@ -13,10 +13,11 @@ use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use build_helper::ci::CiEnv;
use build_helper::git::get_closest_merge_commit;
use crate::Config;
use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
use crate::core::config::TargetSelection;
use crate::core::config::{GccCiMode, TargetSelection};
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
use crate::utils::exec::command;
use crate::utils::helpers::{self, t};
@ -89,17 +90,39 @@ pub enum GccBuildStatus {
ShouldBuild(Meta),
}
/// This returns whether we've already previously built GCC.
/// Tries to download GCC from CI if it is enabled and GCC artifacts
/// are available for the given target.
/// Returns a path to the libgccjit.so file.
fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<PathBuf> {
// Try to download GCC from CI if configured and available
if !matches!(builder.config.gcc_ci_mode, GccCiMode::DownloadFromCi) {
return None;
}
if target != "x86_64-unknown-linux-gnu" {
eprintln!("GCC CI download is only available for the `x86_64-unknown-linux-gnu` target");
return None;
}
let sha =
detect_gcc_sha(&builder.config, builder.config.rust_info.is_managed_git_subrepository());
let root = ci_gcc_root(&builder.config);
let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&sha);
if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() {
builder.config.download_ci_gcc(&sha, &root);
t!(gcc_stamp.write());
}
// FIXME: put libgccjit.so into a lib directory in dist::Gcc
Some(root.join("libgccjit.so"))
}
/// This returns information about whether GCC should be built or if it's already built.
/// It transparently handles downloading GCC from CI if needed.
///
/// It's used to avoid busting caches during x.py check -- if we've already built
/// GCC, it's fine for us to not try to avoid doing so.
pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
// Initialize the gcc submodule if not initialized already.
builder.config.update_submodule("src/gcc");
let root = builder.src.join("src/gcc");
let out_dir = builder.gcc_out(target).join("build");
let install_dir = builder.gcc_out(target).join("install");
if let Some(path) = try_download_gcc(builder, target) {
return GccBuildStatus::AlreadyBuilt(path);
}
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
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
)
});
// Initialize the gcc submodule if not initialized already.
builder.config.update_submodule("src/gcc");
let root = builder.src.join("src/gcc");
let out_dir = builder.gcc_out(target).join("build");
let install_dir = builder.gcc_out(target).join("install");
let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash);
if stamp.is_up_to_date() {
@ -142,7 +172,7 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
install_dir.join("lib/libgccjit.so")
}
fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
let Meta { stamp: _, out_dir, install_dir, root } = metadata;
t!(fs::create_dir_all(out_dir));
@ -202,17 +232,12 @@ fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
}
configure_cmd.run(builder);
command("make")
.current_dir(&out_dir)
.arg("--silent")
.arg(format!("-j{}", builder.jobs()))
.run_capture_stdout(builder);
command("make")
.current_dir(&out_dir)
.arg("--silent")
.arg("install")
.run_capture_stdout(builder);
}
command("make")
.current_dir(out_dir)
.arg("--silent")
.arg(format!("-j{}", builder.jobs()))
.run_capture_stdout(builder);
command("make").current_dir(out_dir).arg("--silent").arg("install").run_capture_stdout(builder);
}
/// Configures a Cargo invocation so that it can build the GCC codegen backend.
@ -220,3 +245,34 @@ pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) {
// Add the path to libgccjit.so to the linker search paths.
cargo.rustflag(&format!("-L{}", gcc.libgccjit.parent().unwrap().to_str().unwrap()));
}
/// The absolute path to the downloaded GCC artifacts.
fn ci_gcc_root(config: &Config) -> PathBuf {
config.out.join(config.build).join("ci-gcc")
}
/// This retrieves the GCC sha we *want* to use, according to git history.
fn detect_gcc_sha(config: &Config, is_git: bool) -> String {
let gcc_sha = if is_git {
get_closest_merge_commit(
Some(&config.src),
&config.git_config(),
&[config.src.join("src/gcc"), config.src.join("src/bootstrap/download-ci-gcc-stamp")],
)
.unwrap()
} else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
info.sha.trim().to_owned()
} else {
"".to_owned()
};
if gcc_sha.is_empty() {
eprintln!("error: could not find commit hash for downloading GCC");
eprintln!("HELP: maybe your repository history is too shallow?");
eprintln!("HELP: consider disabling `download-ci-gcc`");
eprintln!("HELP: or fetch enough history to include one upstream commit");
panic!();
}
gcc_sha
}

View file

@ -826,6 +826,34 @@ download-rustc = false
let llvm_root = self.ci_llvm_root();
self.unpack(&tarball, &llvm_root, "rust-dev");
}
pub fn download_ci_gcc(&self, gcc_sha: &str, root_dir: &Path) {
let cache_prefix = format!("gcc-{gcc_sha}");
let cache_dst =
self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache"));
let gcc_cache = cache_dst.join(cache_prefix);
if !gcc_cache.exists() {
t!(fs::create_dir_all(&gcc_cache));
}
let base = &self.stage0_metadata.config.artifacts_server;
let filename = format!("gcc-nightly-{}.tar.xz", self.build.triple);
let tarball = gcc_cache.join(&filename);
if !tarball.exists() {
let help_on_error = "ERROR: failed to download gcc from ci
HELP: There could be two reasons behind this:
1) The host triple is not supported for `download-ci-gcc`.
2) Old builds get deleted after a certain time.
HELP: In either case, disable `download-ci-gcc` in your config.toml:
[gcc]
download-ci-gcc = false
";
self.download_file(&format!("{base}/{gcc_sha}/{filename}"), &tarball, help_on_error);
}
self.unpack(&tarball, root_dir, "gcc");
}
}
fn path_is_dylib(path: &Path) -> bool {