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
+ } ;
2
4
use std:: collections:: HashMap ;
3
5
use std:: env as std_env;
4
6
use std:: ffi:: OsStr ;
5
7
use std:: fs;
6
- use std:: path:: Path ;
8
+ use std:: path:: { Path , PathBuf } ;
7
9
8
10
use boml:: { types:: TomlValue , Toml } ;
9
11
@@ -23,8 +25,12 @@ impl Channel {
23
25
}
24
26
}
25
27
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
+ ) )
28
34
}
29
35
30
36
#[ derive( Default ) ]
@@ -34,12 +40,11 @@ pub struct ConfigFile {
34
40
}
35
41
36
42
impl 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 > {
39
44
let content = fs:: read_to_string ( config_file) . map_err ( |_| {
40
45
format ! (
41
46
"Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project" ,
42
- config_file,
47
+ config_file. display ( ) ,
43
48
)
44
49
} ) ?;
45
50
let toml = Toml :: parse ( & content) . map_err ( |err| {
@@ -70,19 +75,30 @@ impl ConfigFile {
70
75
_ => return failed_config_parsing ( config_file, & format ! ( "Unknown key `{}`" , key) ) ,
71
76
}
72
77
}
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
+ _ => { }
86
102
}
87
103
Ok ( config)
88
104
}
@@ -104,6 +120,13 @@ pub struct ConfigInfo {
104
120
pub sysroot_path : String ,
105
121
pub gcc_path : String ,
106
122
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 ,
107
130
}
108
131
109
132
impl ConfigInfo {
@@ -146,6 +169,14 @@ impl ConfigInfo {
146
169
"--release-sysroot" => self . sysroot_release_channel = true ,
147
170
"--release" => self . channel = Channel :: Release ,
148
171
"--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
+ } ,
149
180
_ => return Ok ( false ) ,
150
181
}
151
182
Ok ( true )
@@ -159,30 +190,165 @@ impl ConfigInfo {
159
190
command
160
191
}
161
192
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 ( ) ;
164
203
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,
175
347
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
+ ) )
186
352
}
187
353
} ;
188
354
Ok ( ( ) )
@@ -191,12 +357,12 @@ impl ConfigInfo {
191
357
pub fn setup (
192
358
& mut self ,
193
359
env : & mut HashMap < String , String > ,
194
- override_gcc_path : Option < & str > ,
360
+ use_system_gcc : bool ,
195
361
) -> Result < ( ) , String > {
196
362
env. insert ( "CARGO_INCREMENTAL" . to_string ( ) , "0" . to_string ( ) ) ;
197
363
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 ( ) ?;
200
366
}
201
367
env. insert ( "GCC_PATH" . to_string ( ) , self . gcc_path . clone ( ) ) ;
202
368
@@ -375,7 +541,9 @@ impl ConfigInfo {
375
541
--release : Build in release mode
376
542
--release-sysroot : Build sysroot in release mode
377
543
--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)"
379
547
) ;
380
548
}
381
549
}
0 commit comments