1- use crate :: utils:: { get_os_name, rustc_version_info, split_args} ;
1+ use crate :: utils:: {
2+ create_symlink, get_os_name, run_command_with_output, rustc_version_info, split_args,
3+ } ;
24use std:: collections:: HashMap ;
35use std:: env as std_env;
46use std:: ffi:: OsStr ;
57use std:: fs;
6- use std:: path:: Path ;
8+ use std:: path:: { Path , PathBuf } ;
79
810use boml:: { types:: TomlValue , Toml } ;
911
@@ -23,8 +25,12 @@ impl Channel {
2325 }
2426}
2527
26- fn failed_config_parsing ( config_file : & str , err : & str ) -> Result < ConfigFile , String > {
27- Err ( format ! ( "Failed to parse `{}`: {}" , config_file, err) )
28+ fn failed_config_parsing ( config_file : & Path , err : & str ) -> Result < ConfigFile , String > {
29+ Err ( format ! (
30+ "Failed to parse `{}`: {}" ,
31+ config_file. display( ) ,
32+ err
33+ ) )
2834}
2935
3036#[ derive( Default ) ]
@@ -34,12 +40,11 @@ pub struct ConfigFile {
3440}
3541
3642impl ConfigFile {
37- pub fn new ( config_file : Option < & str > ) -> Result < Self , String > {
38- let config_file = config_file. unwrap_or ( "config.toml" ) ;
43+ pub fn new ( config_file : & Path ) -> Result < Self , String > {
3944 let content = fs:: read_to_string ( config_file) . map_err ( |_| {
4045 format ! (
4146 "Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project" ,
42- config_file,
47+ config_file. display ( ) ,
4348 )
4449 } ) ?;
4550 let toml = Toml :: parse ( & content) . map_err ( |err| {
@@ -70,19 +75,30 @@ impl ConfigFile {
7075 _ => return failed_config_parsing ( config_file, & format ! ( "Unknown key `{}`" , key) ) ,
7176 }
7277 }
73- if config. gcc_path . is_none ( ) && config. download_gccjit . is_none ( ) {
74- return failed_config_parsing (
75- config_file,
76- "At least one of `gcc-path` or `download-gccjit` value must be set" ,
77- ) ;
78- }
79- if let Some ( gcc_path) = config. gcc_path . as_mut ( ) {
80- let path = Path :: new ( gcc_path) ;
81- * gcc_path = path
82- . canonicalize ( )
83- . map_err ( |err| format ! ( "Failed to get absolute path of `{}`: {:?}" , gcc_path, err) ) ?
84- . display ( )
85- . to_string ( ) ;
78+ match ( config. gcc_path . as_mut ( ) , config. download_gccjit ) {
79+ ( None , None | Some ( false ) ) => {
80+ return failed_config_parsing (
81+ config_file,
82+ "At least one of `gcc-path` or `download-gccjit` value must be set" ,
83+ )
84+ }
85+ ( Some ( _) , Some ( true ) ) => {
86+ println ! (
87+ "WARNING: both `gcc-path` and `download-gccjit` arguments are used, \
88+ ignoring `gcc-path`"
89+ ) ;
90+ }
91+ ( Some ( gcc_path) , _) => {
92+ let path = Path :: new ( gcc_path) ;
93+ * gcc_path = path
94+ . canonicalize ( )
95+ . map_err ( |err| {
96+ format ! ( "Failed to get absolute path of `{}`: {:?}" , gcc_path, err)
97+ } ) ?
98+ . display ( )
99+ . to_string ( ) ;
100+ }
101+ _ => { }
86102 }
87103 Ok ( config)
88104 }
@@ -104,6 +120,13 @@ pub struct ConfigInfo {
104120 pub sysroot_path : String ,
105121 pub gcc_path : String ,
106122 config_file : Option < String > ,
123+ // This is used in particular in rust compiler bootstrap because it doesn't run at the root
124+ // of the `cg_gcc` folder, making it complicated for us to get access to local files we need
125+ // like `libgccjit.version` or `config.toml`.
126+ cg_gcc_path : Option < PathBuf > ,
127+ // Needed for the `info` command which doesn't want to actually download the lib if needed,
128+ // just to set the `gcc_path` field to display it.
129+ pub no_download : bool ,
107130}
108131
109132impl ConfigInfo {
@@ -146,6 +169,14 @@ impl ConfigInfo {
146169 "--release-sysroot" => self . sysroot_release_channel = true ,
147170 "--release" => self . channel = Channel :: Release ,
148171 "--sysroot-panic-abort" => self . sysroot_panic_abort = true ,
172+ "--cg_gcc-path" => match args. next ( ) {
173+ Some ( arg) if !arg. is_empty ( ) => {
174+ self . cg_gcc_path = Some ( arg. into ( ) ) ;
175+ }
176+ _ => {
177+ return Err ( "Expected a value after `--cg_gcc-path`, found nothing" . to_string ( ) )
178+ }
179+ } ,
149180 _ => return Ok ( false ) ,
150181 }
151182 Ok ( true )
@@ -159,30 +190,165 @@ impl ConfigInfo {
159190 command
160191 }
161192
162- pub fn setup_gcc_path ( & mut self , override_gcc_path : Option < & str > ) -> Result < ( ) , String > {
163- let ConfigFile { gcc_path, .. } = ConfigFile :: new ( self . config_file . as_deref ( ) ) ?;
193+ fn download_gccjit (
194+ & self ,
195+ output_dir : & Path ,
196+ libgccjit_so_name : & str ,
197+ commit : & str ,
198+ ) -> Result < ( ) , String > {
199+ // Download time!
200+ let tempfile_name = format ! ( "{}.download" , libgccjit_so_name) ;
201+ let tempfile = output_dir. join ( & tempfile_name) ;
202+ let is_in_ci = std:: env:: var ( "GITHUB_ACTIONS" ) . is_ok ( ) ;
164203
165- self . gcc_path = match override_gcc_path {
166- Some ( path) => {
167- if gcc_path. is_some ( ) {
168- println ! (
169- "overriding setting from `{}`" ,
170- self . config_file. as_deref( ) . unwrap_or( "config.toml" )
171- ) ;
172- }
173- path. to_string ( )
174- }
204+ let url = format ! (
205+ "https://github.com/antoyo/gcc/releases/download/master-{}/libgccjit.so" ,
206+ commit,
207+ ) ;
208+
209+ println ! ( "Downloading `{}`..." , url) ;
210+ // Try curl. If that fails and we are on windows, fallback to PowerShell.
211+ let mut ret = run_command_with_output (
212+ & [
213+ & "curl" ,
214+ & "--speed-time" ,
215+ & "30" ,
216+ & "--speed-limit" ,
217+ & "10" , // timeout if speed is < 10 bytes/sec for > 30 seconds
218+ & "--connect-timeout" ,
219+ & "30" , // timeout if cannot connect within 30 seconds
220+ & "-o" ,
221+ & tempfile_name,
222+ & "--retry" ,
223+ & "3" ,
224+ & "-SRfL" ,
225+ if is_in_ci { & "-s" } else { & "--progress-bar" } ,
226+ & url. as_str ( ) ,
227+ ] ,
228+ Some ( & output_dir) ,
229+ ) ;
230+ if ret. is_err ( ) && cfg ! ( windows) {
231+ eprintln ! ( "Fallback to PowerShell" ) ;
232+ ret = run_command_with_output (
233+ & [
234+ & "PowerShell.exe" ,
235+ & "/nologo" ,
236+ & "-Command" ,
237+ & "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;" ,
238+ & format ! (
239+ "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')" ,
240+ url,
241+ tempfile_name,
242+ ) . as_str ( ) ,
243+ ] ,
244+ Some ( & output_dir) ,
245+ ) ;
246+ }
247+ ret?;
248+
249+ let libgccjit_so = output_dir. join ( libgccjit_so_name) ;
250+ // If we reach this point, it means the file was correctly downloaded, so let's
251+ // rename it!
252+ std:: fs:: rename ( & tempfile, & libgccjit_so) . map_err ( |err| {
253+ format ! (
254+ "Failed to rename `{}` into `{}`: {:?}" ,
255+ tempfile. display( ) ,
256+ libgccjit_so. display( ) ,
257+ err,
258+ )
259+ } ) ?;
260+
261+ println ! ( "Downloaded libgccjit.so version {} successfully!" , commit) ;
262+ // We need to create a link named `libgccjit.so.0` because that's what the linker is
263+ // looking for.
264+ create_symlink (
265+ & libgccjit_so,
266+ output_dir. join ( & format ! ( "{}.0" , libgccjit_so_name) ) ,
267+ )
268+ }
269+
270+ fn download_gccjit_if_needed ( & mut self ) -> Result < ( ) , String > {
271+ let output_dir = Path :: new (
272+ std:: env:: var ( "CARGO_TARGET_DIR" )
273+ . as_deref ( )
274+ . unwrap_or ( "target" ) ,
275+ )
276+ . join ( "libgccjit" ) ;
277+
278+ let commit_hash_file = self . compute_path ( "libgccjit.version" ) ;
279+ let content = fs:: read_to_string ( & commit_hash_file) . map_err ( |_| {
280+ format ! (
281+ "Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project" ,
282+ commit_hash_file. display( ) ,
283+ )
284+ } ) ?;
285+ let commit = content. trim ( ) ;
286+ // This is a very simple check to ensure this is not a path. For the rest, it'll just fail
287+ // when trying to download the file so we should be fine.
288+ if commit. contains ( '/' ) || commit. contains ( '\\' ) {
289+ return Err ( format ! (
290+ "{}: invalid commit hash `{}`" ,
291+ commit_hash_file. display( ) ,
292+ commit,
293+ ) ) ;
294+ }
295+ let output_dir = output_dir. join ( commit) ;
296+ if !output_dir. is_dir ( ) {
297+ std:: fs:: create_dir_all ( & output_dir) . map_err ( |err| {
298+ format ! (
299+ "failed to create folder `{}`: {:?}" ,
300+ output_dir. display( ) ,
301+ err,
302+ )
303+ } ) ?;
304+ }
305+ let output_dir = output_dir. canonicalize ( ) . map_err ( |err| {
306+ format ! (
307+ "Failed to get absolute path of `{}`: {:?}" ,
308+ output_dir. display( ) ,
309+ err
310+ )
311+ } ) ?;
312+
313+ let libgccjit_so_name = "libgccjit.so" ;
314+ let libgccjit_so = output_dir. join ( libgccjit_so_name) ;
315+ if !libgccjit_so. is_file ( ) && !self . no_download {
316+ self . download_gccjit ( & output_dir, libgccjit_so_name, commit) ?;
317+ }
318+
319+ self . gcc_path = output_dir. display ( ) . to_string ( ) ;
320+ println ! ( "Using `{}` as path for libgccjit" , self . gcc_path) ;
321+ Ok ( ( ) )
322+ }
323+
324+ pub fn compute_path < P : AsRef < Path > > ( & self , other : P ) -> PathBuf {
325+ match self . cg_gcc_path {
326+ Some ( ref path) => path. join ( other) ,
327+ None => PathBuf :: new ( ) . join ( other) ,
328+ }
329+ }
330+
331+ pub fn setup_gcc_path ( & mut self ) -> Result < ( ) , String > {
332+ let config_file = match self . config_file . as_deref ( ) {
333+ Some ( config_file) => config_file. into ( ) ,
334+ None => self . compute_path ( "config.toml" ) ,
335+ } ;
336+ let ConfigFile {
337+ gcc_path,
338+ download_gccjit,
339+ } = ConfigFile :: new ( & config_file) ?;
340+
341+ if let Some ( true ) = download_gccjit {
342+ self . download_gccjit_if_needed ( ) ?;
343+ return Ok ( ( ) ) ;
344+ }
345+ self . gcc_path = match gcc_path {
346+ Some ( path) => path,
175347 None => {
176- match gcc_path {
177- Some ( path) => path,
178- // FIXME: Once we support "download", rewrite this.
179- None => {
180- return Err ( format ! (
181- "missing `gcc-path` value from `{}`" ,
182- self . config_file. as_deref( ) . unwrap_or( "config.toml" ) ,
183- ) )
184- }
185- }
348+ return Err ( format ! (
349+ "missing `gcc-path` value from `{}`" ,
350+ config_file. display( ) ,
351+ ) )
186352 }
187353 } ;
188354 Ok ( ( ) )
@@ -191,12 +357,12 @@ impl ConfigInfo {
191357 pub fn setup (
192358 & mut self ,
193359 env : & mut HashMap < String , String > ,
194- override_gcc_path : Option < & str > ,
360+ use_system_gcc : bool ,
195361 ) -> Result < ( ) , String > {
196362 env. insert ( "CARGO_INCREMENTAL" . to_string ( ) , "0" . to_string ( ) ) ;
197363
198- if self . gcc_path . is_empty ( ) || override_gcc_path . is_some ( ) {
199- self . setup_gcc_path ( override_gcc_path ) ?;
364+ if self . gcc_path . is_empty ( ) && !use_system_gcc {
365+ self . setup_gcc_path ( ) ?;
200366 }
201367 env. insert ( "GCC_PATH" . to_string ( ) , self . gcc_path . clone ( ) ) ;
202368
@@ -375,7 +541,9 @@ impl ConfigInfo {
375541 --release : Build in release mode
376542 --release-sysroot : Build sysroot in release mode
377543 --sysroot-panic-abort : Build the sysroot without unwinding support
378- --config-file : Location of the config file to be used"
544+ --config-file : Location of the config file to be used
545+ --cg_gcc-path : Location of the rustc_codegen_gcc root folder (used
546+ when ran from another directory)"
379547 ) ;
380548 }
381549}
0 commit comments