From 80dedb601aababe989be488a18f14a4d3c4b7234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 31 Dec 2025 14:11:32 +0100 Subject: [PATCH 1/2] Add a dist step for GCC --- src/bootstrap/src/core/build_steps/compile.rs | 29 ++++--- src/bootstrap/src/core/build_steps/dist.rs | 84 ++++++++++++++++++- src/bootstrap/src/core/builder/mod.rs | 3 +- src/bootstrap/src/utils/tarball.rs | 10 +++ 4 files changed, 114 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 02be3f2cc7353..79a564cfb1ffb 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1612,26 +1612,35 @@ impl GccDylibSet { "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(); + let libgccjit_path = libgccjit.libgccjit(); // 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()) + let libgccjit_path = t!( + libgccjit_path.canonicalize(), + format!("Cannot find libgccjit at {}", libgccjit_path.display()) ); - // /lib//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); + let dst = cg_sysroot.join(libgccjit_path_relative_to_cg_dir(target_pair, libgccjit)); + t!(std::fs::create_dir_all(dst.parent().unwrap())); + builder.copy_link(&libgccjit_path, &dst, FileType::NativeLibrary); } } } +/// Returns a path where libgccjit.so should be stored, **relative** to the +/// **codegen backend directory**. +pub fn libgccjit_path_relative_to_cg_dir( + target_pair: &GccTargetPair, + libgccjit: &GccOutput, +) -> PathBuf { + let target_filename = libgccjit.libgccjit().file_name().unwrap().to_str().unwrap(); + + // /lib//libgccjit.so + Path::new("lib").join(target_pair.target()).join(target_filename) +} + /// Output of the `compile::GccCodegenBackend` step. /// /// It contains a build stamp with the path to the built cg_gcc dylib. diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index f47b0c0b0072d..cfcb144e0993e 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -19,7 +19,9 @@ use object::read::archive::ArchiveFile; #[cfg(feature = "tracing")] use tracing::instrument; -use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name}; +use crate::core::build_steps::compile::{ + get_codegen_backend_file, libgccjit_path_relative_to_cg_dir, normalize_codegen_backend_name, +}; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::gcc::GccTargetPair; use crate::core::build_steps::tool::{ @@ -2992,3 +2994,83 @@ impl Step for GccDev { Some(StepMetadata::dist("gcc-dev", self.target)) } } + +/// Tarball containing a libgccjit dylib, +/// needed as a dependency for the GCC codegen backend (similarly to the LLVM +/// backend needing a prebuilt libLLVM). +/// +/// This component is used for distribution through rustup. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct Gcc { + host: TargetSelection, + target: TargetSelection, +} + +impl Step for Gcc { + type Output = Option; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("gcc") + } + + fn make_run(run: RunConfig<'_>) { + // GCC is always built for a target pair, (host, target). + // We do not yet support cross-compilation here, so the host target is always inferred to + // be the bootstrap host target. + run.builder.ensure(Gcc { host: run.builder.host_target, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + // This prevents gcc from being built for "dist" + // or "install" on the stable/beta channels. It is not yet stable and + // should not be included. + if !builder.build.unstable_features() { + return None; + } + + let host = self.host; + let target = self.target; + if host != "x86_64-unknown-linux-gnu" { + builder.info(&format!("host target `{host}` not supported by gcc. skipping")); + return None; + } + + // We need the GCC sources to build GCC and also to add its license and README + // files to the tarball + builder.require_submodule( + "src/gcc", + Some("The src/gcc submodule is required for disting libgccjit"), + ); + + let target_pair = GccTargetPair::for_target_pair(host, target); + let libgccjit = builder.ensure(super::gcc::Gcc { target_pair }); + + // We have to include the target name in the component name, so that rustup can somehow + // distinguish that there are multiple gcc components on a given host target. + // So the tarball includes the target name. + let mut tarball = Tarball::new(builder, &format!("gcc-{target}"), &host.triple); + tarball.set_overlay(OverlayKind::Gcc); + tarball.is_preview(true); + tarball.add_legal_and_readme_to("share/doc/gcc"); + + // The path where to put libgccjit is determined by GccDylibSet. + // However, it requires a Compiler to figure out the path to the codegen backend sysroot. + // We don't really have any compiler here, because we just build libgccjit. + // So we duplicate the logic for determining the CG sysroot here. + let cg_dir = PathBuf::from(format!("lib/rustlib/{host}/codegen-backends")); + + // This returns the path to the actual file, but here we need its parent + let rel_libgccjit_path = libgccjit_path_relative_to_cg_dir(&target_pair, &libgccjit); + let path = cg_dir.join(rel_libgccjit_path.parent().unwrap()); + + tarball.add_file(libgccjit.libgccjit(), path, FileType::NativeLibrary); + Some(tarball.generate()) + } + + fn metadata(&self) -> Option { + Some(StepMetadata::dist( + "gcc", + TargetSelection::from_user(&format!("({}, {})", self.host, self.target)), + )) + } +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 4a04b97c549a1..f63b8e0445509 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -990,7 +990,8 @@ impl<'a> Builder<'a> { dist::PlainSourceTarballGpl, dist::BuildManifest, dist::ReproducibleArtifacts, - dist::GccDev + dist::GccDev, + dist::Gcc ), Kind::Install => describe!( install::Docs, diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 7db06dcc3e9ae..cd6a03c84870c 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -26,6 +26,7 @@ pub(crate) enum OverlayKind { RustAnalyzer, RustcCodegenCranelift, RustcCodegenGcc, + Gcc, LlvmBitcodeLinker, } @@ -78,6 +79,14 @@ impl OverlayKind { "LICENSE-MIT", "src/tools/llvm-bitcode-linker/README.md", ], + OverlayKind::Gcc => &[ + "src/gcc/README", + "src/gcc/COPYING", + "src/gcc/COPYING.LIB", + "src/gcc/COPYING.RUNTIME", + "src/gcc/COPYING3", + "src/gcc/COPYING3.LIB", + ], } } @@ -101,6 +110,7 @@ impl OverlayKind { OverlayKind::RustcCodegenCranelift => builder.rust_version(), OverlayKind::RustcCodegenGcc => builder.rust_version(), OverlayKind::LlvmBitcodeLinker => builder.rust_version(), + OverlayKind::Gcc => builder.rust_version(), } } } From b0aa77a0267040b6155d75487e8ecc7be7790bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 31 Dec 2025 14:12:17 +0100 Subject: [PATCH 2/2] Build GCC on CI --- src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index f77a0d562f03f..c78b76a53f44c 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -14,5 +14,7 @@ python3 ../x.py build --set rust.debug=true opt-dist # Use GCC for building GCC components, as it seems to behave badly when built with Clang # Only build GCC on full builds, not try builds if [ "${DIST_TRY_BUILD:-0}" == "0" ]; then - CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc-dev + CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist \ + gcc-dev \ + gcc fi