77//! goes along from the output of the previous stage.
88
99use std:: borrow:: Cow ;
10- use std:: collections:: { HashMap , HashSet } ;
10+ use std:: collections:: { BTreeMap , HashMap , HashSet } ;
1111use std:: ffi:: OsStr ;
1212use std:: io:: BufReader ;
1313use std:: io:: prelude:: * ;
@@ -19,7 +19,7 @@ use serde_derive::Deserialize;
1919#[ cfg( feature = "tracing" ) ]
2020use 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} ;
2323use crate :: core:: build_steps:: tool:: { RustcPrivateCompilers , SourceType , copy_lld_artifacts} ;
2424use crate :: core:: build_steps:: { dist, llvm} ;
2525use 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 ) ]
15691626pub 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 ) ]
15751642pub 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
15791660impl 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 }
0 commit comments