Skip to content

Commit 3a627bb

Browse files
Auto merge of #150541 - Kobzol:dist-gcc-component, r=<try>
Add a dist component for libgccjit
2 parents 629b092 + f7682ee commit 3a627bb

File tree

8 files changed

+300
-98
lines changed

8 files changed

+300
-98
lines changed

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

Lines changed: 72 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use serde_derive::Deserialize;
1919
#[cfg(feature = "tracing")]
2020
use tracing::span;
2121

22-
use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair, add_cg_gcc_cargo_flags};
22+
use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair};
2323
use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts};
2424
use crate::core::build_steps::{dist, llvm};
2525
use crate::core::builder;
@@ -1569,21 +1569,29 @@ impl Step for RustcLink {
15691569
}
15701570

15711571
/// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets.
1572+
/// `libgccjit` requires a separate build for each `(host, target)` pair.
1573+
/// So if you are on linux-x64 and build for linux-aarch64, you will need at least:
1574+
/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros)
1575+
/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code)
15721576
#[derive(Clone)]
15731577
pub struct GccDylibSet {
15741578
dylibs: BTreeMap<GccTargetPair, GccOutput>,
1575-
host_pair: GccTargetPair,
15761579
}
15771580

15781581
impl GccDylibSet {
1579-
/// Returns the libgccjit.so dylib that corresponds to a host target on which `cg_gcc` will be
1580-
/// executed, and which will target the host. So e.g. if `cg_gcc` will be executed on
1581-
/// x86_64-unknown-linux-gnu, the host dylib will be for compilation pair
1582-
/// `(x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu)`.
1583-
fn host_dylib(&self) -> &GccOutput {
1584-
self.dylibs.get(&self.host_pair).unwrap_or_else(|| {
1585-
panic!("libgccjit.so was not built for host target {}", self.host_pair)
1586-
})
1582+
/// Build a set of libgccjit dylibs that will be executed on `host` and will generate code for
1583+
/// each specified target.
1584+
pub fn build(
1585+
builder: &Builder<'_>,
1586+
host: TargetSelection,
1587+
targets: Vec<TargetSelection>,
1588+
) -> Self {
1589+
let dylibs = targets
1590+
.iter()
1591+
.map(|t| GccTargetPair::for_target_pair(host, *t))
1592+
.map(|target_pair| (target_pair, builder.ensure(Gcc { target_pair })))
1593+
.collect();
1594+
Self { dylibs }
15871595
}
15881596

15891597
/// Install the libgccjit dylibs to the corresponding target directories of the given compiler.
@@ -1604,61 +1612,65 @@ impl GccDylibSet {
16041612
"Trying to install libgccjit ({target_pair}) to a compiler with a different host ({})",
16051613
compiler.host
16061614
);
1607-
let libgccjit = libgccjit.libgccjit();
1608-
let target_filename = libgccjit.file_name().unwrap().to_str().unwrap();
1615+
let libgccjit_path = libgccjit.libgccjit();
16091616

16101617
// If we build libgccjit ourselves, then `libgccjit` can actually be a symlink.
16111618
// In that case, we have to resolve it first, otherwise we'd create a symlink to a
16121619
// symlink, which wouldn't work.
1613-
let actual_libgccjit_path = t!(
1614-
libgccjit.canonicalize(),
1615-
format!("Cannot find libgccjit at {}", libgccjit.display())
1620+
let libgccjit_path = t!(
1621+
libgccjit_path.canonicalize(),
1622+
format!("Cannot find libgccjit at {}", libgccjit_path.display())
16161623
);
16171624

1618-
// <cg-sysroot>/lib/<target>/libgccjit.so
1619-
let dest_dir = cg_sysroot.join("lib").join(target_pair.target());
1620-
t!(fs::create_dir_all(&dest_dir));
1621-
let dst = dest_dir.join(target_filename);
1622-
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
1625+
let dst = cg_sysroot.join(libgccjit_path_relative_to_cg_dir(target_pair, libgccjit));
1626+
t!(std::fs::create_dir_all(dst.parent().unwrap()));
1627+
builder.copy_link(&libgccjit_path, &dst, FileType::NativeLibrary);
16231628
}
16241629
}
16251630
}
16261631

1632+
/// Returns a path where libgccjit.so should be stored, **relative** to the
1633+
/// **codegen backend directory**.
1634+
pub fn libgccjit_path_relative_to_cg_dir(
1635+
target_pair: &GccTargetPair,
1636+
libgccjit: &GccOutput,
1637+
) -> PathBuf {
1638+
let target_filename = libgccjit.libgccjit().file_name().unwrap().to_str().unwrap();
1639+
1640+
// <cg-dir>/lib/<target>/libgccjit.so
1641+
Path::new("lib").join(target_pair.target()).join(target_filename)
1642+
}
1643+
16271644
/// Output of the `compile::GccCodegenBackend` step.
16281645
///
1629-
/// It contains paths to all built libgccjit libraries on which this backend depends here.
1646+
/// It contains a build stamp with the path to the built cg_gcc dylib.
16301647
#[derive(Clone)]
16311648
pub struct GccCodegenBackendOutput {
16321649
stamp: BuildStamp,
1633-
dylib_set: GccDylibSet,
1650+
}
1651+
1652+
impl GccCodegenBackendOutput {
1653+
pub fn stamp(&self) -> &BuildStamp {
1654+
&self.stamp
1655+
}
16341656
}
16351657

16361658
/// Builds the GCC codegen backend (`cg_gcc`).
1637-
/// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each
1638-
/// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64,
1639-
/// you will need at least:
1640-
/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros)
1641-
/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code)
1642-
///
1643-
/// We model this by having a single cg_gcc for a given host target, which contains one
1644-
/// libgccjit per (host, target) pair.
1645-
/// Note that the host target is taken from `self.compilers.target_compiler.host`.
1659+
/// Note that this **does not** build libgccjit, which is a dependency of cg_gcc.
1660+
/// That has to be built separately, because a separate copy of libgccjit is required
1661+
/// for each (host, target) compilation pair.
1662+
/// cg_gcc goes to great lengths to ensure that it does not *directly* link to libgccjit,
1663+
/// so we respect that here and allow building cg_gcc without building libgccjit itself.
16461664
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16471665
pub struct GccCodegenBackend {
16481666
compilers: RustcPrivateCompilers,
1649-
targets: Vec<TargetSelection>,
1667+
target: TargetSelection,
16501668
}
16511669

16521670
impl GccCodegenBackend {
1653-
/// Build `cg_gcc` that will run on host `H` (`compilers.target_compiler.host`) and will be
1654-
/// able to produce code target pairs (`H`, `T`) for all `T` from `targets`.
1655-
pub fn for_targets(
1656-
compilers: RustcPrivateCompilers,
1657-
mut targets: Vec<TargetSelection>,
1658-
) -> Self {
1659-
// Sort targets to improve step cache hits
1660-
targets.sort();
1661-
Self { compilers, targets }
1671+
/// Build `cg_gcc` that will run on the given host target.
1672+
pub fn for_target(compilers: RustcPrivateCompilers, target: TargetSelection) -> Self {
1673+
Self { compilers, target }
16621674
}
16631675
}
16641676

@@ -1672,10 +1684,8 @@ impl Step for GccCodegenBackend {
16721684
}
16731685

16741686
fn make_run(run: RunConfig<'_>) {
1675-
// By default, build cg_gcc that will only be able to compile native code for the given
1676-
// host target.
16771687
let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target);
1678-
run.builder.ensure(GccCodegenBackend { compilers, targets: vec![run.target] });
1688+
run.builder.ensure(GccCodegenBackend::for_target(compilers, run.target));
16791689
}
16801690

16811691
fn run(self, builder: &Builder<'_>) -> Self::Output {
@@ -1689,18 +1699,6 @@ impl Step for GccCodegenBackend {
16891699
&CodegenBackendKind::Gcc,
16901700
);
16911701

1692-
let dylib_set = GccDylibSet {
1693-
dylibs: self
1694-
.targets
1695-
.iter()
1696-
.map(|&target| {
1697-
let target_pair = GccTargetPair::for_target_pair(host, target);
1698-
(target_pair, builder.ensure(Gcc { target_pair }))
1699-
})
1700-
.collect(),
1701-
host_pair: GccTargetPair::for_native_build(host),
1702-
};
1703-
17041702
if builder.config.keep_stage.contains(&build_compiler.stage) {
17051703
trace!("`keep-stage` requested");
17061704
builder.info(
@@ -1709,7 +1707,7 @@ impl Step for GccCodegenBackend {
17091707
);
17101708
// Codegen backends are linked separately from this step today, so we don't do
17111709
// anything here.
1712-
return GccCodegenBackendOutput { stamp, dylib_set };
1710+
return GccCodegenBackendOutput { stamp };
17131711
}
17141712

17151713
let mut cargo = builder::Cargo::new(
@@ -1723,15 +1721,12 @@ impl Step for GccCodegenBackend {
17231721
cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
17241722
rustc_cargo_env(builder, &mut cargo, host);
17251723

1726-
add_cg_gcc_cargo_flags(&mut cargo, dylib_set.host_dylib());
1727-
17281724
let _guard =
17291725
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host);
17301726
let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
17311727

17321728
GccCodegenBackendOutput {
17331729
stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()),
1734-
dylib_set,
17351730
}
17361731
}
17371732

@@ -2457,12 +2452,18 @@ impl Step for Assemble {
24572452
// GCC dylibs built below by taking a look at the current stage and whether
24582453
// cg_gcc is used as the default codegen backend.
24592454

2455+
// First, the easy part: build cg_gcc
24602456
let compilers = prepare_compilers();
2457+
let cg_gcc = builder
2458+
.ensure(GccCodegenBackend::for_target(compilers, target_compiler.host));
2459+
copy_codegen_backends_to_sysroot(builder, cg_gcc.stamp, target_compiler);
2460+
2461+
// Then, the hard part: prepare all required libgccjit dylibs.
24612462

24622463
// The left side of the target pairs below is implied. It has to match the
2463-
// host target on which cg_gcc will run, which is the host target of
2464+
// host target on which libgccjit will be used, which is the host target of
24642465
// `target_compiler`. We only pass the right side of the target pairs to
2465-
// the `GccCodegenBackend` constructor.
2466+
// the `GccDylibSet` constructor.
24662467
let mut targets = HashSet::new();
24672468
// Add all host targets, so that we are able to build host code in this
24682469
// bootstrap invocation using cg_gcc.
@@ -2477,14 +2478,16 @@ impl Step for Assemble {
24772478
// host code (e.g. proc macros) using cg_gcc.
24782479
targets.insert(compilers.target_compiler().host);
24792480

2480-
let output = builder.ensure(GccCodegenBackend::for_targets(
2481-
compilers,
2481+
// Now build all the required libgccjit dylibs
2482+
let dylib_set = GccDylibSet::build(
2483+
builder,
2484+
compilers.target_compiler().host,
24822485
targets.into_iter().collect(),
2483-
));
2484-
copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler);
2485-
// Also copy all requires libgccjit dylibs to the corresponding
2486-
// library sysroots, so that they are available for the codegen backend.
2487-
output.dylib_set.install_to(builder, target_compiler);
2486+
);
2487+
2488+
// And then copy all the dylibs to the corresponding
2489+
// library sysroots, so that they are available for cg_gcc.
2490+
dylib_set.install_to(builder, target_compiler);
24882491
}
24892492
CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
24902493
}

0 commit comments

Comments
 (0)