diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 6a8da0305464a..cc96cd555e74e 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -190,6 +190,7 @@ # modifications to the `src/gcc` submodule. # Currently, this is only supported for the `x86_64-unknown-linux-gnu` target. #gcc.download-ci-gcc = false +#libgccjit-libs-dir = "/path/to/libgccjit-libs-dir" # ============================================================================= # General build configuration options diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 4fdcdb2051f55..acdb34b847a48 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2823,7 +2823,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); + if let Some(ref path) = output.libgccjit { + tarball.add_file(path, "lib", FileType::NativeLibrary); + } tarball.generate() } diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 17ab8c4e2f479..f05b90911fa5e 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -26,7 +26,11 @@ pub struct Gcc { #[derive(Clone)] pub struct GccOutput { - pub libgccjit: PathBuf, + /// Path to a built or downloaded libgccjit. + /// Is None when setting libgccjit-libs-dir. + /// FIXME: it seems wrong to make this an Option. + /// Perhaps it should be a Vec so that we can install all libs from libgccjit-libs-dir? + pub libgccjit: Option, } impl GccOutput { @@ -36,23 +40,55 @@ impl GccOutput { return; } - // At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol). - // However, at runtime, it will by default look for libgccjit.so.0. - // So when we install the built libgccjit.so file to the target `directory`, we add it there - // with the `.0` suffix. - let mut target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string(); - target_filename.push_str(".0"); - - // 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()) - ); + if let Some(ref path) = self.libgccjit { + // At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol). + // However, at runtime, it will by default look for libgccjit.so.0. + // So when we install the built libgccjit.so file to the target `directory`, we add it there + // with the `.0` suffix. + let mut target_filename = path.file_name().unwrap().to_str().unwrap().to_string(); + target_filename.push_str(".0"); + + // 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!(path.canonicalize(), format!("Cannot find libgccjit at {}", path.display())); + + let dst = directory.join(&target_filename); + builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); + } - let dst = directory.join(target_filename); - builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); + if let Some(ref path) = builder.config.libgccjit_libs_dir { + let host_target = builder.config.host_target.triple; + + let source = path.join(host_target); + let dst = directory; + + let targets = builder + .config + .targets + .iter() + .map(|target| target.triple) + .chain(std::iter::once(host_target)); + + let target_filename = "libgccjit.so.0"; + for target in targets { + let source = source.join(target).join(target_filename); + // To support symlinks in libgccjit-libs-dir, we have to resolve it first, + // otherwise we'd create a symlink to a symlink, which wouldn't work. + let actual_libgccjit_path = t!( + source.canonicalize(), + format!("Cannot find libgccjit at {}", source.display()) + ); + let target_dir = dst.join(target); + t!( + std::fs::create_dir_all(&target_dir), + format!("Cannot create target dir {} for libgccjit", target_dir.display()) + ); + let dst = target_dir.join(target_filename); + builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); + } + } } } @@ -75,7 +111,8 @@ impl Step for Gcc { // 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 }, + GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: Some(path) }, + GccBuildStatus::InLibsDir => return GccOutput { libgccjit: None }, GccBuildStatus::ShouldBuild(m) => m, }; @@ -85,14 +122,14 @@ impl Step for Gcc { let libgccjit_path = libgccjit_built_path(&metadata.install_dir); if builder.config.dry_run() { - return GccOutput { libgccjit: libgccjit_path }; + return GccOutput { libgccjit: Some(libgccjit_path) }; } build_gcc(&metadata, builder, target); t!(metadata.stamp.write()); - GccOutput { libgccjit: libgccjit_path } + GccOutput { libgccjit: Some(libgccjit_path) } } } @@ -106,6 +143,7 @@ pub struct Meta { pub enum GccBuildStatus { /// libgccjit is already built at this path AlreadyBuilt(PathBuf), + InLibsDir, ShouldBuild(Meta), } @@ -170,6 +208,11 @@ 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 matches!(builder.config.gcc_ci_mode, crate::core::config::GccCiMode::CopyFromLibsDir) { + // FIXME: check if this is OK. + return GccBuildStatus::InLibsDir; + } + if let Some(path) = try_download_gcc(builder, target) { return GccBuildStatus::AlreadyBuilt(path); } @@ -293,7 +336,9 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { /// Configures a Cargo invocation so that it can build the GCC codegen backend. pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) { // Add the path to libgccjit.so to the linker search paths. - cargo.rustflag(&format!("-L{}", gcc.libgccjit.parent().unwrap().to_str().unwrap())); + if let Some(ref path) = gcc.libgccjit { + cargo.rustflag(&format!("-L{}", path.parent().unwrap().to_str().unwrap())); + } } /// The absolute path to the downloaded GCC artifacts. diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 4fc938d33c6c6..2180ed3cc4c08 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3870,13 +3870,15 @@ impl Step for CodegenGCC { .arg("test") .arg("--use-backend") .arg("gcc") - .arg("--gcc-path") - .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"); + + if let Some(ref path) = gcc.libgccjit { + cargo.arg("--gcc-path").arg(path.parent().unwrap()); + } cargo.args(builder.config.test_args()); cargo.into_cmd().run(builder); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2f493658ec0ec..b6c0e930f01be 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -188,6 +188,7 @@ pub struct Config { // gcc codegen options pub gcc_ci_mode: GccCiMode, + pub libgccjit_libs_dir: Option, // rust codegen options pub rust_optimize: RustOptimize, @@ -620,7 +621,8 @@ 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 } = + toml.gcc.unwrap_or_default(); if rust_bootstrap_override_lld.is_some() && rust_bootstrap_override_lld_legacy.is_some() { panic!( @@ -1218,12 +1220,13 @@ impl Config { Warnings::Default => rust_deny_warnings.unwrap_or(true), }; - let gcc_ci_mode = match gcc_download_ci_gcc { - Some(value) => match value { + let gcc_ci_mode = match (&libgccjit_libs_dir, gcc_download_ci_gcc) { + (Some(_), _) => GccCiMode::CopyFromLibsDir, + (None, Some(value)) => match value { true => GccCiMode::DownloadFromCi, false => GccCiMode::BuildLocally, }, - None => GccCiMode::default(), + (None, None) => GccCiMode::default(), }; let targets = flags_target @@ -1346,6 +1349,7 @@ impl Config { keep_stage: flags_keep_stage, keep_stage_std: flags_keep_stage_std, libdir: install_libdir.map(PathBuf::from), + libgccjit_libs_dir, library_docs_private_items: build_library_docs_private_items.unwrap_or(false), lld_enabled, lldb: build_lldb.map(PathBuf::from), diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 007ed4aaba13f..bbc273ec27178 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -433,6 +433,7 @@ pub enum GccCiMode { /// If it is not available on CI, it will be built locally instead. #[default] DownloadFromCi, + CopyFromLibsDir, } pub fn threads_from_config(v: u32) -> u32 { diff --git a/src/bootstrap/src/core/config/toml/gcc.rs b/src/bootstrap/src/core/config/toml/gcc.rs index 9ea697edf159e..94d15a9baaff9 100644 --- a/src/bootstrap/src/core/config/toml/gcc.rs +++ b/src/bootstrap/src/core/config/toml/gcc.rs @@ -15,5 +15,6 @@ define_config! { #[derive(Default)] struct Gcc { download_ci_gcc: Option = "download-ci-gcc", + libgccjit_libs_dir: Option = "libgccjit-libs-dir", } }