@@ -13,10 +13,11 @@ use std::path::{Path, PathBuf};
13
13
use std:: sync:: OnceLock ;
14
14
15
15
use build_helper:: ci:: CiEnv ;
16
+ use build_helper:: git:: get_closest_merge_commit;
16
17
17
18
use crate :: Config ;
18
19
use crate :: core:: builder:: { Builder , Cargo , Kind , RunConfig , ShouldRun , Step } ;
19
- use crate :: core:: config:: TargetSelection ;
20
+ use crate :: core:: config:: { GccCiMode , TargetSelection } ;
20
21
use crate :: utils:: build_stamp:: { BuildStamp , generate_smart_stamp_hash} ;
21
22
use crate :: utils:: exec:: command;
22
23
use crate :: utils:: helpers:: { self , t} ;
@@ -89,17 +90,39 @@ pub enum GccBuildStatus {
89
90
ShouldBuild ( Meta ) ,
90
91
}
91
92
92
- /// This returns whether we've already previously built GCC.
93
+ /// Tries to download GCC from CI if it is enabled and GCC artifacts
94
+ /// are available for the given target.
95
+ /// Returns a path to the libgccjit.so file.
96
+ fn try_download_gcc ( builder : & Builder < ' _ > , target : TargetSelection ) -> Option < PathBuf > {
97
+ // Try to download GCC from CI if configured and available
98
+ if !matches ! ( builder. config. gcc_ci_mode, GccCiMode :: DownloadFromCi ) {
99
+ return None ;
100
+ }
101
+ if target != "x86_64-unknown-linux-gnu" {
102
+ eprintln ! ( "GCC CI download is only available for the `x86_64-unknown-linux-gnu` target" ) ;
103
+ return None ;
104
+ }
105
+ let sha =
106
+ detect_gcc_sha ( & builder. config , builder. config . rust_info . is_managed_git_subrepository ( ) ) ;
107
+ let root = ci_gcc_root ( & builder. config ) ;
108
+ let gcc_stamp = BuildStamp :: new ( & root) . with_prefix ( "gcc" ) . add_stamp ( & sha) ;
109
+ if !gcc_stamp. is_up_to_date ( ) && !builder. config . dry_run ( ) {
110
+ builder. config . download_ci_gcc ( & sha, & root) ;
111
+ t ! ( gcc_stamp. write( ) ) ;
112
+ }
113
+ // FIXME: put libgccjit.so into a lib directory in dist::Gcc
114
+ Some ( root. join ( "libgccjit.so" ) )
115
+ }
116
+
117
+ /// This returns information about whether GCC should be built or if it's already built.
118
+ /// It transparently handles downloading GCC from CI if needed.
93
119
///
94
120
/// It's used to avoid busting caches during x.py check -- if we've already built
95
121
/// GCC, it's fine for us to not try to avoid doing so.
96
122
pub 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" ) ;
123
+ if let Some ( path) = try_download_gcc ( builder, target) {
124
+ return GccBuildStatus :: AlreadyBuilt ( path) ;
125
+ }
103
126
104
127
static STAMP_HASH_MEMO : OnceLock < String > = OnceLock :: new ( ) ;
105
128
let smart_stamp_hash = STAMP_HASH_MEMO . get_or_init ( || {
@@ -110,6 +133,13 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G
110
133
)
111
134
} ) ;
112
135
136
+ // Initialize the gcc submodule if not initialized already.
137
+ builder. config . update_submodule ( "src/gcc" ) ;
138
+
139
+ let root = builder. src . join ( "src/gcc" ) ;
140
+ let out_dir = builder. gcc_out ( target) . join ( "build" ) ;
141
+ let install_dir = builder. gcc_out ( target) . join ( "install" ) ;
142
+
113
143
let stamp = BuildStamp :: new ( & out_dir) . with_prefix ( "gcc" ) . add_stamp ( smart_stamp_hash) ;
114
144
115
145
if stamp. is_up_to_date ( ) {
@@ -142,7 +172,7 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
142
172
install_dir. join ( "lib/libgccjit.so" )
143
173
}
144
174
145
- fn build_gcc ( metadata : & Meta , builder : & Builder , target : TargetSelection ) {
175
+ fn build_gcc ( metadata : & Meta , builder : & Builder < ' _ > , target : TargetSelection ) {
146
176
let Meta { stamp : _, out_dir, install_dir, root } = metadata;
147
177
148
178
t ! ( fs:: create_dir_all( out_dir) ) ;
@@ -202,21 +232,51 @@ fn build_gcc(metadata: &Meta, builder: &Builder, target: TargetSelection) {
202
232
}
203
233
configure_cmd. run ( builder) ;
204
234
205
- command ( "make" )
206
- . current_dir ( & out_dir)
207
- . arg ( "--silent" )
208
- . arg ( format ! ( "-j{}" , builder. jobs( ) ) )
209
- . run_capture_stdout ( builder) ;
210
- command ( "make" )
211
- . current_dir ( & out_dir)
212
- . arg ( "--silent" )
213
- . arg ( "install" )
214
- . run_capture_stdout ( builder) ;
215
- }
235
+ command ( "make" )
236
+ . current_dir ( & out_dir)
237
+ . arg ( "--silent" )
238
+ . arg ( format ! ( "-j{}" , builder. jobs( ) ) )
239
+ . run_capture_stdout ( builder) ;
240
+ command ( "make" )
241
+ . current_dir ( & out_dir)
242
+ . arg ( "--silent" )
243
+ . arg ( "install" )
244
+ . run_capture_stdout ( builder) ;
216
245
}
217
246
218
247
/// Configures a Cargo invocation so that it can build the GCC codegen backend.
219
248
pub fn add_cg_gcc_cargo_flags ( cargo : & mut Cargo , gcc : & GccOutput ) {
220
249
// Add the path to libgccjit.so to the linker search paths.
221
250
cargo. rustflag ( & format ! ( "-L{}" , gcc. libgccjit. parent( ) . unwrap( ) . to_str( ) . unwrap( ) ) ) ;
222
251
}
252
+
253
+ /// The absolute path to the downloaded GCC artifacts.
254
+ fn ci_gcc_root ( config : & Config ) -> PathBuf {
255
+ config. out . join ( config. build ) . join ( "ci-gcc" )
256
+ }
257
+
258
+ /// This retrieves the GCC sha we *want* to use, according to git history.
259
+ fn detect_gcc_sha ( config : & Config , is_git : bool ) -> String {
260
+ let gcc_sha = if is_git {
261
+ get_closest_merge_commit (
262
+ Some ( & config. src ) ,
263
+ & config. git_config ( ) ,
264
+ & [ config. src . join ( "src/gcc" ) , config. src . join ( "src/bootstrap/download-ci-gcc-stamp" ) ] ,
265
+ )
266
+ . unwrap ( )
267
+ } else if let Some ( info) = crate :: utils:: channel:: read_commit_info_file ( & config. src ) {
268
+ info. sha . trim ( ) . to_owned ( )
269
+ } else {
270
+ "" . to_owned ( )
271
+ } ;
272
+
273
+ if gcc_sha. is_empty ( ) {
274
+ eprintln ! ( "error: could not find commit hash for downloading GCC" ) ;
275
+ eprintln ! ( "HELP: maybe your repository history is too shallow?" ) ;
276
+ eprintln ! ( "HELP: consider disabling `download-ci-gcc`" ) ;
277
+ eprintln ! ( "HELP: or fetch enough history to include one upstream commit" ) ;
278
+ panic ! ( ) ;
279
+ }
280
+
281
+ gcc_sha
282
+ }
0 commit comments