Skip to content

Commit a79d14d

Browse files
committed
Refactor GCC compilation
1 parent 91a0e16 commit a79d14d

File tree

1 file changed

+111
-105
lines changed
  • src/bootstrap/src/core/build_steps

1 file changed

+111
-105
lines changed

src/bootstrap/src/core/build_steps/gcc.rs

Lines changed: 111 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,68 @@ use std::sync::OnceLock;
1414

1515
use build_helper::ci::CiEnv;
1616

17-
use crate::Kind;
18-
use crate::core::builder::{Builder, Cargo, RunConfig, ShouldRun, Step};
17+
use crate::Config;
18+
use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
1919
use crate::core::config::TargetSelection;
2020
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
2121
use crate::utils::exec::command;
2222
use crate::utils::helpers::{self, t};
2323

24+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
25+
pub struct Gcc {
26+
pub target: TargetSelection,
27+
}
28+
29+
#[derive(Clone)]
30+
pub struct GccOutput {
31+
pub libgccjit: PathBuf,
32+
}
33+
34+
impl Step for Gcc {
35+
type Output = GccOutput;
36+
37+
const ONLY_HOSTS: bool = true;
38+
39+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
40+
run.path("src/gcc").alias("gcc")
41+
}
42+
43+
fn make_run(run: RunConfig<'_>) {
44+
run.builder.ensure(Gcc { target: run.target });
45+
}
46+
47+
/// Compile GCC (specifically `libgccjit`) for `target`.
48+
fn run(self, builder: &Builder<'_>) -> Self::Output {
49+
let target = self.target;
50+
51+
// If GCC has already been built, we avoid building it again.
52+
let metadata = match get_gcc_build_status(builder, target) {
53+
GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path },
54+
GccBuildStatus::ShouldBuild(m) => m,
55+
};
56+
57+
let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
58+
t!(metadata.stamp.remove());
59+
let _time = helpers::timeit(builder);
60+
61+
let libgccjit_path = libgccjit_built_path(&metadata.install_dir);
62+
if builder.config.dry_run() {
63+
return GccOutput { libgccjit: libgccjit_path };
64+
}
65+
66+
build_gcc(&metadata, builder, target);
67+
68+
let lib_alias = metadata.install_dir.join("lib/libgccjit.so.0");
69+
if !lib_alias.exists() {
70+
t!(builder.symlink_file(&libgccjit_path, lib_alias));
71+
}
72+
73+
t!(metadata.stamp.write());
74+
75+
GccOutput { libgccjit: libgccjit_path }
76+
}
77+
}
78+
2479
pub struct Meta {
2580
stamp: BuildStamp,
2681
out_dir: PathBuf,
@@ -38,7 +93,7 @@ pub enum GccBuildStatus {
3893
///
3994
/// It's used to avoid busting caches during x.py check -- if we've already built
4095
/// GCC, it's fine for us to not try to avoid doing so.
41-
pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
96+
pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
4297
// Initialize the gcc submodule if not initialized already.
4398
builder.config.update_submodule("src/gcc");
4499

@@ -87,116 +142,67 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
87142
install_dir.join("lib/libgccjit.so")
88143
}
89144

90-
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
91-
pub struct Gcc {
92-
pub target: TargetSelection,
93-
}
145+
fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
146+
let Meta { stamp: _, out_dir, install_dir, root } = metadata;
94147

95-
#[derive(Clone)]
96-
pub struct GccOutput {
97-
pub libgccjit: PathBuf,
98-
}
99-
100-
impl Step for Gcc {
101-
type Output = GccOutput;
102-
103-
const ONLY_HOSTS: bool = true;
104-
105-
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
106-
run.path("src/gcc").alias("gcc")
107-
}
108-
109-
fn make_run(run: RunConfig<'_>) {
110-
run.builder.ensure(Gcc { target: run.target });
111-
}
112-
113-
/// Compile GCC (specifically `libgccjit`) for `target`.
114-
fn run(self, builder: &Builder<'_>) -> Self::Output {
115-
let target = self.target;
148+
t!(fs::create_dir_all(out_dir));
116149

117-
// If GCC has already been built, we avoid building it again.
118-
let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config(builder, target)
119-
{
120-
GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path },
121-
GccBuildStatus::ShouldBuild(m) => m,
122-
};
123-
124-
let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
125-
t!(stamp.remove());
126-
let _time = helpers::timeit(builder);
127-
t!(fs::create_dir_all(&out_dir));
128-
129-
let libgccjit_path = libgccjit_built_path(&install_dir);
130-
if builder.config.dry_run() {
131-
return GccOutput { libgccjit: libgccjit_path };
150+
// GCC creates files (e.g. symlinks to the downloaded dependencies)
151+
// in the source directory, which does not work with our CI setup, where we mount
152+
// source directories as read-only on Linux.
153+
// Therefore, as a part of the build in CI, we first copy the whole source directory
154+
// to the build directory, and perform the build from there.
155+
let src_dir = if CiEnv::is_ci() {
156+
let src_dir = builder.gcc_out(target).join("src");
157+
if src_dir.exists() {
158+
builder.remove_dir(&src_dir);
132159
}
133-
134-
// GCC creates files (e.g. symlinks to the downloaded dependencies)
135-
// in the source directory, which does not work with our CI setup, where we mount
136-
// source directories as read-only on Linux.
137-
// Therefore, as a part of the build in CI, we first copy the whole source directory
138-
// to the build directory, and perform the build from there.
139-
let src_dir = if CiEnv::is_ci() {
140-
let src_dir = builder.gcc_out(target).join("src");
141-
if src_dir.exists() {
142-
builder.remove_dir(&src_dir);
143-
}
144-
builder.create_dir(&src_dir);
145-
builder.cp_link_r(&root, &src_dir);
146-
src_dir
147-
} else {
148-
root
149-
};
150-
151-
command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder);
152-
let mut configure_cmd = command(src_dir.join("configure"));
153-
configure_cmd
154-
.current_dir(&out_dir)
155-
// On CI, we compile GCC with Clang.
156-
// The -Wno-everything flag is needed to make GCC compile with Clang 19.
157-
// `-g -O2` are the default flags that are otherwise used by Make.
158-
// FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml.
159-
.env("CXXFLAGS", "-Wno-everything -g -O2")
160-
.env("CFLAGS", "-Wno-everything -g -O2")
161-
.arg("--enable-host-shared")
162-
.arg("--enable-languages=jit")
163-
.arg("--enable-checking=release")
164-
.arg("--disable-bootstrap")
165-
.arg("--disable-multilib")
166-
.arg(format!("--prefix={}", install_dir.display()));
167-
let cc = builder.build.cc(target).display().to_string();
168-
let cc = builder
160+
builder.create_dir(&src_dir);
161+
builder.cp_link_r(root, &src_dir);
162+
src_dir
163+
} else {
164+
root.clone()
165+
};
166+
167+
command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder);
168+
let mut configure_cmd = command(src_dir.join("configure"));
169+
configure_cmd
170+
.current_dir(out_dir)
171+
// On CI, we compile GCC with Clang.
172+
// The -Wno-everything flag is needed to make GCC compile with Clang 19.
173+
// `-g -O2` are the default flags that are otherwise used by Make.
174+
// FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml.
175+
.env("CXXFLAGS", "-Wno-everything -g -O2")
176+
.env("CFLAGS", "-Wno-everything -g -O2")
177+
.arg("--enable-host-shared")
178+
.arg("--enable-languages=jit")
179+
.arg("--enable-checking=release")
180+
.arg("--disable-bootstrap")
181+
.arg("--disable-multilib")
182+
.arg(format!("--prefix={}", install_dir.display()));
183+
let cc = builder.build.cc(target).display().to_string();
184+
let cc = builder
185+
.build
186+
.config
187+
.ccache
188+
.as_ref()
189+
.map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}"));
190+
configure_cmd.env("CC", cc);
191+
192+
if let Ok(ref cxx) = builder.build.cxx(target) {
193+
let cxx = cxx.display().to_string();
194+
let cxx = builder
169195
.build
170196
.config
171197
.ccache
172198
.as_ref()
173-
.map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}"));
174-
configure_cmd.env("CC", cc);
175-
176-
if let Ok(ref cxx) = builder.build.cxx(target) {
177-
let cxx = cxx.display().to_string();
178-
let cxx = builder
179-
.build
180-
.config
181-
.ccache
182-
.as_ref()
183-
.map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}"));
184-
configure_cmd.env("CXX", cxx);
185-
}
186-
configure_cmd.run(builder);
187-
188-
command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder);
189-
command("make").current_dir(&out_dir).arg("install").run(builder);
190-
191-
let lib_alias = install_dir.join("lib/libgccjit.so.0");
192-
if !lib_alias.exists() {
193-
t!(builder.symlink_file(&libgccjit_path, lib_alias));
194-
}
195-
196-
t!(stamp.write());
197-
198-
GccOutput { libgccjit: libgccjit_path }
199+
.map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}"));
200+
configure_cmd.env("CXX", cxx);
199201
}
202+
configure_cmd.run(builder);
203+
204+
command("make").current_dir(out_dir).arg(format!("-j{}", builder.jobs())).run(builder);
205+
command("make").current_dir(out_dir).arg("install").run(builder);
200206
}
201207

202208
/// Configures a Cargo invocation so that it can build the GCC codegen backend.

0 commit comments

Comments
 (0)