Skip to content

Commit fff2329

Browse files
Add libgccjit dist generation
1 parent c9fc1bb commit fff2329

File tree

5 files changed

+244
-7
lines changed

5 files changed

+244
-7
lines changed

src/bootstrap/src/core/build_steps/dist.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,6 +2332,7 @@ impl Step for RustDev {
23322332
tarball.permit_symlinks(true);
23332333

23342334
builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2335+
builder.ensure(crate::core::build_steps::gcc::Gcc { target });
23352336

23362337
// We want to package `lld` to use it with `download-ci-llvm`.
23372338
builder.ensure(crate::core::build_steps::llvm::Lld { target });
@@ -2365,6 +2366,10 @@ impl Step for RustDev {
23652366
// just broadly useful to be able to link against the bundled LLVM.
23662367
tarball.add_dir(builder.llvm_out(target).join("include"), "include");
23672368

2369+
tarball.add_dir(builder.gcc_out(target).join("install/lib/libgccjit.so"), "libgccjit.so");
2370+
tarball
2371+
.add_dir(builder.gcc_out(target).join("install/lib/libgccjit.so.0"), "libgccjit.so.0");
2372+
23682373
// Copy libLLVM.so to the target lib dir as well, so the RPATH like
23692374
// `$ORIGIN/../lib` can find it. It may also be used as a dependency
23702375
// of `rustc-dev` to support the inherited `-lLLVM` when using the
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
//! Compilation of native dependencies like LLVM.
2+
//!
3+
//! Native projects like LLVM unfortunately aren't suited just yet for
4+
//! compilation in build scripts that Cargo has. This is because the
5+
//! compilation takes a *very* long time but also because we don't want to
6+
//! compile LLVM 3 times as part of a normal bootstrap (we want it cached).
7+
//!
8+
//! LLVM and compiler-rt are essentially just wired up to everything else to
9+
//! ensure that they're always in place if needed.
10+
11+
use std::fs;
12+
use std::path::{Path, PathBuf};
13+
use std::process::Command;
14+
use std::sync::OnceLock;
15+
16+
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
17+
use crate::core::config::TargetSelection;
18+
use crate::utils::helpers::{self, t};
19+
use crate::{generate_smart_stamp_hash, Kind};
20+
21+
use super::llvm::HashStamp;
22+
23+
pub struct Meta {
24+
stamp: HashStamp,
25+
out_dir: PathBuf,
26+
install_dir: PathBuf,
27+
root: PathBuf,
28+
}
29+
30+
pub enum GccBuildStatus {
31+
AlreadyBuilt,
32+
ShouldBuild(Meta),
33+
}
34+
35+
/// This returns whether we've already previously built GCC.
36+
///
37+
/// It's used to avoid busting caches during x.py check -- if we've already built
38+
/// GCC, it's fine for us to not try to avoid doing so.
39+
pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
40+
// If we have gcc submodule initialized already, sync it.
41+
builder.update_existing_submodule(&Path::new("src").join("gcc"));
42+
43+
// FIXME (GuillaumeGomez): To be done once gccjit has been built in the CI.
44+
// builder.config.maybe_download_ci_gcc();
45+
46+
// Initialize the llvm submodule if not initialized already.
47+
builder.update_submodule(&Path::new("src").join("gcc"));
48+
49+
let root = "src/gcc";
50+
let out_dir = builder.gcc_out(target).join("build");
51+
let install_dir = builder.gcc_out(target).join("install");
52+
53+
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
54+
let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
55+
generate_smart_stamp_hash(
56+
&builder.config.src.join("src/llvm-project"),
57+
builder.in_tree_llvm_info.sha().unwrap_or_default(),
58+
)
59+
});
60+
61+
let stamp = out_dir.join("gcc-finished-building");
62+
let stamp = HashStamp::new(stamp, Some(smart_stamp_hash));
63+
64+
if stamp.is_done() {
65+
if stamp.hash.is_none() {
66+
builder.info(
67+
"Could not determine the GCC submodule commit hash. \
68+
Assuming that an GCC rebuild is not necessary.",
69+
);
70+
builder.info(&format!(
71+
"To force GCC to rebuild, remove the file `{}`",
72+
stamp.path.display()
73+
));
74+
}
75+
return GccBuildStatus::AlreadyBuilt;
76+
}
77+
78+
GccBuildStatus::ShouldBuild(Meta { stamp, out_dir, install_dir, root: root.into() })
79+
}
80+
81+
// FIXME (GuillaumeGomez): When gcc-ci-download option is added, uncomment this code.
82+
// /// This retrieves the GCC sha we *want* to use, according to git history.
83+
// pub(crate) fn detect_gcc_sha(config: &Config, is_git: bool) -> String {
84+
// let gcc_sha = if is_git {
85+
// // We proceed in 2 steps. First we get the closest commit that is actually upstream. Then we
86+
// // walk back further to the last bors merge commit that actually changed GCC. The first
87+
// // step will fail on CI because only the `auto` branch exists; we just fall back to `HEAD`
88+
// // in that case.
89+
// let closest_upstream = get_git_merge_base(&config.git_config(), Some(&config.src))
90+
// .unwrap_or_else(|_| "HEAD".into());
91+
// let mut rev_list = config.git();
92+
// rev_list.args(&[
93+
// PathBuf::from("rev-list"),
94+
// format!("--author={}", config.stage0_metadata.config.git_merge_commit_email).into(),
95+
// "-n1".into(),
96+
// "--first-parent".into(),
97+
// closest_upstream.into(),
98+
// "--".into(),
99+
// config.src.join("src/gcc"),
100+
// config.src.join("src/bootstrap/download-ci-gcc-stamp"),
101+
// // the GCC shared object file is named `gcc-12-rust-{version}-nightly`
102+
// config.src.join("src/version"),
103+
// ]);
104+
// output(&mut rev_list).trim().to_owned()
105+
// } else if let Some(info) = channel::read_commit_info_file(&config.src) {
106+
// info.sha.trim().to_owned()
107+
// } else {
108+
// "".to_owned()
109+
// };
110+
111+
// if gcc_sha.is_empty() {
112+
// eprintln!("error: could not find commit hash for downloading LLVM");
113+
// eprintln!("HELP: maybe your repository history is too shallow?");
114+
// eprintln!("HELP: consider disabling `download-ci-gcc`");
115+
// eprintln!("HELP: or fetch enough history to include one upstream commit");
116+
// panic!();
117+
// }
118+
119+
// gcc_sha
120+
// }
121+
122+
// /// Returns whether the CI-found GCC is currently usable.
123+
// ///
124+
// /// This checks both the build triple platform to confirm we're usable at all,
125+
// /// and then verifies if the current HEAD matches the detected GCC SHA head,
126+
// /// in which case GCC is indicated as not available.
127+
// pub(crate) fn is_ci_gcc_available(config: &Config, asserts: bool) -> bool {
128+
// // This is currently all tier 1 targets and tier 2 targets with host tools
129+
// // (since others may not have CI artifacts)
130+
// // https://doc.rust-lang.org/rustc/platform-support.html#tier-1
131+
// let supported_platforms = [
132+
// // tier 1
133+
// ("x86_64-unknown-linux-gnu", true),
134+
// ];
135+
136+
// if !supported_platforms.contains(&(&*config.build.triple, asserts))
137+
// && (asserts || !supported_platforms.contains(&(&*config.build.triple, true)))
138+
// {
139+
// return false;
140+
// }
141+
142+
// if is_ci_gcc_modified(config) {
143+
// eprintln!("Detected GCC as non-available: running in CI and modified GCC in this change");
144+
// return false;
145+
// }
146+
147+
// true
148+
// }
149+
150+
// /// Returns true if we're running in CI with modified GCC (and thus can't download it)
151+
// pub(crate) fn is_ci_gcc_modified(config: &Config) -> bool {
152+
// CiEnv::is_ci() && config.rust_info.is_managed_git_subrepository() && {
153+
// // We assume we have access to git, so it's okay to unconditionally pass
154+
// // `true` here.
155+
// let gcc_sha = detect_gcc_sha(config, true);
156+
// let head_sha = output(config.git().arg("rev-parse").arg("HEAD"));
157+
// let head_sha = head_sha.trim();
158+
// gcc_sha == head_sha
159+
// }
160+
// }
161+
162+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
163+
pub struct Gcc {
164+
pub target: TargetSelection,
165+
}
166+
167+
impl Step for Gcc {
168+
type Output = bool;
169+
170+
const ONLY_HOSTS: bool = true;
171+
172+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
173+
run.path("src/gcc")
174+
}
175+
176+
fn make_run(run: RunConfig<'_>) {
177+
run.builder.ensure(Gcc { target: run.target });
178+
}
179+
180+
/// Compile GCC for `target`.
181+
fn run(self, builder: &Builder<'_>) -> bool {
182+
let target = self.target;
183+
if !target.contains("linux") || !target.contains("x86_64") {
184+
return false;
185+
}
186+
187+
// If GCC has already been built or been downloaded through download-ci-gcc, we avoid
188+
// building it again.
189+
let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config(builder, target)
190+
{
191+
GccBuildStatus::AlreadyBuilt => return true,
192+
GccBuildStatus::ShouldBuild(m) => m,
193+
};
194+
195+
let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
196+
t!(stamp.remove());
197+
let _time = helpers::timeit(builder);
198+
t!(fs::create_dir_all(&out_dir));
199+
200+
if builder.config.dry_run() {
201+
return true;
202+
}
203+
204+
builder.run(
205+
Command::new(root.join("configure"))
206+
.current_dir(&out_dir)
207+
.arg("--enable-host-shared")
208+
.arg("--enable-languages=jit")
209+
.arg("--enable-checking=release")
210+
.arg("--disable-bootstrap")
211+
.arg("--disable-multilib")
212+
.arg(format!("--prefix={}", install_dir.display())),
213+
);
214+
builder
215+
.run(Command::new("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())));
216+
builder.run(Command::new("make").current_dir(&out_dir).arg("install"));
217+
218+
t!(builder.symlink_file(
219+
install_dir.join("lib/libgccjit.so"),
220+
install_dir.join("lib/libgccjit.so.0")
221+
));
222+
223+
t!(stamp.write());
224+
225+
true
226+
}
227+
}

src/bootstrap/src/core/build_steps/llvm.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,17 +1144,17 @@ fn supported_sanitizers(
11441144
}
11451145
}
11461146

1147-
struct HashStamp {
1148-
path: PathBuf,
1149-
hash: Option<Vec<u8>>,
1147+
pub(super) struct HashStamp {
1148+
pub(super) path: PathBuf,
1149+
pub(super) hash: Option<Vec<u8>>,
11501150
}
11511151

11521152
impl HashStamp {
1153-
fn new(path: PathBuf, hash: Option<&str>) -> Self {
1153+
pub(super) fn new(path: PathBuf, hash: Option<&str>) -> Self {
11541154
HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) }
11551155
}
11561156

1157-
fn is_done(&self) -> bool {
1157+
pub(super) fn is_done(&self) -> bool {
11581158
match fs::read(&self.path) {
11591159
Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(),
11601160
Err(e) if e.kind() == io::ErrorKind::NotFound => false,
@@ -1164,7 +1164,7 @@ impl HashStamp {
11641164
}
11651165
}
11661166

1167-
fn remove(&self) -> io::Result<()> {
1167+
pub(super) fn remove(&self) -> io::Result<()> {
11681168
match fs::remove_file(&self.path) {
11691169
Ok(()) => Ok(()),
11701170
Err(e) => {
@@ -1177,7 +1177,7 @@ impl HashStamp {
11771177
}
11781178
}
11791179

1180-
fn write(&self) -> io::Result<()> {
1180+
pub(super) fn write(&self) -> io::Result<()> {
11811181
fs::write(&self.path, self.hash.as_deref().unwrap_or(b""))
11821182
}
11831183
}

src/bootstrap/src/core/build_steps/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub(crate) mod compile;
55
pub(crate) mod dist;
66
pub(crate) mod doc;
77
pub(crate) mod format;
8+
pub(crate) mod gcc;
89
pub(crate) mod install;
910
pub(crate) mod llvm;
1011
pub(crate) mod run;

src/bootstrap/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,10 @@ impl Build {
832832
}
833833
}
834834

835+
fn gcc_out(&self, target: TargetSelection) -> PathBuf {
836+
self.out.join(&*target.triple).join("gcc")
837+
}
838+
835839
fn lld_out(&self, target: TargetSelection) -> PathBuf {
836840
self.out.join(&*target.triple).join("lld")
837841
}

0 commit comments

Comments
 (0)