Skip to content

Commit 9e6f6bf

Browse files
committed
Auto merge of #125419 - GuillaumeGomez:add-gcc-to-dist, r=<try>
[bootstrap] Add gcc to dist generation As `@eholk` summarized below: > From my understanding, this change would add libgccjit as an optional component to the Rust distribution. This library is licensed under GPLv2 and currently we do not have any other components under that license so it would be a new license, and one that is generally more restrictive than the other licenses we use. It'll greatly improve the experience for anyone wanting to work on the GCC backend from the compiler. Should help with #124172. Will unblock #124353. r? `@Kobzol`
2 parents 4f3a276 + f302cfd commit 9e6f6bf

File tree

14 files changed

+625
-8
lines changed

14 files changed

+625
-8
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,6 @@
4747
path = src/tools/rustc-perf
4848
url = https://github.com/rust-lang/rustc-perf.git
4949
shallow = true
50+
[submodule "src/gcc"]
51+
path = src/gcc
52+
url = https://github.com/rust-lang/gcc

LICENSES/GCC-exception-3.1.txt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
GCC RUNTIME LIBRARY EXCEPTION
2+
3+
Version 3.1, 31 March 2009
4+
5+
Copyright © 2009 Free Software Foundation, Inc. <https://fsf.org/>
6+
7+
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
8+
9+
This GCC Runtime Library Exception ("Exception") is an additional permission under section 7 of the GNU General Public License, version 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that bears a notice placed by the copyright holder of the file stating that the file is governed by GPLv3 along with this Exception.
10+
11+
When you use GCC to compile a program, GCC may combine portions of certain GCC header files and runtime libraries with the compiled program. The purpose of this Exception is to allow compilation of non-GPL (including proprietary) programs to use, in this way, the header files and runtime libraries covered by this Exception.
12+
0. Definitions.
13+
14+
A file is an "Independent Module" if it either requires the Runtime Library for execution after a Compilation Process, or makes use of an interface provided by the Runtime Library, but is not otherwise based on the Runtime Library.
15+
16+
"GCC" means a version of the GNU Compiler Collection, with or without modifications, governed by version 3 (or a specified later version) of the GNU General Public License (GPL) with the option of using any subsequent versions published by the FSF.
17+
18+
"GPL-compatible Software" is software whose conditions of propagation, modification and use would permit combination with GCC in accord with the license of GCC.
19+
20+
"Target Code" refers to output from any compiler for a real or virtual target processor architecture, in executable form or suitable for input to an assembler, loader, linker and/or execution phase. Notwithstanding that, Target Code does not include data in any format that is used as a compiler intermediate representation, or used for producing a compiler intermediate representation.
21+
22+
The "Compilation Process" transforms code entirely represented in non-intermediate languages designed for human-written code, and/or in Java Virtual Machine byte code, into Target Code. Thus, for example, use of source code generators and preprocessors need not be considered part of the Compilation Process, since the Compilation Process can be understood as starting with the output of the generators or preprocessors.
23+
24+
A Compilation Process is "Eligible" if it is done using GCC, alone or with other GPL-compatible software, or if it is done without using any work based on GCC. For example, using non-GPL-compatible Software to optimize any GCC intermediate representations would not qualify as an Eligible Compilation Process.
25+
1. Grant of Additional Permission.
26+
27+
You have permission to propagate a work of Target Code formed by combining the Runtime Library with Independent Modules, even if such propagation would otherwise violate the terms of GPLv3, provided that all Target Code was generated by Eligible Compilation Processes. You may then convey such a combination under terms of your choice, consistent with the licensing of the Independent Modules.
28+
2. No Weakening of GCC Copyleft.
29+
30+
The availability of this Exception does not imply any general presumption that third-party software is unaffected by the copyleft requirements of the license of GCC.

LICENSES/GPL-2.0.txt

+133
Large diffs are not rendered by default.

LICENSES/GPL-3.0-or-later.txt

+202
Large diffs are not rendered by default.

LICENSES/ISC.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ISC License
2+
3+
<copyright notice>
4+
5+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

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

+5
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
+227
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

+7-7
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

+1
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

+4
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,10 @@ impl Build {
835835
}
836836
}
837837

838+
fn gcc_out(&self, target: TargetSelection) -> PathBuf {
839+
self.out.join(&*target.triple).join("gcc")
840+
}
841+
838842
fn lld_out(&self, target: TargetSelection) -> PathBuf {
839843
self.out.join(&*target.triple).join("lld")
840844
}

src/gcc

Submodule gcc added at 272d0cc

src/tools/tidy/config/black.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ extend-exclude = """(\
1212
src/llvm-project/|\
1313
src/doc/embedded-book/|\
1414
src/tools/rustc-perf/|\
15-
library/backtrace/
15+
library/backtrace/|\
16+
src/gcc/
1617
)"""

src/tools/tidy/config/ruff.toml

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ extend-exclude = [
2727
"src/doc/embedded-book/",
2828
"library/backtrace/",
2929
"src/tools/rustc-perf/",
30+
"src/gcc/",
3031
# Hack: CI runs from a subdirectory under the main checkout
3132
"../src/doc/nomicon/",
3233
"../src/tools/cargo/",
@@ -40,4 +41,5 @@ extend-exclude = [
4041
"../src/doc/embedded-book/",
4142
"../library/backtrace/",
4243
"../src/tools/rustc-perf/",
44+
"../src/gcc/",
4345
]

src/tools/tidy/src/walk.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn filter_dirs(path: &Path) -> bool {
2828
"src/doc/rust-by-example",
2929
"src/doc/rustc-dev-guide",
3030
"src/doc/reference",
31+
"src/gcc",
3132
// Filter RLS output directories
3233
"target/rls",
3334
"src/bootstrap/target",

0 commit comments

Comments
 (0)