Skip to content

Commit 6f0746d

Browse files
Rollup merge of #145341 - Kobzol:codegen-backend-gcc, r=jieyouxu
Install libgccjit into the compiler's sysroot when cg_gcc is enabled This PR installs the `libgccjit.so` library (which is essentially GCC) into the rustc sysroot (`stageN/lib` on Linux) when the GCC codegen backend is enabled. This allows using the GCC codegen backend "out of the box" with the resulting rustc. It would be nice to get rid of the `libgccjit.so.0` alias (https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/Forcing.20unversioned.20dylib.20dependencies.20on.20Linux/with/534180740), but it's not blocking for this change. You can try running `x build std --set 'rust.codegen-backends=["llvm", "gcc"]'` and then compiling a hello world with `rustc +stage1 -Zcodegen-backend=gcc main.rs`. It is now also possible to build stage2 rustc when the GCC is configured to be the default codegen backend, without any further `LD_LIBRARY_PATH` hacks: `./x build compiler --stage 2 --set 'rust.codegen-backends=["gcc"]'`. After this change, it should be pretty simple to add a dist/rustup step for actually shipping cg_gcc to end users. CC ```@GuillaumeGomez``` r? ```@jieyouxu```
2 parents a6146fd + f40a784 commit 6f0746d

File tree

3 files changed

+98
-67
lines changed

3 files changed

+98
-67
lines changed

src/bootstrap/src/core/build_steps/compile.rs

Lines changed: 69 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use serde_derive::Deserialize;
1818
#[cfg(feature = "tracing")]
1919
use tracing::{instrument, span};
2020

21-
use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
21+
use crate::core::build_steps::gcc::{Gcc, GccOutput, add_cg_gcc_cargo_flags};
2222
use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts};
2323
use crate::core::build_steps::{dist, llvm};
2424
use crate::core::builder;
@@ -1543,13 +1543,22 @@ impl Step for RustcLink {
15431543
}
15441544
}
15451545

1546+
/// Output of the `compile::GccCodegenBackend` step.
1547+
/// It includes the path to the libgccjit library on which this backend depends.
1548+
#[derive(Clone)]
1549+
pub struct GccCodegenBackendOutput {
1550+
stamp: BuildStamp,
1551+
gcc: GccOutput,
1552+
}
1553+
15461554
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15471555
pub struct GccCodegenBackend {
15481556
compilers: RustcPrivateCompilers,
15491557
}
15501558

15511559
impl Step for GccCodegenBackend {
1552-
type Output = BuildStamp;
1560+
type Output = GccCodegenBackendOutput;
1561+
15531562
const ONLY_HOSTS: bool = true;
15541563

15551564
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -1584,6 +1593,8 @@ impl Step for GccCodegenBackend {
15841593
&CodegenBackendKind::Gcc,
15851594
);
15861595

1596+
let gcc = builder.ensure(Gcc { target });
1597+
15871598
if builder.config.keep_stage.contains(&build_compiler.stage) {
15881599
trace!("`keep-stage` requested");
15891600
builder.info(
@@ -1592,7 +1603,7 @@ impl Step for GccCodegenBackend {
15921603
);
15931604
// Codegen backends are linked separately from this step today, so we don't do
15941605
// anything here.
1595-
return stamp;
1606+
return GccCodegenBackendOutput { stamp, gcc };
15961607
}
15971608

15981609
let mut cargo = builder::Cargo::new(
@@ -1606,13 +1617,16 @@ impl Step for GccCodegenBackend {
16061617
cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
16071618
rustc_cargo_env(builder, &mut cargo, target);
16081619

1609-
let gcc = builder.ensure(Gcc { target });
16101620
add_cg_gcc_cargo_flags(&mut cargo, &gcc);
16111621

16121622
let _guard =
16131623
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target);
16141624
let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
1615-
write_codegen_backend_stamp(stamp, files, builder.config.dry_run())
1625+
1626+
GccCodegenBackendOutput {
1627+
stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()),
1628+
gcc,
1629+
}
16161630
}
16171631

16181632
fn metadata(&self) -> Option<StepMetadata> {
@@ -2191,53 +2205,6 @@ impl Step for Assemble {
21912205
);
21922206
build_compiler.stage = actual_stage;
21932207

2194-
let mut codegen_backend_stamps = vec![];
2195-
{
2196-
#[cfg(feature = "tracing")]
2197-
let _codegen_backend_span =
2198-
span!(tracing::Level::DEBUG, "building requested codegen backends").entered();
2199-
2200-
for backend in builder.config.enabled_codegen_backends(target_compiler.host) {
2201-
// FIXME: this is a horrible hack used to make `x check` work when other codegen
2202-
// backends are enabled.
2203-
// `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot.
2204-
// Then it checks codegen backends, which correctly use these rmetas.
2205-
// Then it needs to check std, but for that it needs to build stage 1 rustc.
2206-
// This copies the build rmetas into the stage0 sysroot, effectively poisoning it,
2207-
// because we then have both check and build rmetas in the same sysroot.
2208-
// That would be fine on its own. However, when another codegen backend is enabled,
2209-
// then building stage 1 rustc implies also building stage 1 codegen backend (even if
2210-
// it isn't used for anything). And since that tries to use the poisoned
2211-
// rmetas, it fails to build.
2212-
// We don't actually need to build rustc-private codegen backends for checking std,
2213-
// so instead we skip that.
2214-
// Note: this would be also an issue for other rustc-private tools, but that is "solved"
2215-
// by check::Std being last in the list of checked things (see
2216-
// `Builder::get_step_descriptions`).
2217-
if builder.kind == Kind::Check && builder.top_stage == 1 {
2218-
continue;
2219-
}
2220-
2221-
let prepare_compilers = || {
2222-
RustcPrivateCompilers::from_build_and_target_compiler(
2223-
build_compiler,
2224-
target_compiler,
2225-
)
2226-
};
2227-
2228-
let stamp = match backend {
2229-
CodegenBackendKind::Cranelift => {
2230-
builder.ensure(CraneliftCodegenBackend { compilers: prepare_compilers() })
2231-
}
2232-
CodegenBackendKind::Gcc => {
2233-
builder.ensure(GccCodegenBackend { compilers: prepare_compilers() })
2234-
}
2235-
CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
2236-
};
2237-
codegen_backend_stamps.push(stamp);
2238-
}
2239-
}
2240-
22412208
let stage = target_compiler.stage;
22422209
let host = target_compiler.host;
22432210
let (host_info, dir_name) = if build_compiler.host == host {
@@ -2296,9 +2263,56 @@ impl Step for Assemble {
22962263
}
22972264
}
22982265

2299-
debug!("copying codegen backends to sysroot");
2300-
for stamp in codegen_backend_stamps {
2301-
copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
2266+
{
2267+
#[cfg(feature = "tracing")]
2268+
let _codegen_backend_span =
2269+
span!(tracing::Level::DEBUG, "building requested codegen backends").entered();
2270+
2271+
for backend in builder.config.enabled_codegen_backends(target_compiler.host) {
2272+
// FIXME: this is a horrible hack used to make `x check` work when other codegen
2273+
// backends are enabled.
2274+
// `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot.
2275+
// Then it checks codegen backends, which correctly use these rmetas.
2276+
// Then it needs to check std, but for that it needs to build stage 1 rustc.
2277+
// This copies the build rmetas into the stage0 sysroot, effectively poisoning it,
2278+
// because we then have both check and build rmetas in the same sysroot.
2279+
// That would be fine on its own. However, when another codegen backend is enabled,
2280+
// then building stage 1 rustc implies also building stage 1 codegen backend (even if
2281+
// it isn't used for anything). And since that tries to use the poisoned
2282+
// rmetas, it fails to build.
2283+
// We don't actually need to build rustc-private codegen backends for checking std,
2284+
// so instead we skip that.
2285+
// Note: this would be also an issue for other rustc-private tools, but that is "solved"
2286+
// by check::Std being last in the list of checked things (see
2287+
// `Builder::get_step_descriptions`).
2288+
if builder.kind == Kind::Check && builder.top_stage == 1 {
2289+
continue;
2290+
}
2291+
2292+
let prepare_compilers = || {
2293+
RustcPrivateCompilers::from_build_and_target_compiler(
2294+
build_compiler,
2295+
target_compiler,
2296+
)
2297+
};
2298+
2299+
match backend {
2300+
CodegenBackendKind::Cranelift => {
2301+
let stamp = builder
2302+
.ensure(CraneliftCodegenBackend { compilers: prepare_compilers() });
2303+
copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
2304+
}
2305+
CodegenBackendKind::Gcc => {
2306+
let output =
2307+
builder.ensure(GccCodegenBackend { compilers: prepare_compilers() });
2308+
copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler);
2309+
// Also copy libgccjit to the library sysroot, so that it is available for
2310+
// the codegen backend.
2311+
output.gcc.install_to(builder, &rustc_libdir);
2312+
}
2313+
CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
2314+
}
2315+
}
23022316
}
23032317

23042318
if builder.config.lld_enabled {

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,19 @@ impl Step for Rustc {
478478
if libdir_relative.to_str() != Some("bin") {
479479
let libdir = builder.rustc_libdir(compiler);
480480
for entry in builder.read_dir(&libdir) {
481-
if is_dylib(&entry.path()) {
481+
// A safeguard that we will not ship libgccjit.so from the libdir, in case the
482+
// GCC codegen backend is enabled by default.
483+
// Long-term we should probably split the config options for:
484+
// - Include cg_gcc in the rustc sysroot by default
485+
// - Run dist of a specific codegen backend in `x dist` by default
486+
if is_dylib(&entry.path())
487+
&& !entry
488+
.path()
489+
.file_name()
490+
.and_then(|n| n.to_str())
491+
.map(|n| n.contains("libgccjit"))
492+
.unwrap_or(false)
493+
{
482494
// Don't use custom libdir here because ^lib/ will be resolved again
483495
// with installer
484496
builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary);

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::fs;
1212
use std::path::{Path, PathBuf};
1313
use std::sync::OnceLock;
1414

15+
use crate::FileType;
1516
use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
1617
use crate::core::config::TargetSelection;
1718
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
@@ -28,6 +29,21 @@ pub struct GccOutput {
2829
pub libgccjit: PathBuf,
2930
}
3031

32+
impl GccOutput {
33+
/// Install the required libgccjit library file(s) to the specified `path`.
34+
pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) {
35+
// At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol).
36+
// However, at runtime, it will by default look for libgccjit.so.0.
37+
// So when we install the built libgccjit.so file to the target `directory`, we add it there
38+
// with the `.0` suffix.
39+
let mut target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string();
40+
target_filename.push_str(".0");
41+
42+
let dst = directory.join(target_filename);
43+
builder.copy_link(&self.libgccjit, &dst, FileType::NativeLibrary);
44+
}
45+
}
46+
3147
impl Step for Gcc {
3248
type Output = GccOutput;
3349

@@ -61,23 +77,13 @@ impl Step for Gcc {
6177
}
6278

6379
build_gcc(&metadata, builder, target);
64-
create_lib_alias(builder, &libgccjit_path);
6580

6681
t!(metadata.stamp.write());
6782

6883
GccOutput { libgccjit: libgccjit_path }
6984
}
7085
}
7186

72-
/// Creates a libgccjit.so.0 alias next to libgccjit.so if it does not
73-
/// already exist
74-
fn create_lib_alias(builder: &Builder<'_>, libgccjit: &PathBuf) {
75-
let lib_alias = libgccjit.parent().unwrap().join("libgccjit.so.0");
76-
if !lib_alias.exists() {
77-
t!(builder.symlink_file(libgccjit, lib_alias));
78-
}
79-
}
80-
8187
pub struct Meta {
8288
stamp: BuildStamp,
8389
out_dir: PathBuf,
@@ -124,7 +130,6 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<Pa
124130
}
125131

126132
let libgccjit = root.join("lib").join("libgccjit.so");
127-
create_lib_alias(builder, &libgccjit);
128133
Some(libgccjit)
129134
}
130135
PathFreshness::HasLocalModifications { .. } => {

0 commit comments

Comments
 (0)