Add libgccjit-libs-dir config
This commit is contained in:
parent
08de25c4ea
commit
95c38b2bba
8 changed files with 313 additions and 79 deletions
|
|
@ -191,6 +191,31 @@
|
|||
# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
|
||||
#gcc.download-ci-gcc = false
|
||||
|
||||
# Provide a directory of prebuilt libgccjit.so dylibs for given (host, target) compilation pairs.
|
||||
# This is useful when you want to cross-compile `rustc` to another target since GCC is not a
|
||||
# multi-target compiler.
|
||||
# You have to use a directory structure that looks like this:
|
||||
# `<libgccjit-libs-dir>/<host>/<target>/libgccjit.so`.
|
||||
# For example:
|
||||
#
|
||||
# ```
|
||||
# <libgccjit-libs-dir>
|
||||
# ├── m68k-unknown-linux-gnu
|
||||
# │ └── m68k-unknown-linux-gnu
|
||||
# │ └── libgccjit.so
|
||||
# └── x86_64-unknown-linux-gnu
|
||||
# ├── m68k-unknown-linux-gnu
|
||||
# │ └── libgccjit.so
|
||||
# └── x86_64-unknown-linux-gnu
|
||||
# └── libgccjit.so
|
||||
# ```
|
||||
# The directory above would allow you to cross-compile rustc from x64 to m68k
|
||||
#
|
||||
# Note that this option has priority over `gcc.download-ci-gcc`.
|
||||
# If you set both, bootstrap will first try to load libgccjit.so from this directory.
|
||||
# Only if it isn't found, it will try to download it from CI or build it locally.
|
||||
#gcc.libgccjit-libs-dir = "/path/to/libgccjit-libs-dir"
|
||||
|
||||
# =============================================================================
|
||||
# General build configuration options
|
||||
# =============================================================================
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
//! goes along from the output of the previous stage.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::io::BufReader;
|
||||
use std::io::prelude::*;
|
||||
|
|
@ -19,7 +19,7 @@ use serde_derive::Deserialize;
|
|||
#[cfg(feature = "tracing")]
|
||||
use tracing::span;
|
||||
|
||||
use crate::core::build_steps::gcc::{Gcc, GccOutput, add_cg_gcc_cargo_flags};
|
||||
use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair, add_cg_gcc_cargo_flags};
|
||||
use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts};
|
||||
use crate::core::build_steps::{dist, llvm};
|
||||
use crate::core::builder;
|
||||
|
|
@ -1576,17 +1576,98 @@ impl Step for RustcLink {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets.
|
||||
#[derive(Clone)]
|
||||
pub struct GccDylibSet {
|
||||
dylibs: BTreeMap<GccTargetPair, GccOutput>,
|
||||
host_pair: GccTargetPair,
|
||||
}
|
||||
|
||||
impl GccDylibSet {
|
||||
/// Returns the libgccjit.so dylib that corresponds to a host target on which `cg_gcc` will be
|
||||
/// executed, and which will target the host. So e.g. if `cg_gcc` will be executed on
|
||||
/// x86_64-unknown-linux-gnu, the host dylib will be for compilation pair
|
||||
/// `(x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu)`.
|
||||
fn host_dylib(&self) -> &GccOutput {
|
||||
self.dylibs.get(&self.host_pair).unwrap_or_else(|| {
|
||||
panic!("libgccjit.so was not built for host target {}", self.host_pair)
|
||||
})
|
||||
}
|
||||
|
||||
/// Install the libgccjit dylibs to the corresponding target directories of the given compiler.
|
||||
/// cg_gcc know how to search for the libgccjit dylibs in these directories, according to the
|
||||
/// (host, target) pair that is being compiled by rustc and cg_gcc.
|
||||
pub fn install_to(&self, builder: &Builder<'_>, compiler: Compiler) {
|
||||
if builder.config.dry_run() {
|
||||
return;
|
||||
}
|
||||
|
||||
// <rustc>/lib/<host-target>/codegen-backends
|
||||
let cg_sysroot = builder.sysroot_codegen_backends(compiler);
|
||||
|
||||
for (target_pair, libgccjit) in &self.dylibs {
|
||||
assert_eq!(
|
||||
target_pair.host(),
|
||||
compiler.host,
|
||||
"Trying to install libgccjit ({target_pair}) to a compiler with a different host ({})",
|
||||
compiler.host
|
||||
);
|
||||
let libgccjit = libgccjit.libgccjit();
|
||||
let target_filename = libgccjit.file_name().unwrap().to_str().unwrap();
|
||||
|
||||
// If we build libgccjit ourselves, then `libgccjit` can actually be a symlink.
|
||||
// In that case, we have to resolve it first, otherwise we'd create a symlink to a
|
||||
// symlink, which wouldn't work.
|
||||
let actual_libgccjit_path = t!(
|
||||
libgccjit.canonicalize(),
|
||||
format!("Cannot find libgccjit at {}", libgccjit.display())
|
||||
);
|
||||
|
||||
// <cg-sysroot>/lib/<target>/libgccjit.so
|
||||
let dest_dir = cg_sysroot.join("lib").join(target_pair.target());
|
||||
t!(fs::create_dir_all(&dest_dir));
|
||||
let dst = dest_dir.join(target_filename);
|
||||
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Output of the `compile::GccCodegenBackend` step.
|
||||
/// It includes the path to the libgccjit library on which this backend depends.
|
||||
///
|
||||
/// It contains paths to all built libgccjit libraries on which this backend depends here.
|
||||
#[derive(Clone)]
|
||||
pub struct GccCodegenBackendOutput {
|
||||
stamp: BuildStamp,
|
||||
gcc: GccOutput,
|
||||
dylib_set: GccDylibSet,
|
||||
}
|
||||
|
||||
/// Builds the GCC codegen backend (`cg_gcc`).
|
||||
/// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each
|
||||
/// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64,
|
||||
/// you will need at least:
|
||||
/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros)
|
||||
/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code)
|
||||
///
|
||||
/// We model this by having a single cg_gcc for a given host target, which contains one
|
||||
/// libgccjit per (host, target) pair.
|
||||
/// Note that the host target is taken from `self.compilers.target_compiler.host`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GccCodegenBackend {
|
||||
compilers: RustcPrivateCompilers,
|
||||
targets: Vec<TargetSelection>,
|
||||
}
|
||||
|
||||
impl GccCodegenBackend {
|
||||
/// Build `cg_gcc` that will run on host `H` (`compilers.target_compiler.host`) and will be
|
||||
/// able to produce code target pairs (`H`, `T`) for all `T` from `targets`.
|
||||
pub fn for_targets(
|
||||
compilers: RustcPrivateCompilers,
|
||||
mut targets: Vec<TargetSelection>,
|
||||
) -> Self {
|
||||
// Sort targets to improve step cache hits
|
||||
targets.sort();
|
||||
Self { compilers, targets }
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for GccCodegenBackend {
|
||||
|
|
@ -1599,23 +1680,34 @@ impl Step for GccCodegenBackend {
|
|||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
run.builder.ensure(GccCodegenBackend {
|
||||
compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target),
|
||||
});
|
||||
// By default, build cg_gcc that will only be able to compile native code for the given
|
||||
// host target.
|
||||
let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target);
|
||||
run.builder.ensure(GccCodegenBackend { compilers, targets: vec![run.target] });
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
||||
let target = self.compilers.target();
|
||||
let host = self.compilers.target();
|
||||
let build_compiler = self.compilers.build_compiler();
|
||||
|
||||
let stamp = build_stamp::codegen_backend_stamp(
|
||||
builder,
|
||||
build_compiler,
|
||||
target,
|
||||
host,
|
||||
&CodegenBackendKind::Gcc,
|
||||
);
|
||||
|
||||
let gcc = builder.ensure(Gcc { target });
|
||||
let dylib_set = GccDylibSet {
|
||||
dylibs: self
|
||||
.targets
|
||||
.iter()
|
||||
.map(|&target| {
|
||||
let target_pair = GccTargetPair::for_target_pair(host, target);
|
||||
(target_pair, builder.ensure(Gcc { target_pair }))
|
||||
})
|
||||
.collect(),
|
||||
host_pair: GccTargetPair::for_native_build(host),
|
||||
};
|
||||
|
||||
if builder.config.keep_stage.contains(&build_compiler.stage) {
|
||||
trace!("`keep-stage` requested");
|
||||
|
|
@ -1625,7 +1717,7 @@ impl Step for GccCodegenBackend {
|
|||
);
|
||||
// Codegen backends are linked separately from this step today, so we don't do
|
||||
// anything here.
|
||||
return GccCodegenBackendOutput { stamp, gcc };
|
||||
return GccCodegenBackendOutput { stamp, dylib_set };
|
||||
}
|
||||
|
||||
let mut cargo = builder::Cargo::new(
|
||||
|
|
@ -1633,21 +1725,21 @@ impl Step for GccCodegenBackend {
|
|||
build_compiler,
|
||||
Mode::Codegen,
|
||||
SourceType::InTree,
|
||||
target,
|
||||
host,
|
||||
Kind::Build,
|
||||
);
|
||||
cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
|
||||
rustc_cargo_env(builder, &mut cargo, target);
|
||||
rustc_cargo_env(builder, &mut cargo, host);
|
||||
|
||||
add_cg_gcc_cargo_flags(&mut cargo, &gcc);
|
||||
add_cg_gcc_cargo_flags(&mut cargo, dylib_set.host_dylib());
|
||||
|
||||
let _guard =
|
||||
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target);
|
||||
builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host);
|
||||
let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
|
||||
|
||||
GccCodegenBackendOutput {
|
||||
stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()),
|
||||
gcc,
|
||||
dylib_set,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2324,12 +2416,65 @@ impl Step for Assemble {
|
|||
copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
|
||||
}
|
||||
CodegenBackendKind::Gcc => {
|
||||
let output =
|
||||
builder.ensure(GccCodegenBackend { compilers: prepare_compilers() });
|
||||
// We need to build cg_gcc for the host target of the compiler which we
|
||||
// build here, which is `target_compiler`.
|
||||
// But we also need to build libgccjit for some additional targets, in
|
||||
// the most general case.
|
||||
// 1. We need to build (target_compiler.host, stdlib target) libgccjit
|
||||
// for all stdlibs that we build, so that cg_gcc can be used to build code
|
||||
// for all those targets.
|
||||
// 2. We need to build (target_compiler.host, target_compiler.host)
|
||||
// libgccjit, so that the target compiler can compile host code (e.g. proc
|
||||
// macros).
|
||||
// 3. We need to build (target_compiler.host, host target) libgccjit
|
||||
// for all *host targets* that we build, so that cg_gcc can be used to
|
||||
// build a (possibly cross-compiled) stage 2+ rustc.
|
||||
//
|
||||
// Assume that we are on host T1 and we do a stage2 build of rustc for T2.
|
||||
// We want the T2 rustc compiler to be able to use cg_gcc and build code
|
||||
// for T2 (host) and T3 (target). We also want to build the stage2 compiler
|
||||
// itself using cg_gcc.
|
||||
// This could correspond to the following bootstrap invocation:
|
||||
// `x build rustc --build T1 --host T2 --target T3 --set codegen-backends=['gcc', 'llvm']`
|
||||
//
|
||||
// For that, we will need the following GCC target pairs:
|
||||
// 1. T1 -> T2 (to cross-compile a T2 rustc using cg_gcc running on T1)
|
||||
// 2. T2 -> T2 (to build host code with the stage 2 rustc running on T2)
|
||||
// 3. T2 -> T3 (to cross-compile code with the stage 2 rustc running on T2)
|
||||
//
|
||||
// FIXME: this set of targets is *maximal*, in reality we might need
|
||||
// less libgccjits at this current build stage. Try to reduce the set of
|
||||
// GCC dylibs built below by taking a look at the current stage and whether
|
||||
// cg_gcc is used as the default codegen backend.
|
||||
|
||||
let compilers = prepare_compilers();
|
||||
|
||||
// The left side of the target pairs below is implied. It has to match the
|
||||
// host target on which cg_gcc will run, which is the host target of
|
||||
// `target_compiler`. We only pass the right side of the target pairs to
|
||||
// the `GccCodegenBackend` constructor.
|
||||
let mut targets = HashSet::new();
|
||||
// Add all host targets, so that we are able to build host code in this
|
||||
// bootstrap invocation using cg_gcc.
|
||||
for target in &builder.hosts {
|
||||
targets.insert(*target);
|
||||
}
|
||||
// Add all stdlib targets, so that the built rustc can produce code for them
|
||||
for target in &builder.targets {
|
||||
targets.insert(*target);
|
||||
}
|
||||
// Add the host target of the built rustc itself, so that it can build
|
||||
// host code (e.g. proc macros) using cg_gcc.
|
||||
targets.insert(compilers.target_compiler().host);
|
||||
|
||||
let output = builder.ensure(GccCodegenBackend::for_targets(
|
||||
compilers,
|
||||
targets.into_iter().collect(),
|
||||
));
|
||||
copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler);
|
||||
// Also copy libgccjit to the library sysroot, so that it is available for
|
||||
// the codegen backend.
|
||||
output.gcc.install_to(builder, &rustc_libdir);
|
||||
// Also copy all requires libgccjit dylibs to the corresponding
|
||||
// library sysroots, so that they are available for the codegen backend.
|
||||
output.dylib_set.install_to(builder, target_compiler);
|
||||
}
|
||||
CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use tracing::instrument;
|
|||
|
||||
use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name};
|
||||
use crate::core::build_steps::doc::DocumentationFormat;
|
||||
use crate::core::build_steps::gcc::GccTargetPair;
|
||||
use crate::core::build_steps::tool::{
|
||||
self, RustcPrivateCompilers, ToolTargetBuildMode, get_tool_target_compiler,
|
||||
};
|
||||
|
|
@ -2856,8 +2857,9 @@ impl Step for Gcc {
|
|||
|
||||
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
||||
let tarball = Tarball::new(builder, "gcc", &self.target.triple);
|
||||
let output = builder.ensure(super::gcc::Gcc { target: self.target });
|
||||
tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary);
|
||||
let output = builder
|
||||
.ensure(super::gcc::Gcc { target_pair: GccTargetPair::for_native_build(self.target) });
|
||||
tarball.add_file(output.libgccjit(), "lib", FileType::NativeLibrary);
|
||||
tarball.generate()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,49 +8,69 @@
|
|||
//! GCC and compiler-rt are essentially just wired up to everything else to
|
||||
//! ensure that they're always in place if needed.
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use crate::FileType;
|
||||
use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
|
||||
use crate::core::config::TargetSelection;
|
||||
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
|
||||
use crate::utils::exec::command;
|
||||
use crate::utils::helpers::{self, t};
|
||||
|
||||
/// GCC cannot cross-compile from a single binary to multiple targets.
|
||||
/// So we need to have a separate GCC dylib for each (host, target) pair.
|
||||
/// We represent this explicitly using this struct.
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct GccTargetPair {
|
||||
/// Target on which the libgccjit.so dylib will be executed.
|
||||
host: TargetSelection,
|
||||
/// Target for which the libgccjit.so dylib will generate assembly.
|
||||
target: TargetSelection,
|
||||
}
|
||||
|
||||
impl GccTargetPair {
|
||||
/// Create a target pair for a GCC that will run on `target` and generate assembly for `target`.
|
||||
pub fn for_native_build(target: TargetSelection) -> Self {
|
||||
Self { host: target, target }
|
||||
}
|
||||
|
||||
/// Create a target pair for a GCC that will run on `host` and generate assembly for `target`.
|
||||
/// This may be cross-compilation if `host != target`.
|
||||
pub fn for_target_pair(host: TargetSelection, target: TargetSelection) -> Self {
|
||||
Self { host, target }
|
||||
}
|
||||
|
||||
pub fn host(&self) -> TargetSelection {
|
||||
self.host
|
||||
}
|
||||
|
||||
pub fn target(&self) -> TargetSelection {
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GccTargetPair {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} -> {}", self.host, self.target)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Gcc {
|
||||
pub target: TargetSelection,
|
||||
pub target_pair: GccTargetPair,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GccOutput {
|
||||
pub libgccjit: PathBuf,
|
||||
target: TargetSelection,
|
||||
/// Path to a built or downloaded libgccjit.
|
||||
libgccjit: PathBuf,
|
||||
}
|
||||
|
||||
impl GccOutput {
|
||||
/// Install the required libgccjit library file(s) to the specified `path`.
|
||||
pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) {
|
||||
if builder.config.dry_run() {
|
||||
return;
|
||||
}
|
||||
|
||||
let target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string();
|
||||
|
||||
// If we build libgccjit ourselves, then `self.libgccjit` can actually be a symlink.
|
||||
// In that case, we have to resolve it first, otherwise we'd create a symlink to a symlink,
|
||||
// which wouldn't work.
|
||||
let actual_libgccjit_path = t!(
|
||||
self.libgccjit.canonicalize(),
|
||||
format!("Cannot find libgccjit at {}", self.libgccjit.display())
|
||||
);
|
||||
|
||||
let dest_dir = directory.join("rustlib").join(self.target).join("lib");
|
||||
t!(fs::create_dir_all(&dest_dir));
|
||||
let dst = dest_dir.join(target_filename);
|
||||
builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary);
|
||||
pub fn libgccjit(&self) -> &Path {
|
||||
&self.libgccjit
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,33 +84,38 @@ impl Step for Gcc {
|
|||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
run.builder.ensure(Gcc { target: run.target });
|
||||
// By default, we build libgccjit that can do native compilation (no cross-compilation)
|
||||
// on a given target.
|
||||
run.builder
|
||||
.ensure(Gcc { target_pair: GccTargetPair { host: run.target, target: run.target } });
|
||||
}
|
||||
|
||||
/// Compile GCC (specifically `libgccjit`) for `target`.
|
||||
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
||||
let target = self.target;
|
||||
let target_pair = self.target_pair;
|
||||
|
||||
// If GCC has already been built, we avoid building it again.
|
||||
let metadata = match get_gcc_build_status(builder, target) {
|
||||
GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path, target },
|
||||
let metadata = match get_gcc_build_status(builder, target_pair) {
|
||||
GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path },
|
||||
GccBuildStatus::ShouldBuild(m) => m,
|
||||
};
|
||||
|
||||
let _guard = builder.msg_unstaged(Kind::Build, "GCC", target);
|
||||
let action = Kind::Build.description();
|
||||
let msg = format!("{action} GCC for {target_pair}");
|
||||
let _guard = builder.group(&msg);
|
||||
t!(metadata.stamp.remove());
|
||||
let _time = helpers::timeit(builder);
|
||||
|
||||
let libgccjit_path = libgccjit_built_path(&metadata.install_dir);
|
||||
if builder.config.dry_run() {
|
||||
return GccOutput { libgccjit: libgccjit_path, target };
|
||||
return GccOutput { libgccjit: libgccjit_path };
|
||||
}
|
||||
|
||||
build_gcc(&metadata, builder, target);
|
||||
build_gcc(&metadata, builder, target_pair);
|
||||
|
||||
t!(metadata.stamp.write());
|
||||
|
||||
GccOutput { libgccjit: libgccjit_path, target }
|
||||
GccOutput { libgccjit: libgccjit_path }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,15 +136,27 @@ pub enum GccBuildStatus {
|
|||
/// are available for the given target.
|
||||
/// Returns a path to the libgccjit.so file.
|
||||
#[cfg(not(test))]
|
||||
fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<PathBuf> {
|
||||
fn try_download_gcc(builder: &Builder<'_>, target_pair: GccTargetPair) -> Option<PathBuf> {
|
||||
use build_helper::git::PathFreshness;
|
||||
|
||||
// Try to download GCC from CI if configured and available
|
||||
if !matches!(builder.config.gcc_ci_mode, crate::core::config::GccCiMode::DownloadFromCi) {
|
||||
return None;
|
||||
}
|
||||
if target != "x86_64-unknown-linux-gnu" {
|
||||
eprintln!("GCC CI download is only available for the `x86_64-unknown-linux-gnu` target");
|
||||
|
||||
// We currently do not support downloading CI GCC if the host/target pair doesn't match.
|
||||
if target_pair.host != target_pair.target {
|
||||
eprintln!(
|
||||
"GCC CI download is not available when the host ({}) does not equal the compilation target ({}).",
|
||||
target_pair.host, target_pair.target
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
if target_pair.host != "x86_64-unknown-linux-gnu" {
|
||||
eprintln!(
|
||||
"GCC CI download is only available for the `x86_64-unknown-linux-gnu` host/target"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let source = detect_gcc_freshness(
|
||||
|
|
@ -132,7 +169,7 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<Pa
|
|||
match source {
|
||||
PathFreshness::LastModifiedUpstream { upstream } => {
|
||||
// Download from upstream CI
|
||||
let root = ci_gcc_root(&builder.config, target);
|
||||
let root = ci_gcc_root(&builder.config, target_pair.target);
|
||||
let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&upstream);
|
||||
if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() {
|
||||
builder.config.download_ci_gcc(&upstream, &root);
|
||||
|
|
@ -158,7 +195,7 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<Pa
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn try_download_gcc(_builder: &Builder<'_>, _target: TargetSelection) -> Option<PathBuf> {
|
||||
fn try_download_gcc(_builder: &Builder<'_>, _target_pair: GccTargetPair) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -167,11 +204,28 @@ fn try_download_gcc(_builder: &Builder<'_>, _target: TargetSelection) -> Option<
|
|||
///
|
||||
/// It's used to avoid busting caches during x.py check -- if we've already built
|
||||
/// GCC, it's fine for us to not try to avoid doing so.
|
||||
pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus {
|
||||
if let Some(path) = try_download_gcc(builder, target) {
|
||||
pub fn get_gcc_build_status(builder: &Builder<'_>, target_pair: GccTargetPair) -> GccBuildStatus {
|
||||
// Prefer taking externally provided prebuilt libgccjit dylib
|
||||
if let Some(dir) = &builder.config.libgccjit_libs_dir {
|
||||
// The dir structure should be <root>/<host>/<target>/libgccjit.so
|
||||
let host_dir = dir.join(target_pair.host);
|
||||
let path = host_dir.join(target_pair.target).join("libgccjit.so");
|
||||
if path.exists() {
|
||||
return GccBuildStatus::AlreadyBuilt(path);
|
||||
} else {
|
||||
builder.info(&format!(
|
||||
"libgccjit.so for `{target_pair}` was not found at `{}`",
|
||||
path.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// If not available, try to download from CI
|
||||
if let Some(path) = try_download_gcc(builder, target_pair) {
|
||||
return GccBuildStatus::AlreadyBuilt(path);
|
||||
}
|
||||
|
||||
// If not available, try to build (or use already built libgccjit from disk)
|
||||
static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
|
||||
let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
|
||||
generate_smart_stamp_hash(
|
||||
|
|
@ -185,8 +239,8 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G
|
|||
builder.config.update_submodule("src/gcc");
|
||||
|
||||
let root = builder.src.join("src/gcc");
|
||||
let out_dir = builder.gcc_out(target).join("build");
|
||||
let install_dir = builder.gcc_out(target).join("install");
|
||||
let out_dir = gcc_out(builder, target_pair).join("build");
|
||||
let install_dir = gcc_out(builder, target_pair).join("install");
|
||||
|
||||
let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash);
|
||||
|
||||
|
|
@ -215,15 +269,20 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G
|
|||
GccBuildStatus::ShouldBuild(Meta { stamp, out_dir, install_dir, root })
|
||||
}
|
||||
|
||||
fn gcc_out(builder: &Builder<'_>, pair: GccTargetPair) -> PathBuf {
|
||||
builder.out.join(pair.host).join("gcc").join(pair.target)
|
||||
}
|
||||
|
||||
/// Returns the path to a libgccjit.so file in the install directory of GCC.
|
||||
fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
|
||||
install_dir.join("lib/libgccjit.so")
|
||||
}
|
||||
|
||||
fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
|
||||
if builder.build.cc_tool(target).is_like_clang()
|
||||
|| builder.build.cxx_tool(target).is_like_clang()
|
||||
{
|
||||
fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target_pair: GccTargetPair) {
|
||||
// Target on which libgccjit.so will be executed. Here we will generate a dylib with
|
||||
// instructions for that target.
|
||||
let host = target_pair.host;
|
||||
if builder.build.cc_tool(host).is_like_clang() || builder.build.cxx_tool(host).is_like_clang() {
|
||||
panic!(
|
||||
"Attempting to build GCC using Clang, which is known to misbehave. Please use GCC as the host C/C++ compiler. "
|
||||
);
|
||||
|
|
@ -241,7 +300,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
|
|||
// builds.
|
||||
// Therefore, we first copy the whole source directory to the build directory, and perform the
|
||||
// build from there.
|
||||
let src_dir = builder.gcc_out(target).join("src");
|
||||
let src_dir = gcc_out(builder, target_pair).join("src");
|
||||
if src_dir.exists() {
|
||||
builder.remove_dir(&src_dir);
|
||||
}
|
||||
|
|
@ -259,7 +318,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
|
|||
.arg("--disable-multilib")
|
||||
.arg(format!("--prefix={}", install_dir.display()));
|
||||
|
||||
let cc = builder.build.cc(target).display().to_string();
|
||||
let cc = builder.build.cc(host).display().to_string();
|
||||
let cc = builder
|
||||
.build
|
||||
.config
|
||||
|
|
@ -268,7 +327,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
|
|||
.map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}"));
|
||||
configure_cmd.env("CC", cc);
|
||||
|
||||
if let Ok(ref cxx) = builder.build.cxx(target) {
|
||||
if let Ok(ref cxx) = builder.build.cxx(host) {
|
||||
let cxx = cxx.display().to_string();
|
||||
let cxx = builder
|
||||
.build
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use build_helper::exit;
|
|||
|
||||
use crate::core::build_steps::compile::{Std, run_cargo};
|
||||
use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler};
|
||||
use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
|
||||
use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags};
|
||||
use crate::core::build_steps::llvm::get_llvm_version;
|
||||
use crate::core::build_steps::run::{get_completion_paths, get_help_path};
|
||||
use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget;
|
||||
|
|
@ -3956,7 +3956,7 @@ impl Step for CodegenGCC {
|
|||
let compilers = self.compilers;
|
||||
let target = self.target;
|
||||
|
||||
let gcc = builder.ensure(Gcc { target });
|
||||
let gcc = builder.ensure(Gcc { target_pair: GccTargetPair::for_native_build(target) });
|
||||
|
||||
builder.ensure(
|
||||
compile::Std::new(compilers.build_compiler(), target)
|
||||
|
|
@ -3997,12 +3997,13 @@ impl Step for CodegenGCC {
|
|||
.arg("--use-backend")
|
||||
.arg("gcc")
|
||||
.arg("--gcc-path")
|
||||
.arg(gcc.libgccjit.parent().unwrap())
|
||||
.arg(gcc.libgccjit().parent().unwrap())
|
||||
.arg("--out-dir")
|
||||
.arg(builder.stage_out(compilers.build_compiler(), Mode::Codegen).join("cg_gcc"))
|
||||
.arg("--release")
|
||||
.arg("--mini-tests")
|
||||
.arg("--std-tests");
|
||||
|
||||
cargo.args(builder.config.test_args());
|
||||
|
||||
cargo.into_cmd().run(builder);
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ pub struct Config {
|
|||
|
||||
// gcc codegen options
|
||||
pub gcc_ci_mode: GccCiMode,
|
||||
pub libgccjit_libs_dir: Option<PathBuf>,
|
||||
|
||||
// rust codegen options
|
||||
pub rust_optimize: RustOptimize,
|
||||
|
|
@ -620,7 +621,10 @@ impl Config {
|
|||
vendor: dist_vendor,
|
||||
} = toml.dist.unwrap_or_default();
|
||||
|
||||
let Gcc { download_ci_gcc: gcc_download_ci_gcc } = toml.gcc.unwrap_or_default();
|
||||
let Gcc {
|
||||
download_ci_gcc: gcc_download_ci_gcc,
|
||||
libgccjit_libs_dir: gcc_libgccjit_libs_dir,
|
||||
} = toml.gcc.unwrap_or_default();
|
||||
|
||||
if rust_bootstrap_override_lld.is_some() && rust_bootstrap_override_lld_legacy.is_some() {
|
||||
panic!(
|
||||
|
|
@ -1346,6 +1350,7 @@ impl Config {
|
|||
keep_stage: flags_keep_stage,
|
||||
keep_stage_std: flags_keep_stage_std,
|
||||
libdir: install_libdir.map(PathBuf::from),
|
||||
libgccjit_libs_dir: gcc_libgccjit_libs_dir,
|
||||
library_docs_private_items: build_library_docs_private_items.unwrap_or(false),
|
||||
lld_enabled,
|
||||
lldb: build_lldb.map(PathBuf::from),
|
||||
|
|
|
|||
|
|
@ -15,5 +15,6 @@ define_config! {
|
|||
#[derive(Default)]
|
||||
struct Gcc {
|
||||
download_ci_gcc: Option<bool> = "download-ci-gcc",
|
||||
libgccjit_libs_dir: Option<PathBuf> = "libgccjit-libs-dir",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -975,10 +975,6 @@ impl Build {
|
|||
self.out.join(&*target.triple).join("enzyme")
|
||||
}
|
||||
|
||||
fn gcc_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(&*target.triple).join("gcc")
|
||||
}
|
||||
|
||||
fn lld_out(&self, target: TargetSelection) -> PathBuf {
|
||||
self.out.join(target).join("lld")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue