Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit dd1057d

Browse files
Add libgccjit dist generation
1 parent 935adfd commit dd1057d

File tree

5 files changed

+248
-7
lines changed

5 files changed

+248
-7
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,6 +2286,7 @@ impl Step for RustDev {
22862286
tarball.permit_symlinks(true);
22872287

22882288
builder.ensure(crate::core::build_steps::llvm::Llvm { target });
2289+
builder.ensure(crate::core::build_steps::gcc::Gcc { target });
22892290

22902291
let src_bindir = builder.llvm_out(target).join("bin");
22912292
// If updating this, you likely want to change
@@ -2321,6 +2322,15 @@ impl Step for RustDev {
23212322
// just broadly useful to be able to link against the bundled LLVM.
23222323
tarball.add_dir(builder.llvm_out(target).join("include"), "include");
23232324

2325+
let libgccjit_path = builder.gcc_out(target).join("install/lib/libgccjit.so");
2326+
if libgccjit_path.exists() {
2327+
tarball.add_dir(libgccjit_path, "libgccjit.so");
2328+
tarball.add_dir(
2329+
builder.gcc_out(target).join("install/lib/libgccjit.so.0"),
2330+
"libgccjit.so.0",
2331+
);
2332+
}
2333+
23242334
// Copy libLLVM.so to the target lib dir as well, so the RPATH like
23252335
// `$ORIGIN/../lib` can find it. It may also be used as a dependency
23262336
// of `rustc-dev` to support the inherited `-lLLVM` when using the
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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::sync::OnceLock;
14+
15+
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
16+
use crate::core::config::TargetSelection;
17+
use crate::utils::exec::command;
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(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.run(command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())));
215+
builder.run(command("make").current_dir(&out_dir).arg("install"));
216+
217+
t!(builder.symlink_file(
218+
install_dir.join("lib/libgccjit.so"),
219+
install_dir.join("lib/libgccjit.so.0")
220+
));
221+
222+
t!(stamp.write());
223+
224+
true
225+
}
226+
}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,17 +1141,17 @@ fn supported_sanitizers(
11411141
}
11421142
}
11431143

1144-
struct HashStamp {
1145-
path: PathBuf,
1146-
hash: Option<Vec<u8>>,
1144+
pub(super) struct HashStamp {
1145+
pub(super) path: PathBuf,
1146+
pub(super) hash: Option<Vec<u8>>,
11471147
}
11481148

11491149
impl HashStamp {
1150-
fn new(path: PathBuf, hash: Option<&str>) -> Self {
1150+
pub(super) fn new(path: PathBuf, hash: Option<&str>) -> Self {
11511151
HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) }
11521152
}
11531153

1154-
fn is_done(&self) -> bool {
1154+
pub(super) fn is_done(&self) -> bool {
11551155
match fs::read(&self.path) {
11561156
Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(),
11571157
Err(e) if e.kind() == io::ErrorKind::NotFound => false,
@@ -1161,7 +1161,7 @@ impl HashStamp {
11611161
}
11621162
}
11631163

1164-
fn remove(&self) -> io::Result<()> {
1164+
pub(super) fn remove(&self) -> io::Result<()> {
11651165
match fs::remove_file(&self.path) {
11661166
Ok(()) => Ok(()),
11671167
Err(e) => {
@@ -1174,7 +1174,7 @@ impl HashStamp {
11741174
}
11751175
}
11761176

1177-
fn write(&self) -> io::Result<()> {
1177+
pub(super) fn write(&self) -> io::Result<()> {
11781178
fs::write(&self.path, self.hash.as_deref().unwrap_or(b""))
11791179
}
11801180
}

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 perf;

src/bootstrap/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,10 @@ impl Build {
849849
}
850850
}
851851

852+
fn gcc_out(&self, target: TargetSelection) -> PathBuf {
853+
self.out.join(&*target.triple).join("gcc")
854+
}
855+
852856
fn lld_out(&self, target: TargetSelection) -> PathBuf {
853857
self.out.join(&*target.triple).join("lld")
854858
}

0 commit comments

Comments
 (0)