@@ -14,13 +14,68 @@ use std::sync::OnceLock;
1414
1515use build_helper:: ci:: CiEnv ;
1616
17- use crate :: Kind ;
18- use crate :: core:: builder:: { Builder , Cargo , RunConfig , ShouldRun , Step } ;
17+ use crate :: Config ;
18+ use crate :: core:: builder:: { Builder , Cargo , Kind , RunConfig , ShouldRun , Step } ;
1919use crate :: core:: config:: TargetSelection ;
2020use crate :: utils:: build_stamp:: { BuildStamp , generate_smart_stamp_hash} ;
2121use crate :: utils:: exec:: command;
2222use crate :: utils:: helpers:: { self , t} ;
2323
24+ #[ derive( Debug , Clone , Hash , PartialEq , Eq ) ]
25+ pub struct Gcc {
26+ pub target : TargetSelection ,
27+ }
28+
29+ #[ derive( Clone ) ]
30+ pub struct GccOutput {
31+ pub libgccjit : PathBuf ,
32+ }
33+
34+ impl Step for Gcc {
35+ type Output = GccOutput ;
36+
37+ const ONLY_HOSTS : bool = true ;
38+
39+ fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
40+ run. path ( "src/gcc" ) . alias ( "gcc" )
41+ }
42+
43+ fn make_run ( run : RunConfig < ' _ > ) {
44+ run. builder . ensure ( Gcc { target : run. target } ) ;
45+ }
46+
47+ /// Compile GCC (specifically `libgccjit`) for `target`.
48+ fn run ( self , builder : & Builder < ' _ > ) -> Self :: Output {
49+ let target = self . target ;
50+
51+ // If GCC has already been built, we avoid building it again.
52+ let metadata = match get_gcc_build_status ( builder, target) {
53+ GccBuildStatus :: AlreadyBuilt ( path) => return GccOutput { libgccjit : path } ,
54+ GccBuildStatus :: ShouldBuild ( m) => m,
55+ } ;
56+
57+ let _guard = builder. msg_unstaged ( Kind :: Build , "GCC" , target) ;
58+ t ! ( metadata. stamp. remove( ) ) ;
59+ let _time = helpers:: timeit ( builder) ;
60+
61+ let libgccjit_path = libgccjit_built_path ( & metadata. install_dir ) ;
62+ if builder. config . dry_run ( ) {
63+ return GccOutput { libgccjit : libgccjit_path } ;
64+ }
65+
66+ build_gcc ( & metadata, builder, target) ;
67+
68+ let lib_alias = metadata. install_dir . join ( "lib/libgccjit.so.0" ) ;
69+ if !lib_alias. exists ( ) {
70+ t ! ( builder. symlink_file( & libgccjit_path, lib_alias) ) ;
71+ }
72+
73+ t ! ( metadata. stamp. write( ) ) ;
74+
75+ GccOutput { libgccjit : libgccjit_path }
76+ }
77+ }
78+
2479pub struct Meta {
2580 stamp : BuildStamp ,
2681 out_dir : PathBuf ,
@@ -38,7 +93,7 @@ pub enum GccBuildStatus {
3893///
3994/// It's used to avoid busting caches during x.py check -- if we've already built
4095/// GCC, it's fine for us to not try to avoid doing so.
41- pub fn prebuilt_gcc_config ( builder : & Builder < ' _ > , target : TargetSelection ) -> GccBuildStatus {
96+ pub fn get_gcc_build_status ( builder : & Builder < ' _ > , target : TargetSelection ) -> GccBuildStatus {
4297 // Initialize the gcc submodule if not initialized already.
4398 builder. config . update_submodule ( "src/gcc" ) ;
4499
@@ -87,116 +142,67 @@ fn libgccjit_built_path(install_dir: &Path) -> PathBuf {
87142 install_dir. join ( "lib/libgccjit.so" )
88143}
89144
90- #[ derive( Debug , Clone , Hash , PartialEq , Eq ) ]
91- pub struct Gcc {
92- pub target : TargetSelection ,
93- }
145+ fn build_gcc ( metadata : & Meta , builder : & Builder , target : TargetSelection ) {
146+ let Meta { stamp : _, out_dir, install_dir, root } = metadata;
94147
95- #[ derive( Clone ) ]
96- pub struct GccOutput {
97- pub libgccjit : PathBuf ,
98- }
99-
100- impl Step for Gcc {
101- type Output = GccOutput ;
102-
103- const ONLY_HOSTS : bool = true ;
104-
105- fn should_run ( run : ShouldRun < ' _ > ) -> ShouldRun < ' _ > {
106- run. path ( "src/gcc" ) . alias ( "gcc" )
107- }
108-
109- fn make_run ( run : RunConfig < ' _ > ) {
110- run. builder . ensure ( Gcc { target : run. target } ) ;
111- }
112-
113- /// Compile GCC (specifically `libgccjit`) for `target`.
114- fn run ( self , builder : & Builder < ' _ > ) -> Self :: Output {
115- let target = self . target ;
148+ t ! ( fs:: create_dir_all( out_dir) ) ;
116149
117- // If GCC has already been built, we avoid building it again.
118- let Meta { stamp, out_dir, install_dir, root } = match prebuilt_gcc_config ( builder, target)
119- {
120- GccBuildStatus :: AlreadyBuilt ( path) => return GccOutput { libgccjit : path } ,
121- GccBuildStatus :: ShouldBuild ( m) => m,
122- } ;
123-
124- let _guard = builder. msg_unstaged ( Kind :: Build , "GCC" , target) ;
125- t ! ( stamp. remove( ) ) ;
126- let _time = helpers:: timeit ( builder) ;
127- t ! ( fs:: create_dir_all( & out_dir) ) ;
128-
129- let libgccjit_path = libgccjit_built_path ( & install_dir) ;
130- if builder. config . dry_run ( ) {
131- return GccOutput { libgccjit : libgccjit_path } ;
150+ // GCC creates files (e.g. symlinks to the downloaded dependencies)
151+ // in the source directory, which does not work with our CI setup, where we mount
152+ // source directories as read-only on Linux.
153+ // Therefore, as a part of the build in CI, we first copy the whole source directory
154+ // to the build directory, and perform the build from there.
155+ let src_dir = if CiEnv :: is_ci ( ) {
156+ let src_dir = builder. gcc_out ( target) . join ( "src" ) ;
157+ if src_dir. exists ( ) {
158+ builder. remove_dir ( & src_dir) ;
132159 }
133-
134- // GCC creates files (e.g. symlinks to the downloaded dependencies)
135- // in the source directory, which does not work with our CI setup, where we mount
136- // source directories as read-only on Linux.
137- // Therefore, as a part of the build in CI, we first copy the whole source directory
138- // to the build directory, and perform the build from there.
139- let src_dir = if CiEnv :: is_ci ( ) {
140- let src_dir = builder. gcc_out ( target) . join ( "src" ) ;
141- if src_dir. exists ( ) {
142- builder. remove_dir ( & src_dir) ;
143- }
144- builder. create_dir ( & src_dir) ;
145- builder. cp_link_r ( & root, & src_dir) ;
146- src_dir
147- } else {
148- root
149- } ;
150-
151- command ( src_dir. join ( "contrib/download_prerequisites" ) ) . current_dir ( & src_dir) . run ( builder) ;
152- let mut configure_cmd = command ( src_dir. join ( "configure" ) ) ;
153- configure_cmd
154- . current_dir ( & out_dir)
155- // On CI, we compile GCC with Clang.
156- // The -Wno-everything flag is needed to make GCC compile with Clang 19.
157- // `-g -O2` are the default flags that are otherwise used by Make.
158- // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml.
159- . env ( "CXXFLAGS" , "-Wno-everything -g -O2" )
160- . env ( "CFLAGS" , "-Wno-everything -g -O2" )
161- . arg ( "--enable-host-shared" )
162- . arg ( "--enable-languages=jit" )
163- . arg ( "--enable-checking=release" )
164- . arg ( "--disable-bootstrap" )
165- . arg ( "--disable-multilib" )
166- . arg ( format ! ( "--prefix={}" , install_dir. display( ) ) ) ;
167- let cc = builder. build . cc ( target) . display ( ) . to_string ( ) ;
168- let cc = builder
160+ builder. create_dir ( & src_dir) ;
161+ builder. cp_link_r ( root, & src_dir) ;
162+ src_dir
163+ } else {
164+ root. clone ( )
165+ } ;
166+
167+ command ( src_dir. join ( "contrib/download_prerequisites" ) ) . current_dir ( & src_dir) . run ( builder) ;
168+ let mut configure_cmd = command ( src_dir. join ( "configure" ) ) ;
169+ configure_cmd
170+ . current_dir ( out_dir)
171+ // On CI, we compile GCC with Clang.
172+ // The -Wno-everything flag is needed to make GCC compile with Clang 19.
173+ // `-g -O2` are the default flags that are otherwise used by Make.
174+ // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml.
175+ . env ( "CXXFLAGS" , "-Wno-everything -g -O2" )
176+ . env ( "CFLAGS" , "-Wno-everything -g -O2" )
177+ . arg ( "--enable-host-shared" )
178+ . arg ( "--enable-languages=jit" )
179+ . arg ( "--enable-checking=release" )
180+ . arg ( "--disable-bootstrap" )
181+ . arg ( "--disable-multilib" )
182+ . arg ( format ! ( "--prefix={}" , install_dir. display( ) ) ) ;
183+ let cc = builder. build . cc ( target) . display ( ) . to_string ( ) ;
184+ let cc = builder
185+ . build
186+ . config
187+ . ccache
188+ . as_ref ( )
189+ . map_or_else ( || cc. clone ( ) , |ccache| format ! ( "{ccache} {cc}" ) ) ;
190+ configure_cmd. env ( "CC" , cc) ;
191+
192+ if let Ok ( ref cxx) = builder. build . cxx ( target) {
193+ let cxx = cxx. display ( ) . to_string ( ) ;
194+ let cxx = builder
169195 . build
170196 . config
171197 . ccache
172198 . as_ref ( )
173- . map_or_else ( || cc. clone ( ) , |ccache| format ! ( "{ccache} {cc}" ) ) ;
174- configure_cmd. env ( "CC" , cc) ;
175-
176- if let Ok ( ref cxx) = builder. build . cxx ( target) {
177- let cxx = cxx. display ( ) . to_string ( ) ;
178- let cxx = builder
179- . build
180- . config
181- . ccache
182- . as_ref ( )
183- . map_or_else ( || cxx. clone ( ) , |ccache| format ! ( "{ccache} {cxx}" ) ) ;
184- configure_cmd. env ( "CXX" , cxx) ;
185- }
186- configure_cmd. run ( builder) ;
187-
188- command ( "make" ) . current_dir ( & out_dir) . arg ( format ! ( "-j{}" , builder. jobs( ) ) ) . run ( builder) ;
189- command ( "make" ) . current_dir ( & out_dir) . arg ( "install" ) . run ( builder) ;
190-
191- let lib_alias = install_dir. join ( "lib/libgccjit.so.0" ) ;
192- if !lib_alias. exists ( ) {
193- t ! ( builder. symlink_file( & libgccjit_path, lib_alias) ) ;
194- }
195-
196- t ! ( stamp. write( ) ) ;
197-
198- GccOutput { libgccjit : libgccjit_path }
199+ . map_or_else ( || cxx. clone ( ) , |ccache| format ! ( "{ccache} {cxx}" ) ) ;
200+ configure_cmd. env ( "CXX" , cxx) ;
199201 }
202+ configure_cmd. run ( builder) ;
203+
204+ command ( "make" ) . current_dir ( out_dir) . arg ( format ! ( "-j{}" , builder. jobs( ) ) ) . run ( builder) ;
205+ command ( "make" ) . current_dir ( out_dir) . arg ( "install" ) . run ( builder) ;
200206}
201207
202208/// Configures a Cargo invocation so that it can build the GCC codegen backend.
0 commit comments