Skip to content

Commit 23b04c0

Browse files
committed
Auto merge of rust-lang#125419 - GuillaumeGomez:add-gcc-to-dist, r=Kobzol
[bootstrap] Add support for building gcc and libgccjit 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 rust-lang#124172. Will unblock rust-lang#124353. r? `@Kobzol`
2 parents 507c05b + e7fa03b commit 23b04c0

File tree

17 files changed

+604
-42
lines changed

17 files changed

+604
-42
lines changed

Diff for: .gitmodules

+4
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,7 @@
5151
path = src/tools/enzyme
5252
url = https://github.com/EnzymeAD/Enzyme.git
5353
shallow = true
54+
[submodule "src/gcc"]
55+
path = src/gcc
56+
url = https://github.com/rust-lang/gcc.git
57+
shallow = true

Diff for: 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.

Diff for: LICENSES/GPL-2.0-only.txt

+133
Large diffs are not rendered by default.

Diff for: LICENSES/GPL-3.0-or-later.txt

+202
Large diffs are not rendered by default.

Diff for: 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.

Diff for: REUSE.toml

+34
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,37 @@ SPDX-FileCopyrightText = [
168168
"2003-2019 University of Illinois at Urbana-Champaign.",
169169
]
170170
SPDX-License-Identifier = "NCSA AND Apache-2.0 WITH LLVM-exception"
171+
172+
[[annotations]]
173+
path = "src/gcc/**"
174+
precedence = "override"
175+
SPDX-FileCopyrightText = [
176+
"Copyright (C) 1997-2024 Free Software Foundation, Inc.",
177+
]
178+
SPDX-License-Identifier = "GPL-3.0-or-later"
179+
180+
[[annotations]]
181+
path = "src/gcc/gcc/testsuite/**"
182+
precedence = "override"
183+
SPDX-FileCopyrightText = [
184+
"Copyright (C) 2000-2024 Free Software Foundation, Inc.",
185+
]
186+
SPDX-License-Identifier = "GPL-2.0-only"
187+
188+
[[annotations]]
189+
path = "src/gcc/gcc/testsuite/c-c++-common/analyzer/*.c"
190+
precedence = "override"
191+
SPDX-FileCopyrightText = [
192+
"Copyright (c) 2007-2011 Atheros Communications Inc.",
193+
"Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc.",
194+
"Copyright (c) 2016-2017 Erik Stromdahl <[email protected]>",
195+
]
196+
SPDX-License-Identifier = "ISC"
197+
198+
[[annotations]]
199+
path = "src/gcc/libstdc++-v3/config/os/aix/os_defines.h"
200+
precedence = "override"
201+
SPDX-FileCopyrightText = [
202+
"Copyright (C) 2000-2024 Free Software Foundation, Inc.",
203+
]
204+
SPDX-License-Identifier = "GCC-exception-3.1"

Diff for: rustfmt.toml

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ ignore = [
4545
"src/tools/rust-analyzer",
4646
"src/tools/rustc-perf",
4747
"src/tools/rustfmt",
48+
"src/gcc",
4849

4950
# These are ignored by a standard cargo fmt run.
5051
"compiler/rustc_codegen_cranelift/scripts",

Diff for: src/bootstrap/src/core/build_steps/gcc.rs

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//! Compilation of native dependencies like GCC.
2+
//!
3+
//! Native projects like GCC 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 GCC 3 times as part of a normal bootstrap (we want it cached).
7+
//!
8+
//! GCC 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::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, HashStamp};
19+
use crate::{generate_smart_stamp_hash, Kind};
20+
21+
pub struct Meta {
22+
stamp: HashStamp,
23+
out_dir: PathBuf,
24+
install_dir: PathBuf,
25+
root: PathBuf,
26+
}
27+
28+
pub enum GccBuildStatus {
29+
AlreadyBuilt,
30+
ShouldBuild(Meta),
31+
}
32+
33+
/// This returns whether we've already previously built GCC.
34+
///
35+
/// It's used to avoid busting caches during x.py check -- if we've already built
36+
/// GCC, it's fine for us to not try to avoid doing so.
37+
pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
38+
// Initialize the gcc submodule if not initialized already.
39+
builder.config.update_submodule("src/gcc");
40+
41+
// FIXME (GuillaumeGomez): To be done once gccjit has been built in the CI.
42+
// builder.config.maybe_download_ci_gcc();
43+
44+
let root = builder.src.join("src/gcc");
45+
let out_dir = builder.gcc_out(target).join("build");
46+
let install_dir = builder.gcc_out(target).join("install");
47+
48+
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
49+
let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
50+
generate_smart_stamp_hash(
51+
builder,
52+
&builder.config.src.join("src/gcc"),
53+
builder.in_tree_gcc_info.sha().unwrap_or_default(),
54+
)
55+
});
56+
57+
let stamp = out_dir.join("gcc-finished-building");
58+
let stamp = HashStamp::new(stamp, Some(smart_stamp_hash));
59+
60+
if stamp.is_done() {
61+
if stamp.hash.is_none() {
62+
builder.info(
63+
"Could not determine the GCC submodule commit hash. \
64+
Assuming that an GCC rebuild is not necessary.",
65+
);
66+
builder.info(&format!(
67+
"To force GCC to rebuild, remove the file `{}`",
68+
stamp.path.display()
69+
));
70+
}
71+
return GccBuildStatus::AlreadyBuilt;
72+
}
73+
74+
GccBuildStatus::ShouldBuild(Meta { stamp, out_dir, install_dir, root })
75+
}
76+
77+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
78+
pub struct Gcc {
79+
pub target: TargetSelection,
80+
}
81+
82+
impl Step for Gcc {
83+
type Output = bool;
84+
85+
const ONLY_HOSTS: bool = true;
86+
87+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
88+
run.path("src/gcc").alias("gcc")
89+
}
90+
91+
fn make_run(run: RunConfig<'_>) {
92+
run.builder.ensure(Gcc { target: run.target });
93+
}
94+
95+
/// Compile GCC for `target`.
96+
fn run(self, builder: &Builder<'_>) -> bool {
97+
let target = self.target;
98+
99+
// If GCC has already been built, we avoid building it again.
100+
let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config(builder, target)
101+
{
102+
GccBuildStatus::AlreadyBuilt => return true,
103+
GccBuildStatus::ShouldBuild(m) => m,
104+
};
105+
106+
let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
107+
t!(stamp.remove());
108+
let _time = helpers::timeit(builder);
109+
t!(fs::create_dir_all(&out_dir));
110+
111+
if builder.config.dry_run() {
112+
return true;
113+
}
114+
115+
command(root.join("contrib/download_prerequisites")).current_dir(&root).run(builder);
116+
command(root.join("configure"))
117+
.current_dir(&out_dir)
118+
.arg("--enable-host-shared")
119+
.arg("--enable-languages=jit")
120+
.arg("--enable-checking=release")
121+
.arg("--disable-bootstrap")
122+
.arg("--disable-multilib")
123+
.arg(format!("--prefix={}", install_dir.display()))
124+
.run(builder);
125+
command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder);
126+
command("make").current_dir(&out_dir).arg("install").run(builder);
127+
128+
let lib_alias = install_dir.join("lib/libgccjit.so.0");
129+
if !lib_alias.exists() {
130+
t!(builder.symlink_file(install_dir.join("lib/libgccjit.so"), lib_alias,));
131+
}
132+
133+
t!(stamp.write());
134+
135+
true
136+
}
137+
}

Diff for: src/bootstrap/src/core/build_steps/llvm.rs

+2-40
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
//! LLVM and compiler-rt are essentially just wired up to everything else to
99
//! ensure that they're always in place if needed.
1010
11+
use std::env;
1112
use std::env::consts::EXE_EXTENSION;
1213
use std::ffi::{OsStr, OsString};
1314
use std::fs::{self, File};
1415
use std::path::{Path, PathBuf};
1516
use std::sync::OnceLock;
16-
use std::{env, io};
1717

1818
use build_helper::ci::CiEnv;
1919
use build_helper::git::get_closest_merge_commit;
@@ -23,7 +23,7 @@ use crate::core::config::{Config, TargetSelection};
2323
use crate::utils::channel;
2424
use crate::utils::exec::command;
2525
use crate::utils::helpers::{
26-
self, exe, get_clang_cl_resource_dir, output, t, unhashed_basename, up_to_date,
26+
self, exe, get_clang_cl_resource_dir, output, t, unhashed_basename, up_to_date, HashStamp,
2727
};
2828
use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind};
2929

@@ -1242,44 +1242,6 @@ fn supported_sanitizers(
12421242
}
12431243
}
12441244

1245-
struct HashStamp {
1246-
path: PathBuf,
1247-
hash: Option<Vec<u8>>,
1248-
}
1249-
1250-
impl HashStamp {
1251-
fn new(path: PathBuf, hash: Option<&str>) -> Self {
1252-
HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) }
1253-
}
1254-
1255-
fn is_done(&self) -> bool {
1256-
match fs::read(&self.path) {
1257-
Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(),
1258-
Err(e) if e.kind() == io::ErrorKind::NotFound => false,
1259-
Err(e) => {
1260-
panic!("failed to read stamp file `{}`: {}", self.path.display(), e);
1261-
}
1262-
}
1263-
}
1264-
1265-
fn remove(&self) -> io::Result<()> {
1266-
match fs::remove_file(&self.path) {
1267-
Ok(()) => Ok(()),
1268-
Err(e) => {
1269-
if e.kind() == io::ErrorKind::NotFound {
1270-
Ok(())
1271-
} else {
1272-
Err(e)
1273-
}
1274-
}
1275-
}
1276-
}
1277-
1278-
fn write(&self) -> io::Result<()> {
1279-
fs::write(&self.path, self.hash.as_deref().unwrap_or(b""))
1280-
}
1281-
}
1282-
12831245
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12841246
pub struct CrtBeginEnd {
12851247
pub target: TargetSelection,

Diff for: 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 perf;

Diff for: src/bootstrap/src/core/builder.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use clap::ValueEnum;
1414

1515
use crate::core::build_steps::tool::{self, SourceType};
1616
use crate::core::build_steps::{
17-
check, clean, clippy, compile, dist, doc, install, llvm, run, setup, test, vendor,
17+
check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, vendor,
1818
};
1919
use crate::core::config::flags::{Color, Subcommand};
2020
use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
@@ -793,6 +793,7 @@ impl<'a> Builder<'a> {
793793
tool::Clippy,
794794
tool::CargoClippy,
795795
llvm::Llvm,
796+
gcc::Gcc,
796797
llvm::Sanitizers,
797798
tool::Rustfmt,
798799
tool::Miri,

Diff for: src/bootstrap/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub struct Build {
145145
rustfmt_info: GitInfo,
146146
enzyme_info: GitInfo,
147147
in_tree_llvm_info: GitInfo,
148+
in_tree_gcc_info: GitInfo,
148149
local_rebuild: bool,
149150
fail_fast: bool,
150151
doc_tests: DocTests,
@@ -315,6 +316,7 @@ impl Build {
315316

316317
// we always try to use git for LLVM builds
317318
let in_tree_llvm_info = GitInfo::new(false, &src.join("src/llvm-project"));
319+
let in_tree_gcc_info = GitInfo::new(false, &src.join("src/gcc"));
318320

319321
let initial_target_libdir_str = if config.dry_run() {
320322
"/dummy/lib/path/to/lib/".to_string()
@@ -407,6 +409,7 @@ impl Build {
407409
rustfmt_info,
408410
enzyme_info,
409411
in_tree_llvm_info,
412+
in_tree_gcc_info,
410413
cc: RefCell::new(HashMap::new()),
411414
cxx: RefCell::new(HashMap::new()),
412415
ar: RefCell::new(HashMap::new()),
@@ -765,6 +768,10 @@ impl Build {
765768
self.out.join(&*target.triple).join("enzyme")
766769
}
767770

771+
fn gcc_out(&self, target: TargetSelection) -> PathBuf {
772+
self.out.join(&*target.triple).join("gcc")
773+
}
774+
768775
fn lld_out(&self, target: TargetSelection) -> PathBuf {
769776
self.out.join(target).join("lld")
770777
}

0 commit comments

Comments
 (0)