1
- use crate :: utils:: { get_os_name, rustc_version_info, split_args} ;
1
+ use crate :: utils:: { get_os_name, run_command_with_output , rustc_version_info, split_args} ;
2
2
use std:: collections:: HashMap ;
3
3
use std:: env as std_env;
4
4
use std:: ffi:: OsStr ;
5
5
use std:: fs;
6
- use std:: path:: Path ;
6
+ use std:: path:: { Path , PathBuf } ;
7
7
8
8
use boml:: { types:: TomlValue , Toml } ;
9
9
@@ -23,8 +23,12 @@ impl Channel {
23
23
}
24
24
}
25
25
26
- fn failed_config_parsing ( config_file : & str , err : & str ) -> Result < ConfigFile , String > {
27
- Err ( format ! ( "Failed to parse `{}`: {}" , config_file, err) )
26
+ fn failed_config_parsing ( config_file : & Path , err : & str ) -> Result < ConfigFile , String > {
27
+ Err ( format ! (
28
+ "Failed to parse `{}`: {}" ,
29
+ config_file. display( ) ,
30
+ err
31
+ ) )
28
32
}
29
33
30
34
#[ derive( Default ) ]
@@ -34,12 +38,11 @@ pub struct ConfigFile {
34
38
}
35
39
36
40
impl ConfigFile {
37
- pub fn new ( config_file : Option < & str > ) -> Result < Self , String > {
38
- let config_file = config_file. unwrap_or ( "config.toml" ) ;
41
+ pub fn new ( config_file : & Path ) -> Result < Self , String > {
39
42
let content = fs:: read_to_string ( config_file) . map_err ( |_| {
40
43
format ! (
41
44
"Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project" ,
42
- config_file,
45
+ config_file. display ( ) ,
43
46
)
44
47
} ) ?;
45
48
let toml = Toml :: parse ( & content) . map_err ( |err| {
@@ -70,19 +73,30 @@ impl ConfigFile {
70
73
_ => return failed_config_parsing ( config_file, & format ! ( "Unknown key `{}`" , key) ) ,
71
74
}
72
75
}
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 ( ) ;
76
+ match ( config. gcc_path . as_mut ( ) , config. download_gccjit ) {
77
+ ( None , None | Some ( false ) ) => {
78
+ return failed_config_parsing (
79
+ config_file,
80
+ "At least one of `gcc-path` or `download-gccjit` value must be set" ,
81
+ )
82
+ }
83
+ ( Some ( _) , Some ( true ) ) => {
84
+ println ! (
85
+ "WARNING: both `gcc-path` and `download-gccjit` arguments are used, \
86
+ ignoring `gcc-path`"
87
+ ) ;
88
+ }
89
+ ( Some ( gcc_path) , _) => {
90
+ let path = Path :: new ( gcc_path) ;
91
+ * gcc_path = path
92
+ . canonicalize ( )
93
+ . map_err ( |err| {
94
+ format ! ( "Failed to get absolute path of `{}`: {:?}" , gcc_path, err)
95
+ } ) ?
96
+ . display ( )
97
+ . to_string ( ) ;
98
+ }
99
+ _ => { }
86
100
}
87
101
Ok ( config)
88
102
}
@@ -104,6 +118,7 @@ pub struct ConfigInfo {
104
118
pub sysroot_path : String ,
105
119
pub gcc_path : String ,
106
120
config_file : Option < String > ,
121
+ cg_gcc_path : Option < PathBuf > ,
107
122
}
108
123
109
124
impl ConfigInfo {
@@ -146,6 +161,14 @@ impl ConfigInfo {
146
161
"--release-sysroot" => self . sysroot_release_channel = true ,
147
162
"--release" => self . channel = Channel :: Release ,
148
163
"--sysroot-panic-abort" => self . sysroot_panic_abort = true ,
164
+ "--cg_gcc-path" => match args. next ( ) {
165
+ Some ( arg) if !arg. is_empty ( ) => {
166
+ self . cg_gcc_path = Some ( arg. into ( ) ) ;
167
+ }
168
+ _ => {
169
+ return Err ( "Expected a value after `--cg_gcc-path`, found nothing" . to_string ( ) )
170
+ }
171
+ } ,
149
172
_ => return Ok ( false ) ,
150
173
}
151
174
Ok ( true )
@@ -159,16 +182,144 @@ impl ConfigInfo {
159
182
command
160
183
}
161
184
185
+ fn download_gccjit_if_needed ( & mut self ) -> Result < ( ) , String > {
186
+ let output_dir = Path :: new (
187
+ std:: env:: var ( "CARGO_TARGET_DIR" )
188
+ . as_deref ( )
189
+ . unwrap_or ( "target" ) ,
190
+ )
191
+ . join ( "libgccjit" ) ;
192
+
193
+ let commit_hash_file = self . compute_path ( "libgccjit.version" ) ;
194
+ let content = fs:: read_to_string ( & commit_hash_file) . map_err ( |_| {
195
+ format ! (
196
+ "Failed to read `{}`. Take a look at `Readme.md` to see how to set up the project" ,
197
+ commit_hash_file. display( ) ,
198
+ )
199
+ } ) ?;
200
+ let commit = content. trim ( ) ;
201
+ if commit. contains ( '/' ) || commit. contains ( '\\' ) {
202
+ return Err ( format ! (
203
+ "{}: invalid commit hash `{}`" ,
204
+ commit_hash_file. display( ) ,
205
+ commit
206
+ ) ) ;
207
+ }
208
+ let output_dir = output_dir. join ( commit) ;
209
+ if !output_dir. is_dir ( ) {
210
+ std:: fs:: create_dir_all ( & output_dir) . map_err ( |err| {
211
+ format ! (
212
+ "failed to create folder `{}`: {:?}" ,
213
+ output_dir. display( ) ,
214
+ err,
215
+ )
216
+ } ) ?;
217
+ }
218
+ let libgccjit_so = output_dir. join ( "libgccjit.so" ) ;
219
+ if !libgccjit_so. is_file ( ) {
220
+ // Download time!
221
+ let tempfile_name = "libgccjit.so.download" ;
222
+ let tempfile = output_dir. join ( tempfile_name) ;
223
+ let is_in_ci = std:: env:: var ( "GITHUB_ACTIONS" ) . is_ok ( ) ;
224
+
225
+ let url = format ! (
226
+ "https://github.com/antoyo/gcc/releases/download/master-{}/libgccjit.so" ,
227
+ commit,
228
+ ) ;
229
+
230
+ println ! ( "Downloading `{}`..." , url) ;
231
+ // Try curl. If that fails and we are on windows, fallback to PowerShell.
232
+ let mut ret = run_command_with_output (
233
+ & [
234
+ & "curl" ,
235
+ & "--speed-time" ,
236
+ & "30" ,
237
+ & "--speed-limit" ,
238
+ & "10" , // timeout if speed is < 10 bytes/sec for > 30 seconds
239
+ & "--connect-timeout" ,
240
+ & "30" , // timeout if cannot connect within 30 seconds
241
+ & "-o" ,
242
+ & tempfile_name,
243
+ & "--retry" ,
244
+ & "3" ,
245
+ & "-SRfL" ,
246
+ if is_in_ci { & "-s" } else { & "--progress-bar" } ,
247
+ & url. as_str ( ) ,
248
+ ] ,
249
+ Some ( & output_dir) ,
250
+ ) ;
251
+ if ret. is_err ( ) && cfg ! ( windows) {
252
+ eprintln ! ( "Fallback to PowerShell" ) ;
253
+ ret = run_command_with_output (
254
+ & [
255
+ & "PowerShell.exe" ,
256
+ & "/nologo" ,
257
+ & "-Command" ,
258
+ & "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;" ,
259
+ & format ! (
260
+ "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')" ,
261
+ url,
262
+ tempfile_name,
263
+ ) . as_str ( ) ,
264
+ ] ,
265
+ Some ( & output_dir) ,
266
+ ) ;
267
+ }
268
+ ret?;
269
+
270
+ // If we reach this point, it means the file was correctly downloaded, so let's
271
+ // rename it!
272
+ std:: fs:: rename ( & tempfile, & libgccjit_so) . map_err ( |err| {
273
+ format ! (
274
+ "Failed to rename `{}` into `{}`: {:?}" ,
275
+ tempfile. display( ) ,
276
+ libgccjit_so. display( ) ,
277
+ err,
278
+ )
279
+ } ) ?;
280
+
281
+ println ! ( "Downloaded libgccjit.so version {} successfully!" , commit) ;
282
+ }
283
+
284
+ self . gcc_path = output_dir
285
+ . canonicalize ( )
286
+ . map_err ( |err| {
287
+ format ! (
288
+ "Failed to get absolute path of `{}`: {:?}" ,
289
+ output_dir. display( ) ,
290
+ err
291
+ )
292
+ } ) ?
293
+ . display ( )
294
+ . to_string ( ) ;
295
+ println ! ( "Using `{}` as path for libgccjit" , self . gcc_path) ;
296
+ Ok ( ( ) )
297
+ }
298
+
299
+ pub fn compute_path < P : AsRef < Path > > ( & self , other : P ) -> PathBuf {
300
+ match self . cg_gcc_path {
301
+ Some ( ref path) => path. join ( other) ,
302
+ None => PathBuf :: new ( ) . join ( other) ,
303
+ }
304
+ }
305
+
162
306
pub fn setup_gcc_path ( & mut self ) -> Result < ( ) , String > {
163
- let ConfigFile { gcc_path, .. } = ConfigFile :: new ( self . config_file . as_deref ( ) ) ?;
307
+ let config_file = self . compute_path ( self . config_file . as_deref ( ) . unwrap_or ( "config.toml" ) ) ;
308
+ let ConfigFile {
309
+ gcc_path,
310
+ download_gccjit,
311
+ } = ConfigFile :: new ( & config_file) ?;
164
312
313
+ if let Some ( true ) = download_gccjit {
314
+ self . download_gccjit_if_needed ( ) ?;
315
+ return Ok ( ( ) ) ;
316
+ }
165
317
self . gcc_path = match gcc_path {
166
318
Some ( path) => path,
167
- // FIXME: Once we support "download", rewrite this.
168
319
None => {
169
320
return Err ( format ! (
170
321
"missing `gcc-path` value from `{}`" ,
171
- self . config_file. as_deref ( ) . unwrap_or ( "config.toml" ) ,
322
+ config_file. display ( ) ,
172
323
) )
173
324
}
174
325
} ;
@@ -362,7 +513,9 @@ impl ConfigInfo {
362
513
--release : Build in release mode
363
514
--release-sysroot : Build sysroot in release mode
364
515
--sysroot-panic-abort : Build the sysroot without unwinding support
365
- --config-file : Location of the config file to be used"
516
+ --config-file : Location of the config file to be used
517
+ --cg_gcc-path : Location of the rustc_codegen_gcc root folder (used
518
+ for accessing any file from the project)"
366
519
) ;
367
520
}
368
521
}
0 commit comments