@@ -13,10 +13,11 @@ use std::path::{Path, PathBuf};
1313use std:: sync:: OnceLock ;
1414
1515use build_helper:: ci:: CiEnv ;
16+ use build_helper:: git:: get_closest_merge_commit;
1617
1718use crate :: Config ;
1819use crate :: core:: builder:: { Builder , Cargo , Kind , RunConfig , ShouldRun , Step } ;
19- use crate :: core:: config:: TargetSelection ;
20+ use crate :: core:: config:: { GccCiMode , TargetSelection } ;
2021use crate :: utils:: build_stamp:: { BuildStamp , generate_smart_stamp_hash} ;
2122use crate :: utils:: exec:: command;
2223use crate :: utils:: helpers:: { self , t} ;
@@ -89,18 +90,12 @@ pub enum GccBuildStatus {
8990 ShouldBuild ( Meta ) ,
9091}
9192
92- /// This returns whether we've already previously built GCC.
93+ /// This returns information about whether GCC should be built or if it's already built.
94+ /// It transparently handles downloading GCC from CI if needed.
9395///
9496/// It's used to avoid busting caches during x.py check -- if we've already built
9597/// GCC, it's fine for us to not try to avoid doing so.
9698pub fn get_gcc_build_status ( builder : & Builder < ' _ > , target : TargetSelection ) -> GccBuildStatus {
97- // Initialize the gcc submodule if not initialized already.
98- builder. config . update_submodule ( "src/gcc" ) ;
99-
100- let root = builder. src . join ( "src/gcc" ) ;
101- let out_dir = builder. gcc_out ( target) . join ( "build" ) ;
102- let install_dir = builder. gcc_out ( target) . join ( "install" ) ;
103-
10499 static STAMP_HASH_MEMO : OnceLock < String > = OnceLock :: new ( ) ;
105100 let smart_stamp_hash = STAMP_HASH_MEMO . get_or_init ( || {
106101 generate_smart_stamp_hash (
@@ -110,6 +105,35 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G
110105 )
111106 } ) ;
112107
108+ // Try to download GCC from CI if configured and available
109+ if let GccCiMode :: DownloadFromCi = & builder. config . gcc_ci_mode {
110+ if builder. build . build == "x86_64-unknown-linux-gnu" {
111+ let sha = detect_gcc_sha (
112+ & builder. config ,
113+ builder. config . rust_info . is_managed_git_subrepository ( ) ,
114+ ) ;
115+ let root = ci_gcc_root ( & builder. config ) ;
116+ let gcc_stamp = BuildStamp :: new ( & root) . with_prefix ( "gcc" ) . add_stamp ( & sha) ;
117+ if !gcc_stamp. is_up_to_date ( ) && !builder. config . dry_run ( ) {
118+ builder. config . download_ci_gcc ( & sha, & root) ;
119+ t ! ( gcc_stamp. write( ) ) ;
120+ }
121+ // FIXME: put libgccjit.so into a lib directory in dist::Gcc
122+ return GccBuildStatus :: AlreadyBuilt ( root. join ( "libgccjit.so" ) ) ;
123+ } else {
124+ eprintln ! (
125+ "GCC CI download is only available for the `x86_64-unknown-linux-gnu` target"
126+ ) ;
127+ }
128+ }
129+
130+ // Initialize the gcc submodule if not initialized already.
131+ builder. config . update_submodule ( "src/gcc" ) ;
132+
133+ let root = builder. src . join ( "src/gcc" ) ;
134+ let out_dir = builder. gcc_out ( target) . join ( "build" ) ;
135+ let install_dir = builder. gcc_out ( target) . join ( "install" ) ;
136+
113137 let stamp = BuildStamp :: new ( & out_dir) . with_prefix ( "gcc" ) . add_stamp ( smart_stamp_hash) ;
114138
115139 if stamp. is_up_to_date ( ) {
@@ -142,7 +166,7 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
142166 install_dir. join ( "lib/libgccjit.so" )
143167}
144168
145- fn build_gcc ( metadata : & Meta , builder : & Builder , target : TargetSelection ) {
169+ fn build_gcc ( metadata : & Meta , builder : & Builder < ' _ > , target : TargetSelection ) {
146170 let Meta { stamp : _, out_dir, install_dir, root } = metadata;
147171
148172 t ! ( fs:: create_dir_all( out_dir) ) ;
@@ -210,3 +234,34 @@ pub fn add_cg_gcc_cargo_flags(cargo: &mut Cargo, gcc: &GccOutput) {
210234 // Add the path to libgccjit.so to the linker search paths.
211235 cargo. rustflag ( & format ! ( "-L{}" , gcc. libgccjit. parent( ) . unwrap( ) . to_str( ) . unwrap( ) ) ) ;
212236}
237+
238+ /// The absolute path to the downloaded GCC artifacts.
239+ fn ci_gcc_root ( config : & Config ) -> PathBuf {
240+ config. out . join ( config. build ) . join ( "ci-gcc" )
241+ }
242+
243+ /// This retrieves the GCC sha we *want* to use, according to git history.
244+ fn detect_gcc_sha ( config : & Config , is_git : bool ) -> String {
245+ let gcc_sha = if is_git {
246+ get_closest_merge_commit (
247+ Some ( & config. src ) ,
248+ & config. git_config ( ) ,
249+ & [ config. src . join ( "src/gcc" ) , config. src . join ( "src/bootstrap/download-ci-gcc-stamp" ) ] ,
250+ )
251+ . unwrap ( )
252+ } else if let Some ( info) = crate :: utils:: channel:: read_commit_info_file ( & config. src ) {
253+ info. sha . trim ( ) . to_owned ( )
254+ } else {
255+ "" . to_owned ( )
256+ } ;
257+
258+ if gcc_sha. is_empty ( ) {
259+ eprintln ! ( "error: could not find commit hash for downloading GCC" ) ;
260+ eprintln ! ( "HELP: maybe your repository history is too shallow?" ) ;
261+ eprintln ! ( "HELP: consider disabling `download-ci-gcc`" ) ;
262+ eprintln ! ( "HELP: or fetch enough history to include one upstream commit" ) ;
263+ panic ! ( ) ;
264+ }
265+
266+ gcc_sha
267+ }
0 commit comments