Skip to content

Commit 3392548

Browse files
authored
Rollup merge of #149354 - antoyo:bootstrap-config/libgccjit-libs-dir, r=Kobzol
Bootstrap config: libgccjit libs dir r? `@Kobzol`
2 parents 53f9ff7 + b98a91e commit 3392548

File tree

11 files changed

+369
-82
lines changed

11 files changed

+369
-82
lines changed

bootstrap.example.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,31 @@
191191
# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
192192
#gcc.download-ci-gcc = false
193193

194+
# Provide a directory of prebuilt libgccjit.so dylibs for given (host, target) compilation pairs.
195+
# This is useful when you want to cross-compile `rustc` to another target since GCC is not a
196+
# multi-target compiler.
197+
# You have to use a directory structure that looks like this:
198+
# `<libgccjit-libs-dir>/<host>/<target>/libgccjit.so`.
199+
# For example:
200+
#
201+
# ```
202+
# <libgccjit-libs-dir>
203+
# ├── m68k-unknown-linux-gnu
204+
# │ └── m68k-unknown-linux-gnu
205+
# │ └── libgccjit.so
206+
# └── x86_64-unknown-linux-gnu
207+
# ├── m68k-unknown-linux-gnu
208+
# │ └── libgccjit.so
209+
# └── x86_64-unknown-linux-gnu
210+
# └── libgccjit.so
211+
# ```
212+
# The directory above would allow you to cross-compile rustc from x64 to m68k
213+
#
214+
# Note that this option has priority over `gcc.download-ci-gcc`.
215+
# If you set both, bootstrap will first try to load libgccjit.so from this directory.
216+
# Only if it isn't found, it will try to download it from CI or build it locally.
217+
#gcc.libgccjit-libs-dir = "/path/to/libgccjit-libs-dir"
218+
194219
# =============================================================================
195220
# General build configuration options
196221
# =============================================================================

compiler/rustc_codegen_gcc/src/lib.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ use rustc_middle::ty::TyCtxt;
9898
use rustc_middle::util::Providers;
9999
use rustc_session::Session;
100100
use rustc_session::config::{OptLevel, OutputFilenames};
101-
use rustc_session::filesearch::make_target_lib_path;
102101
use rustc_span::Symbol;
103102
use rustc_target::spec::{Arch, RelocModel};
104103
use tempfile::TempDir;
@@ -207,18 +206,38 @@ impl CodegenBackend for GccCodegenBackend {
207206
}
208207

209208
fn init(&self, sess: &Session) {
209+
fn file_path(sysroot_path: &Path, sess: &Session) -> PathBuf {
210+
let rustlib_path =
211+
rustc_target::relative_target_rustlib_path(sysroot_path, &sess.host.llvm_target);
212+
sysroot_path
213+
.join(rustlib_path)
214+
.join("codegen-backends")
215+
.join("lib")
216+
.join(sess.target.llvm_target.as_ref())
217+
.join("libgccjit.so")
218+
}
219+
210220
// We use all_paths() instead of only path() in case the path specified by --sysroot is
211221
// invalid.
212222
// This is the case for instance in Rust for Linux where they specify --sysroot=/dev/null.
213223
for path in sess.opts.sysroot.all_paths() {
214-
let libgccjit_target_lib_file =
215-
make_target_lib_path(path, &sess.target.llvm_target).join("libgccjit.so");
224+
let libgccjit_target_lib_file = file_path(path, sess);
216225
if let Ok(true) = fs::exists(&libgccjit_target_lib_file) {
217226
load_libgccjit_if_needed(&libgccjit_target_lib_file);
218227
break;
219228
}
220229
}
221230

231+
if !gccjit::is_loaded() {
232+
let mut paths = vec![];
233+
for path in sess.opts.sysroot.all_paths() {
234+
let libgccjit_target_lib_file = file_path(path, sess);
235+
paths.push(libgccjit_target_lib_file);
236+
}
237+
238+
panic!("Could not load libgccjit.so. Attempted paths: {:#?}", paths);
239+
}
240+
222241
#[cfg(feature = "master")]
223242
{
224243
let target_cpu = target_cpu(sess);

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

Lines changed: 166 additions & 21 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::{HashMap, HashSet};
10+
use std::collections::{BTreeMap, HashMap, HashSet};
1111
use std::ffi::OsStr;
1212
use std::io::BufReader;
1313
use std::io::prelude::*;
@@ -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, add_cg_gcc_cargo_flags};
22+
use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair, add_cg_gcc_cargo_flags};
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;
@@ -1563,17 +1563,98 @@ impl Step for RustcLink {
15631563
}
15641564
}
15651565

1566+
/// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets.
1567+
#[derive(Clone)]
1568+
pub struct GccDylibSet {
1569+
dylibs: BTreeMap<GccTargetPair, GccOutput>,
1570+
host_pair: GccTargetPair,
1571+
}
1572+
1573+
impl GccDylibSet {
1574+
/// Returns the libgccjit.so dylib that corresponds to a host target on which `cg_gcc` will be
1575+
/// executed, and which will target the host. So e.g. if `cg_gcc` will be executed on
1576+
/// x86_64-unknown-linux-gnu, the host dylib will be for compilation pair
1577+
/// `(x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu)`.
1578+
fn host_dylib(&self) -> &GccOutput {
1579+
self.dylibs.get(&self.host_pair).unwrap_or_else(|| {
1580+
panic!("libgccjit.so was not built for host target {}", self.host_pair)
1581+
})
1582+
}
1583+
1584+
/// Install the libgccjit dylibs to the corresponding target directories of the given compiler.
1585+
/// cg_gcc know how to search for the libgccjit dylibs in these directories, according to the
1586+
/// (host, target) pair that is being compiled by rustc and cg_gcc.
1587+
pub fn install_to(&self, builder: &Builder<'_>, compiler: Compiler) {
1588+
if builder.config.dry_run() {
1589+
return;
1590+
}
1591+
1592+
// <rustc>/lib/<host-target>/codegen-backends
1593+
let cg_sysroot = builder.sysroot_codegen_backends(compiler);
1594+
1595+
for (target_pair, libgccjit) in &self.dylibs {
1596+
assert_eq!(
1597+
target_pair.host(),
1598+
compiler.host,
1599+
"Trying to install libgccjit ({target_pair}) to a compiler with a different host ({})",
1600+
compiler.host
1601+
);
1602+
let libgccjit = libgccjit.libgccjit();
1603+
let target_filename = libgccjit.file_name().unwrap().to_str().unwrap();
1604+
1605+
// If we build libgccjit ourselves, then `libgccjit` can actually be a symlink.
1606+
// In that case, we have to resolve it first, otherwise we'd create a symlink to a
1607+
// symlink, which wouldn't work.
1608+
let actual_libgccjit_path = t!(
1609+
libgccjit.canonicalize(),
1610+
format!("Cannot find libgccjit at {}", libgccjit.display())
1611+
);
1612+
1613+
// <cg-sysroot>/lib/<target>/libgccjit.so
1614+
let dest_dir = cg_sysroot.join("lib").join(target_pair.target());
1615+
t!(fs::create_dir_all(&dest_dir));
1616+
let dst = dest_dir.join(target_filename);
1617+
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
1618+
}
1619+
}
1620+
}
1621+
15661622
/// Output of the `compile::GccCodegenBackend` step.
1567-
/// It includes the path to the libgccjit library on which this backend depends.
1623+
///
1624+
/// It contains paths to all built libgccjit libraries on which this backend depends here.
15681625
#[derive(Clone)]
15691626
pub struct GccCodegenBackendOutput {
15701627
stamp: BuildStamp,
1571-
gcc: GccOutput,
1628+
dylib_set: GccDylibSet,
15721629
}
15731630

1631+
/// Builds the GCC codegen backend (`cg_gcc`).
1632+
/// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each
1633+
/// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64,
1634+
/// you will need at least:
1635+
/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros)
1636+
/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code)
1637+
///
1638+
/// We model this by having a single cg_gcc for a given host target, which contains one
1639+
/// libgccjit per (host, target) pair.
1640+
/// Note that the host target is taken from `self.compilers.target_compiler.host`.
15741641
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15751642
pub struct GccCodegenBackend {
15761643
compilers: RustcPrivateCompilers,
1644+
targets: Vec<TargetSelection>,
1645+
}
1646+
1647+
impl GccCodegenBackend {
1648+
/// Build `cg_gcc` that will run on host `H` (`compilers.target_compiler.host`) and will be
1649+
/// able to produce code target pairs (`H`, `T`) for all `T` from `targets`.
1650+
pub fn for_targets(
1651+
compilers: RustcPrivateCompilers,
1652+
mut targets: Vec<TargetSelection>,
1653+
) -> Self {
1654+
// Sort targets to improve step cache hits
1655+
targets.sort();
1656+
Self { compilers, targets }
1657+
}
15771658
}
15781659

15791660
impl Step for GccCodegenBackend {
@@ -1586,23 +1667,34 @@ impl Step for GccCodegenBackend {
15861667
}
15871668

15881669
fn make_run(run: RunConfig<'_>) {
1589-
run.builder.ensure(GccCodegenBackend {
1590-
compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
1591-
});
1670+
// By default, build cg_gcc that will only be able to compile native code for the given
1671+
// host target.
1672+
let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target);
1673+
run.builder.ensure(GccCodegenBackend { compilers, targets: vec![run.target] });
15921674
}
15931675

15941676
fn run(self, builder: &Builder<'_>) -> Self::Output {
1595-
let target = self.compilers.target();
1677+
let host = self.compilers.target();
15961678
let build_compiler = self.compilers.build_compiler();
15971679

15981680
let stamp = build_stamp::codegen_backend_stamp(
15991681
builder,
16001682
build_compiler,
1601-
target,
1683+
host,
16021684
&CodegenBackendKind::Gcc,
16031685
);
16041686

1605-
let gcc = builder.ensure(Gcc { target });
1687+
let dylib_set = GccDylibSet {
1688+
dylibs: self
1689+
.targets
1690+
.iter()
1691+
.map(|&target| {
1692+
let target_pair = GccTargetPair::for_target_pair(host, target);
1693+
(target_pair, builder.ensure(Gcc { target_pair }))
1694+
})
1695+
.collect(),
1696+
host_pair: GccTargetPair::for_native_build(host),
1697+
};
16061698

16071699
if builder.config.keep_stage.contains(&build_compiler.stage) {
16081700
trace!("`keep-stage` requested");
@@ -1612,29 +1704,29 @@ impl Step for GccCodegenBackend {
16121704
);
16131705
// Codegen backends are linked separately from this step today, so we don't do
16141706
// anything here.
1615-
return GccCodegenBackendOutput { stamp, gcc };
1707+
return GccCodegenBackendOutput { stamp, dylib_set };
16161708
}
16171709

16181710
let mut cargo = builder::Cargo::new(
16191711
builder,
16201712
build_compiler,
16211713
Mode::Codegen,
16221714
SourceType::InTree,
1623-
target,
1715+
host,
16241716
Kind::Build,
16251717
);
16261718
cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
1627-
rustc_cargo_env(builder, &mut cargo, target);
1719+
rustc_cargo_env(builder, &mut cargo, host);
16281720

1629-
add_cg_gcc_cargo_flags(&mut cargo, &gcc);
1721+
add_cg_gcc_cargo_flags(&mut cargo, dylib_set.host_dylib());
16301722

16311723
let _guard =
1632-
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target);
1724+
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host);
16331725
let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
16341726

16351727
GccCodegenBackendOutput {
16361728
stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()),
1637-
gcc,
1729+
dylib_set,
16381730
}
16391731
}
16401732

@@ -2311,12 +2403,65 @@ impl Step for Assemble {
23112403
copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
23122404
}
23132405
CodegenBackendKind::Gcc => {
2314-
let output =
2315-
builder.ensure(GccCodegenBackend { compilers: prepare_compilers() });
2406+
// We need to build cg_gcc for the host target of the compiler which we
2407+
// build here, which is `target_compiler`.
2408+
// But we also need to build libgccjit for some additional targets, in
2409+
// the most general case.
2410+
// 1. We need to build (target_compiler.host, stdlib target) libgccjit
2411+
// for all stdlibs that we build, so that cg_gcc can be used to build code
2412+
// for all those targets.
2413+
// 2. We need to build (target_compiler.host, target_compiler.host)
2414+
// libgccjit, so that the target compiler can compile host code (e.g. proc
2415+
// macros).
2416+
// 3. We need to build (target_compiler.host, host target) libgccjit
2417+
// for all *host targets* that we build, so that cg_gcc can be used to
2418+
// build a (possibly cross-compiled) stage 2+ rustc.
2419+
//
2420+
// Assume that we are on host T1 and we do a stage2 build of rustc for T2.
2421+
// We want the T2 rustc compiler to be able to use cg_gcc and build code
2422+
// for T2 (host) and T3 (target). We also want to build the stage2 compiler
2423+
// itself using cg_gcc.
2424+
// This could correspond to the following bootstrap invocation:
2425+
// `x build rustc --build T1 --host T2 --target T3 --set codegen-backends=['gcc', 'llvm']`
2426+
//
2427+
// For that, we will need the following GCC target pairs:
2428+
// 1. T1 -> T2 (to cross-compile a T2 rustc using cg_gcc running on T1)
2429+
// 2. T2 -> T2 (to build host code with the stage 2 rustc running on T2)
2430+
// 3. T2 -> T3 (to cross-compile code with the stage 2 rustc running on T2)
2431+
//
2432+
// FIXME: this set of targets is *maximal*, in reality we might need
2433+
// less libgccjits at this current build stage. Try to reduce the set of
2434+
// GCC dylibs built below by taking a look at the current stage and whether
2435+
// cg_gcc is used as the default codegen backend.
2436+
2437+
let compilers = prepare_compilers();
2438+
2439+
// The left side of the target pairs below is implied. It has to match the
2440+
// host target on which cg_gcc will run, which is the host target of
2441+
// `target_compiler`. We only pass the right side of the target pairs to
2442+
// the `GccCodegenBackend` constructor.
2443+
let mut targets = HashSet::new();
2444+
// Add all host targets, so that we are able to build host code in this
2445+
// bootstrap invocation using cg_gcc.
2446+
for target in &builder.hosts {
2447+
targets.insert(*target);
2448+
}
2449+
// Add all stdlib targets, so that the built rustc can produce code for them
2450+
for target in &builder.targets {
2451+
targets.insert(*target);
2452+
}
2453+
// Add the host target of the built rustc itself, so that it can build
2454+
// host code (e.g. proc macros) using cg_gcc.
2455+
targets.insert(compilers.target_compiler().host);
2456+
2457+
let output = builder.ensure(GccCodegenBackend::for_targets(
2458+
compilers,
2459+
targets.into_iter().collect(),
2460+
));
23162461
copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler);
2317-
// Also copy libgccjit to the library sysroot, so that it is available for
2318-
// the codegen backend.
2319-
output.gcc.install_to(builder, &rustc_libdir);
2462+
// Also copy all requires libgccjit dylibs to the corresponding
2463+
// library sysroots, so that they are available for the codegen backend.
2464+
output.dylib_set.install_to(builder, target_compiler);
23202465
}
23212466
CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
23222467
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use tracing::instrument;
2121

2222
use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name};
2323
use crate::core::build_steps::doc::DocumentationFormat;
24+
use crate::core::build_steps::gcc::GccTargetPair;
2425
use crate::core::build_steps::tool::{
2526
self, RustcPrivateCompilers, ToolTargetBuildMode, get_tool_target_compiler,
2627
};
@@ -2856,8 +2857,9 @@ impl Step for Gcc {
28562857

28572858
fn run(self, builder: &Builder<'_>) -> Self::Output {
28582859
let tarball = Tarball::new(builder, "gcc", &self.target.triple);
2859-
let output = builder.ensure(super::gcc::Gcc { target: self.target });
2860-
tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
2860+
let output = builder
2861+
.ensure(super::gcc::Gcc { target_pair: GccTargetPair::for_native_build(self.target) });
2862+
tarball.add_file(output.libgccjit(), "lib", FileType::NativeLibrary);
28612863
tarball.generate()
28622864
}
28632865

0 commit comments

Comments
 (0)