Skip to content

Commit 58721a9

Browse files
committed
Explicitly model that a single cg_gcc might need a bunch of libgccjit.so dylibs
1 parent 3838fcf commit 58721a9

File tree

4 files changed

+98
-75
lines changed

4 files changed

+98
-75
lines changed

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

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! goes along from the output of the previous stage.
88
99
use std::borrow::Cow;
10-
use std::collections::HashSet;
10+
use std::collections::{BTreeMap, HashSet};
1111
use std::ffi::OsStr;
1212
use std::io::BufReader;
1313
use std::io::prelude::*;
@@ -1576,17 +1576,77 @@ impl Step for RustcLink {
15761576
}
15771577
}
15781578

1579+
/// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets.
1580+
#[derive(Clone)]
1581+
pub struct GccDylibSet {
1582+
dylibs: BTreeMap<TargetSelection, GccOutput>,
1583+
}
1584+
1585+
impl GccDylibSet {
1586+
/// Install the libgccjit dylibs to the corresponding target directories of the given compiler.
1587+
pub fn install_to(&self, builder: &Builder<'_>, compiler: Compiler) {
1588+
if builder.config.dry_run() {
1589+
return;
1590+
}
1591+
1592+
// <rustc>/lib
1593+
let lib_sysroot = builder.rustc_libdir(compiler);
1594+
1595+
for (target, libgccjit) in &self.dylibs {
1596+
let libgccjit = libgccjit.libgccjit();
1597+
let target_filename = libgccjit.file_name().unwrap().to_str().unwrap();
1598+
1599+
// If we build libgccjit ourselves, then `libgccjit` can actually be a symlink.
1600+
// In that case, we have to resolve it first, otherwise we'd create a symlink to a
1601+
// symlink, which wouldn't work.
1602+
let actual_libgccjit_path = t!(
1603+
libgccjit.canonicalize(),
1604+
format!("Cannot find libgccjit at {}", libgccjit.display())
1605+
);
1606+
1607+
// <rustc>/lib/rustlib/<target>/lib/libgccjit.so
1608+
let dest_dir = lib_sysroot.join("rustlib").join(target).join("lib");
1609+
t!(fs::create_dir_all(&dest_dir));
1610+
let dst = dest_dir.join(target_filename);
1611+
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
1612+
}
1613+
}
1614+
}
1615+
15791616
/// Output of the `compile::GccCodegenBackend` step.
1580-
/// It includes the path to the libgccjit library on which this backend depends.
1617+
///
1618+
/// It contains paths to all built libgccjit libraries on which this backend depends here.
15811619
#[derive(Clone)]
15821620
pub struct GccCodegenBackendOutput {
15831621
stamp: BuildStamp,
1584-
gcc: GccOutput,
1622+
dylib_set: GccDylibSet,
15851623
}
15861624

1625+
/// Builds the GCC codegen backend (`cg_gcc`).
1626+
/// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each
1627+
/// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64,
1628+
/// you will need at least:
1629+
/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros)
1630+
/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code)
1631+
///
1632+
/// We model this by having a single cg_gcc for a given host target, which contains one
1633+
/// libgccjit per (host, target) pair.
1634+
/// Note that the host target is taken from `self.compilers.build_compiler.host`.
15871635
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15881636
pub struct GccCodegenBackend {
15891637
compilers: RustcPrivateCompilers,
1638+
targets: Vec<TargetSelection>,
1639+
}
1640+
1641+
impl GccCodegenBackend {
1642+
/// Create a cg_gcc backend that can compile code for all targets for which we build the
1643+
/// standard library.
1644+
pub fn for_all_std_targets(builder: &Builder<'_>, compilers: RustcPrivateCompilers) -> Self {
1645+
let mut targets = builder.targets.clone();
1646+
// Sort targets to improve step cache hits
1647+
targets.sort();
1648+
Self { compilers, targets }
1649+
}
15901650
}
15911651

15921652
impl Step for GccCodegenBackend {
@@ -1599,9 +1659,10 @@ impl Step for GccCodegenBackend {
15991659
}
16001660

16011661
fn make_run(run: RunConfig<'_>) {
1602-
run.builder.ensure(GccCodegenBackend {
1603-
compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1604-
});
1662+
run.builder.ensure(GccCodegenBackend::for_all_std_targets(
1663+
run.builder,
1664+
RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1665+
));
16051666
}
16061667

16071668
fn run(self, builder: &Builder<'_>) -> Self::Output {
@@ -1615,7 +1676,13 @@ impl Step for GccCodegenBackend {
16151676
&CodegenBackendKind::Gcc,
16161677
);
16171678

1618-
let gcc = builder.ensure(Gcc { target });
1679+
let dylib_set = GccDylibSet {
1680+
dylibs: self
1681+
.targets
1682+
.iter()
1683+
.map(|&target| (target, builder.ensure(Gcc { target })))
1684+
.collect(),
1685+
};
16191686

16201687
if builder.config.keep_stage.contains(&build_compiler.stage) {
16211688
trace!("`keep-stage` requested");
@@ -1625,7 +1692,7 @@ impl Step for GccCodegenBackend {
16251692
);
16261693
// Codegen backends are linked separately from this step today, so we don't do
16271694
// anything here.
1628-
return GccCodegenBackendOutput { stamp, gcc };
1695+
return GccCodegenBackendOutput { stamp, dylib_set };
16291696
}
16301697

16311698
let mut cargo = builder::Cargo::new(
@@ -1639,15 +1706,21 @@ impl Step for GccCodegenBackend {
16391706
cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
16401707
rustc_cargo_env(builder, &mut cargo, target);
16411708

1642-
add_cg_gcc_cargo_flags(&mut cargo, &gcc);
1709+
add_cg_gcc_cargo_flags(
1710+
&mut cargo,
1711+
&dylib_set
1712+
.dylibs
1713+
.get(&build_compiler.host)
1714+
.expect("libgccjit was not build for the host target"),
1715+
);
16431716

16441717
let _guard =
16451718
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target);
16461719
let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
16471720

16481721
GccCodegenBackendOutput {
16491722
stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()),
1650-
gcc,
1723+
dylib_set,
16511724
}
16521725
}
16531726

@@ -2324,12 +2397,14 @@ impl Step for Assemble {
23242397
copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
23252398
}
23262399
CodegenBackendKind::Gcc => {
2327-
let output =
2328-
builder.ensure(GccCodegenBackend { compilers: prepare_compilers() });
2400+
let output = builder.ensure(GccCodegenBackend::for_all_std_targets(
2401+
builder,
2402+
prepare_compilers(),
2403+
));
23292404
copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler);
2330-
// Also copy libgccjit to the library sysroot, so that it is available for
2331-
// the codegen backend.
2332-
output.gcc.install_to(builder, &rustc_libdir);
2405+
// Also copy all requires libgccjit dylibs to the corresponding
2406+
// library sysroots, so that they are available for the codegen backend.
2407+
output.dylib_set.install_to(builder, target_compiler);
23332408
}
23342409
CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
23352410
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2857,7 +2857,7 @@ impl Step for Gcc {
28572857
fn run(self, builder: &Builder<'_>) -> Self::Output {
28582858
let tarball = Tarball::new(builder, "gcc", &self.target.triple);
28592859
let output = builder.ensure(super::gcc::Gcc { target: self.target });
2860-
tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
2860+
tarball.add_file(&output.libgccjit(), "lib", FileType::NativeLibrary);
28612861
tarball.generate()
28622862
}
28632863

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

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

15-
use crate::FileType;
1615
use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
1716
use crate::core::config::TargetSelection;
1817
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
@@ -27,63 +26,12 @@ pub struct Gcc {
2726
#[derive(Clone)]
2827
pub struct GccOutput {
2928
/// Path to a built or downloaded libgccjit.
30-
pub libgccjit: PathBuf,
31-
target: TargetSelection,
29+
libgccjit: PathBuf,
3230
}
3331

3432
impl GccOutput {
35-
/// Install the required libgccjit library file(s) to the specified `path`.
36-
pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) {
37-
if builder.config.dry_run() {
38-
return;
39-
}
40-
41-
let target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string();
42-
43-
// If we build libgccjit ourselves, then `self.libgccjit` can actually be a symlink.
44-
// In that case, we have to resolve it first, otherwise we'd create a symlink to a symlink,
45-
// which wouldn't work.
46-
let actual_libgccjit_path = t!(
47-
self.libgccjit.canonicalize(),
48-
format!("Cannot find libgccjit at {}", self.libgccjit.display())
49-
);
50-
51-
let dest_dir = directory.join("rustlib").join(self.target).join("lib");
52-
t!(fs::create_dir_all(&dest_dir));
53-
let dst = dest_dir.join(target_filename);
54-
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
55-
56-
if let Some(ref path) = builder.config.libgccjit_libs_dir {
57-
let host_target = builder.config.host_target.triple;
58-
59-
let source = path.join(host_target);
60-
let dst = directory;
61-
62-
let targets = builder
63-
.config
64-
.targets
65-
.iter()
66-
.map(|target| target.triple)
67-
.chain(std::iter::once(host_target));
68-
69-
let target_filename = "libgccjit.so";
70-
for target in targets {
71-
let source = source.join(target).join(target_filename);
72-
// To support symlinks in libgccjit-libs-dir, we have to resolve it first,
73-
// otherwise we'd create a symlink to a symlink, which wouldn't work.
74-
let actual_libgccjit_path = t!(
75-
source.canonicalize(),
76-
format!("Cannot find libgccjit at {}", source.display())
77-
);
78-
let target_dir = dst.join("rustlib").join(target).join("lib");
79-
t!(
80-
std::fs::create_dir_all(&target_dir),
81-
format!("Cannot create target dir {} for libgccjit", target_dir.display())
82-
);
83-
let dst = target_dir.join(target_filename);
84-
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
85-
}
86-
}
33+
pub fn libgccjit(&self) -> &Path {
34+
&self.libgccjit
8735
}
8836
}
8937

@@ -106,7 +54,7 @@ impl Step for Gcc {
10654

10755
// If GCC has already been built, we avoid building it again.
10856
let metadata = match get_gcc_build_status(builder, target) {
109-
GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path, target },
57+
GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path },
11058
GccBuildStatus::ShouldBuild(m) => m,
11159
};
11260

@@ -116,14 +64,14 @@ impl Step for Gcc {
11664

11765
let libgccjit_path = libgccjit_built_path(&metadata.install_dir);
11866
if builder.config.dry_run() {
119-
return GccOutput { libgccjit: libgccjit_path, target };
67+
return GccOutput { libgccjit: libgccjit_path };
12068
}
12169

12270
build_gcc(&metadata, builder, target);
12371

12472
t!(metadata.stamp.write());
12573

126-
GccOutput { libgccjit: libgccjit_path, target }
74+
GccOutput { libgccjit: libgccjit_path }
12775
}
12876
}
12977

src/bootstrap/src/core/build_steps/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3960,7 +3960,7 @@ impl Step for CodegenGCC {
39603960
.arg("--use-backend")
39613961
.arg("gcc")
39623962
.arg("--gcc-path")
3963-
.arg(gcc.libgccjit.parent().unwrap())
3963+
.arg(gcc.libgccjit().parent().unwrap())
39643964
.arg("--out-dir")
39653965
.arg(builder.stage_out(compilers.build_compiler(), Mode::Codegen).join("cg_gcc"))
39663966
.arg("--release")

0 commit comments

Comments
 (0)