@@ -14,13 +14,72 @@ use std::sync::OnceLock;
1414
1515use build_helper:: ci:: CiEnv ;
1616
17- use crate :: Kind ;
18- use crate :: core:: builder:: { Builder , RunConfig , ShouldRun , Step } ;
17+ use crate :: core:: builder:: { Builder , Cargo , Kind , RunConfig , ShouldRun , Step } ;
1918use crate :: core:: config:: TargetSelection ;
2019use crate :: utils:: build_stamp:: { BuildStamp , generate_smart_stamp_hash} ;
2120use crate :: utils:: exec:: command;
2221use crate :: utils:: helpers:: { self , t} ;
2322
23+ #[ derive( Debug , Clone , Hash , PartialEq , Eq ) ]
24+ pub struct Gcc {
25+ pub target : TargetSelection ,
26+ }
27+
28+ #[ derive( Clone ) ]
29+ pub struct GccOutput {
30+ pub libgccjit : PathBuf ,
31+ }
32+
33+ impl Step for Gcc {
34+ type Output = GccOutput ;
35+
36+ const ONLY_HOSTS : bool = true ;
37+
38+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
39+ run. path ( "src/gcc" ) . alias ( "gcc" )
40+ }
41+
42+ fn make_run ( run : RunConfig < ' _ > ) {
43+ run. builder . ensure ( Gcc { target : run. target } ) ;
44+ }
45+
46+ /// Compile GCC (specifically `libgccjit`) for `target`.
47+ fn run ( self , builder : & Builder < ' _ > ) -> Self :: Output {
48+ let target = self . target ;
49+
50+ // If GCC has already been built, we avoid building it again.
51+ let metadata = match get_gcc_build_status ( builder, target) {
52+ GccBuildStatus :: AlreadyBuilt ( path) => return GccOutput { libgccjit : path } ,
53+ GccBuildStatus :: ShouldBuild ( m) => m,
54+ } ;
55+
56+ let _guard = builder. msg_unstaged ( Kind :: Build , "GCC" , target) ;
57+ t ! ( metadata. stamp. remove( ) ) ;
58+ let _time = helpers:: timeit ( builder) ;
59+
60+ let libgccjit_path = libgccjit_built_path ( & metadata. install_dir ) ;
61+ if builder. config . dry_run ( ) {
62+ return GccOutput { libgccjit : libgccjit_path } ;
63+ }
64+
65+ build_gcc ( & metadata, builder, target) ;
66+ create_lib_alias ( builder, & libgccjit_path) ;
67+
68+ t ! ( metadata. stamp. write( ) ) ;
69+
70+ GccOutput { libgccjit : libgccjit_path }
71+ }
72+ }
73+
74+ /// Creates a libgccjit.so.0 alias next to libgccjit.so if it does not
75+ /// already exist
76+ fn create_lib_alias ( builder : & Builder < ' _ > , libgccjit : & PathBuf ) {
77+ let lib_alias = libgccjit. parent ( ) . unwrap ( ) . join ( "libgccjit.so.0" ) ;
78+ if !lib_alias. exists ( ) {
79+ t ! ( builder. symlink_file( libgccjit, lib_alias) ) ;
80+ }
81+ }
82+
2483pub struct Meta {
2584 stamp : BuildStamp ,
2685 out_dir : PathBuf ,
@@ -33,7 +92,54 @@ pub enum GccBuildStatus {
3392 ShouldBuild ( Meta ) ,
3493}
3594
36- /// This returns whether we've already previously built GCC.
95+ /// Tries to download GCC from CI if it is enabled and GCC artifacts
96+ /// are available for the given target.
97+ /// Returns a path to the libgccjit.so file.
98+ #[ cfg( not( test) ) ]
99+ fn try_download_gcc ( builder : & Builder < ' _ > , target : TargetSelection ) -> Option < PathBuf > {
100+ use build_helper:: git:: PathFreshness ;
101+
102+ // Try to download GCC from CI if configured and available
103+ if !matches ! ( builder. config. gcc_ci_mode, crate :: core:: config:: GccCiMode :: DownloadFromCi ) {
104+ return None ;
105+ }
106+ if target != "x86_64-unknown-linux-gnu" {
107+ eprintln ! ( "GCC CI download is only available for the `x86_64-unknown-linux-gnu` target" ) ;
108+ return None ;
109+ }
110+ let source = detect_gcc_freshness (
111+ & builder. config ,
112+ builder. config . rust_info . is_managed_git_subrepository ( ) ,
113+ ) ;
114+ match source {
115+ PathFreshness :: LastModifiedUpstream { upstream } => {
116+ // Download from upstream CI
117+ let root = ci_gcc_root ( & builder. config ) ;
118+ let gcc_stamp = BuildStamp :: new ( & root) . with_prefix ( "gcc" ) . add_stamp ( & upstream) ;
119+ if !gcc_stamp. is_up_to_date ( ) && !builder. config . dry_run ( ) {
120+ builder. config . download_ci_gcc ( & upstream, & root) ;
121+ t ! ( gcc_stamp. write( ) ) ;
122+ }
123+
124+ let libgccjit = root. join ( "lib" ) . join ( "libgccjit.so" ) ;
125+ create_lib_alias ( builder, & libgccjit) ;
126+ Some ( libgccjit)
127+ }
128+ PathFreshness :: HasLocalModifications { .. } => {
129+ // We have local modifications, rebuild GCC.
130+ eprintln ! ( "Found local GCC modifications, GCC will *not* be downloaded" ) ;
131+ None
132+ }
133+ }
134+ }
135+
136+ #[ cfg( test) ]
137+ fn try_download_gcc ( _builder : & Builder < ' _ > , _target : TargetSelection ) -> Option < PathBuf > {
138+ None
139+ }
140+
141+ /// This returns information about whether GCC should be built or if it's already built.
142+ /// It transparently handles downloading GCC from CI if needed.
37143///
38144/// It's used to avoid busting caches during x.py check -- if we've already built
39145/// GCC, it's fine for us to not try to avoid doing so.
@@ -178,6 +284,34 @@ impl Step for Gcc {
178284
179285 t ! ( stamp. write( ) ) ;
180286
181- true
182- }
287+ /// Detect whether GCC sources have been modified locally or not.
288+ #[ cfg( not( test) ) ]
289+ fn detect_gcc_freshness ( config : & crate :: Config , is_git : bool ) -> build_helper:: git:: PathFreshness {
290+ use build_helper:: git:: { PathFreshness , check_path_modifications} ;
291+
292+ let freshness = if is_git {
293+ Some (
294+ check_path_modifications (
295+ Some ( & config. src ) ,
296+ & config. git_config ( ) ,
297+ & [ "src/gcc" , "src/bootstrap/download-ci-gcc-stamp" ] ,
298+ CiEnv :: current ( ) ,
299+ )
300+ . unwrap ( ) ,
301+ )
302+ } else if let Some ( info) = crate :: utils:: channel:: read_commit_info_file ( & config. src ) {
303+ Some ( PathFreshness :: LastModifiedUpstream { upstream : info. sha . trim ( ) . to_owned ( ) } )
304+ } else {
305+ None
306+ } ;
307+
308+ let Some ( freshness) = freshness else {
309+ eprintln ! ( "error: could not find commit hash for downloading GCC" ) ;
310+ eprintln ! ( "HELP: maybe your repository history is too shallow?" ) ;
311+ eprintln ! ( "HELP: consider disabling `download-ci-gcc`" ) ;
312+ eprintln ! ( "HELP: or fetch enough history to include one upstream commit" ) ;
313+ panic ! ( ) ;
314+ } ;
315+
316+ freshness
183317}
0 commit comments