@@ -16,11 +16,13 @@ use la_arena::ArenaMap;
16
16
use paths:: { AbsPath , AbsPathBuf , Utf8PathBuf } ;
17
17
use rustc_hash:: { FxHashMap , FxHashSet } ;
18
18
use serde:: Deserialize as _;
19
+ use stdx:: never;
19
20
use toolchain:: Tool ;
20
21
21
22
use crate :: {
22
23
CargoConfig , CargoFeatures , CargoWorkspace , InvocationStrategy , ManifestPath , Package , Sysroot ,
23
- TargetKind , utf8_stdout,
24
+ TargetKind , cargo_config_file:: make_lockfile_copy,
25
+ cargo_workspace:: MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH , utf8_stdout,
24
26
} ;
25
27
26
28
/// Output of the build script and proc-macro building steps for a workspace.
@@ -30,6 +32,15 @@ pub struct WorkspaceBuildScripts {
30
32
error : Option < String > ,
31
33
}
32
34
35
+ #[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
36
+ pub enum ProcMacroDylibPath {
37
+ Path ( AbsPathBuf ) ,
38
+ DylibNotFound ( Box < [ Utf8PathBuf ] > ) ,
39
+ NotProcMacro ,
40
+ #[ default]
41
+ NotBuilt ,
42
+ }
43
+
33
44
/// Output of the build script and proc-macro building step for a concrete package.
34
45
#[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
35
46
pub ( crate ) struct BuildScriptOutput {
@@ -43,15 +54,15 @@ pub(crate) struct BuildScriptOutput {
43
54
/// Directory where a build script might place its output.
44
55
pub ( crate ) out_dir : Option < AbsPathBuf > ,
45
56
/// Path to the proc-macro library file if this package exposes proc-macros.
46
- pub ( crate ) proc_macro_dylib_path : Option < AbsPathBuf > ,
57
+ pub ( crate ) proc_macro_dylib_path : ProcMacroDylibPath ,
47
58
}
48
59
49
60
impl BuildScriptOutput {
50
61
fn is_empty ( & self ) -> bool {
51
62
self . cfgs . is_empty ( )
52
63
&& self . envs . is_empty ( )
53
64
&& self . out_dir . is_none ( )
54
- && self . proc_macro_dylib_path . is_none ( )
65
+ && self . proc_macro_dylib_path == ProcMacroDylibPath :: NotBuilt
55
66
}
56
67
}
57
68
@@ -67,7 +78,7 @@ impl WorkspaceBuildScripts {
67
78
let current_dir = workspace. workspace_root ( ) ;
68
79
69
80
let allowed_features = workspace. workspace_features ( ) ;
70
- let cmd = Self :: build_command (
81
+ let ( _guard , cmd) = Self :: build_command (
71
82
config,
72
83
& allowed_features,
73
84
workspace. manifest_path ( ) ,
@@ -88,7 +99,7 @@ impl WorkspaceBuildScripts {
88
99
) -> io:: Result < Vec < WorkspaceBuildScripts > > {
89
100
assert_eq ! ( config. invocation_strategy, InvocationStrategy :: Once ) ;
90
101
91
- let cmd = Self :: build_command (
102
+ let ( _guard , cmd) = Self :: build_command (
92
103
config,
93
104
& Default :: default ( ) ,
94
105
// This is not gonna be used anyways, so just construct a dummy here
@@ -126,6 +137,8 @@ impl WorkspaceBuildScripts {
126
137
|package, cb| {
127
138
if let Some ( & ( package, workspace) ) = by_id. get ( package) {
128
139
cb ( & workspaces[ workspace] [ package] . name , & mut res[ workspace] . outputs [ package] ) ;
140
+ } else {
141
+ never ! ( "Received compiler message for unknown package: {}" , package) ;
129
142
}
130
143
} ,
131
144
progress,
@@ -140,12 +153,9 @@ impl WorkspaceBuildScripts {
140
153
if tracing:: enabled!( tracing:: Level :: INFO ) {
141
154
for ( idx, workspace) in workspaces. iter ( ) . enumerate ( ) {
142
155
for package in workspace. packages ( ) {
143
- let package_build_data = & mut res[ idx] . outputs [ package] ;
156
+ let package_build_data: & mut BuildScriptOutput = & mut res[ idx] . outputs [ package] ;
144
157
if !package_build_data. is_empty ( ) {
145
- tracing:: info!(
146
- "{}: {package_build_data:?}" ,
147
- workspace[ package] . manifest. parent( ) ,
148
- ) ;
158
+ tracing:: info!( "{}: {package_build_data:?}" , workspace[ package] . manifest, ) ;
149
159
}
150
160
}
151
161
}
@@ -198,39 +208,58 @@ impl WorkspaceBuildScripts {
198
208
let path = dir_entry. path ( ) ;
199
209
let extension = path. extension ( ) ?;
200
210
if extension == std:: env:: consts:: DLL_EXTENSION {
201
- let name = path. file_stem ( ) ?. to_str ( ) ?. split_once ( '-' ) ?. 0 . to_owned ( ) ;
202
- let path = AbsPathBuf :: try_from ( Utf8PathBuf :: from_path_buf ( path) . ok ( ) ?)
203
- . ok ( ) ?;
204
- return Some ( ( name, path) ) ;
211
+ let name = path
212
+ . file_stem ( ) ?
213
+ . to_str ( ) ?
214
+ . split_once ( '-' ) ?
215
+ . 0
216
+ . trim_start_matches ( "lib" )
217
+ . to_owned ( ) ;
218
+ let path = match Utf8PathBuf :: from_path_buf ( path) {
219
+ Ok ( path) => path,
220
+ Err ( path) => {
221
+ tracing:: warn!(
222
+ "Proc-macro dylib path contains non-UTF8 characters: {:?}" ,
223
+ path. display( )
224
+ ) ;
225
+ return None ;
226
+ }
227
+ } ;
228
+ return match AbsPathBuf :: try_from ( path) {
229
+ Ok ( path) => Some ( ( name, path) ) ,
230
+ Err ( path) => {
231
+ tracing:: error!(
232
+ "proc-macro dylib path is not absolute: {:?}" ,
233
+ path
234
+ ) ;
235
+ None
236
+ }
237
+ } ;
205
238
}
206
239
}
207
240
None
208
241
} )
209
242
. collect ( ) ;
210
243
for p in rustc. packages ( ) {
211
244
let package = & rustc[ p] ;
212
- if package
213
- . targets
214
- . iter ( )
215
- . any ( | & it| matches ! ( rustc [ it ] . kind , TargetKind :: Lib { is_proc_macro : true } ) )
216
- {
217
- if let Some ( ( _, path) ) = proc_macro_dylibs
218
- . iter ( )
219
- . find ( | ( name , _ ) | * name . trim_start_matches ( "lib" ) == package . name )
220
- {
221
- bs . outputs [ p ] . proc_macro_dylib_path = Some ( path . clone ( ) ) ;
245
+ bs . outputs [ p ] . proc_macro_dylib_path =
246
+ if package . targets . iter ( ) . any ( | & it| {
247
+ matches ! ( rustc [ it ] . kind , TargetKind :: Lib { is_proc_macro : true } )
248
+ } ) {
249
+ match proc_macro_dylibs . iter ( ) . find ( | ( name , _ ) | * name == package . name ) {
250
+ Some ( ( _, path) ) => ProcMacroDylibPath :: Path ( path . clone ( ) ) ,
251
+ _ => ProcMacroDylibPath :: DylibNotFound ( Box :: default ( ) ) ,
252
+ }
253
+ } else {
254
+ ProcMacroDylibPath :: NotProcMacro
222
255
}
223
- }
224
256
}
225
257
226
258
if tracing:: enabled!( tracing:: Level :: INFO ) {
227
259
for package in rustc. packages ( ) {
228
260
let package_build_data = & bs. outputs [ package] ;
229
261
if !package_build_data. is_empty ( ) {
230
- tracing:: info!(
231
- "{}: {package_build_data:?}" ,
232
- rustc[ package] . manifest. parent( ) ,
233
- ) ;
262
+ tracing:: info!( "{}: {package_build_data:?}" , rustc[ package] . manifest, ) ;
234
263
}
235
264
}
236
265
}
@@ -263,6 +292,12 @@ impl WorkspaceBuildScripts {
263
292
|package, cb| {
264
293
if let Some ( & package) = by_id. get ( package) {
265
294
cb ( & workspace[ package] . name , & mut outputs[ package] ) ;
295
+ } else {
296
+ never ! (
297
+ "Received compiler message for unknown package: {}\n {}" ,
298
+ package,
299
+ by_id. keys( ) . join( ", " )
300
+ ) ;
266
301
}
267
302
} ,
268
303
progress,
@@ -272,10 +307,7 @@ impl WorkspaceBuildScripts {
272
307
for package in workspace. packages ( ) {
273
308
let package_build_data = & outputs[ package] ;
274
309
if !package_build_data. is_empty ( ) {
275
- tracing:: info!(
276
- "{}: {package_build_data:?}" ,
277
- workspace[ package] . manifest. parent( ) ,
278
- ) ;
310
+ tracing:: info!( "{}: {package_build_data:?}" , workspace[ package] . manifest, ) ;
279
311
}
280
312
}
281
313
}
@@ -348,15 +380,21 @@ impl WorkspaceBuildScripts {
348
380
progress ( format ! (
349
381
"building compile-time-deps: proc-macro {name} built"
350
382
) ) ;
383
+ if data. proc_macro_dylib_path == ProcMacroDylibPath :: NotBuilt {
384
+ data. proc_macro_dylib_path = ProcMacroDylibPath :: NotProcMacro ;
385
+ }
351
386
if message. target . kind . contains ( & cargo_metadata:: TargetKind :: ProcMacro )
352
387
{
353
- // Skip rmeta file
354
- if let Some ( filename) =
355
- message. filenames . iter ( ) . find ( |file| is_dylib ( file) )
356
- {
357
- let filename = AbsPath :: assert ( filename) ;
358
- data. proc_macro_dylib_path = Some ( filename. to_owned ( ) ) ;
359
- }
388
+ data. proc_macro_dylib_path =
389
+ match message. filenames . iter ( ) . find ( |file| is_dylib ( file) ) {
390
+ Some ( filename) => {
391
+ let filename = AbsPath :: assert ( filename) ;
392
+ ProcMacroDylibPath :: Path ( filename. to_owned ( ) )
393
+ }
394
+ None => ProcMacroDylibPath :: DylibNotFound (
395
+ message. filenames . clone ( ) . into_boxed_slice ( ) ,
396
+ ) ,
397
+ } ;
360
398
}
361
399
} ) ;
362
400
}
@@ -393,14 +431,15 @@ impl WorkspaceBuildScripts {
393
431
current_dir : & AbsPath ,
394
432
sysroot : & Sysroot ,
395
433
toolchain : Option < & semver:: Version > ,
396
- ) -> io:: Result < Command > {
434
+ ) -> io:: Result < ( Option < temp_dir :: TempDir > , Command ) > {
397
435
match config. run_build_script_command . as_deref ( ) {
398
436
Some ( [ program, args @ ..] ) => {
399
437
let mut cmd = toolchain:: command ( program, current_dir, & config. extra_env ) ;
400
438
cmd. args ( args) ;
401
- Ok ( cmd)
439
+ Ok ( ( None , cmd) )
402
440
}
403
441
_ => {
442
+ let mut requires_unstable_options = false ;
404
443
let mut cmd = sysroot. tool ( Tool :: Cargo , current_dir, & config. extra_env ) ;
405
444
406
445
cmd. args ( [ "check" , "--quiet" , "--workspace" , "--message-format=json" ] ) ;
@@ -416,7 +455,19 @@ impl WorkspaceBuildScripts {
416
455
if let Some ( target) = & config. target {
417
456
cmd. args ( [ "--target" , target] ) ;
418
457
}
419
-
458
+ let mut temp_dir_guard = None ;
459
+ if toolchain
460
+ . is_some_and ( |v| * v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH )
461
+ {
462
+ let lockfile_path =
463
+ <_ as AsRef < Utf8Path > >:: as_ref ( manifest_path) . with_extension ( "lock" ) ;
464
+ if let Some ( ( temp_dir, target_lockfile) ) = make_lockfile_copy ( & lockfile_path) {
465
+ temp_dir_guard = Some ( temp_dir) ;
466
+ cmd. arg ( "--lockfile-path" ) ;
467
+ cmd. arg ( target_lockfile. as_str ( ) ) ;
468
+ requires_unstable_options = true ;
469
+ }
470
+ }
420
471
match & config. features {
421
472
CargoFeatures :: All => {
422
473
cmd. arg ( "--all-features" ) ;
@@ -438,6 +489,7 @@ impl WorkspaceBuildScripts {
438
489
}
439
490
440
491
if manifest_path. is_rust_manifest ( ) {
492
+ requires_unstable_options = true ;
441
493
cmd. arg ( "-Zscript" ) ;
442
494
}
443
495
@@ -447,7 +499,7 @@ impl WorkspaceBuildScripts {
447
499
// available in current toolchain's cargo, use it to build compile time deps only.
448
500
const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION : semver:: Version = semver:: Version {
449
501
major : 1 ,
450
- minor : 89 ,
502
+ minor : 189 ,
451
503
patch : 0 ,
452
504
pre : semver:: Prerelease :: EMPTY ,
453
505
build : semver:: BuildMetadata :: EMPTY ,
@@ -457,8 +509,7 @@ impl WorkspaceBuildScripts {
457
509
toolchain. is_some_and ( |v| * v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION ) ;
458
510
459
511
if cargo_comp_time_deps_available {
460
- cmd. env ( "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS" , "nightly" ) ;
461
- cmd. arg ( "-Zunstable-options" ) ;
512
+ requires_unstable_options = true ;
462
513
cmd. arg ( "--compile-time-deps" ) ;
463
514
// we can pass this unconditionally, because we won't actually build the
464
515
// binaries, and as such, this will succeed even on targets without libtest
@@ -481,7 +532,11 @@ impl WorkspaceBuildScripts {
481
532
cmd. env ( "RA_RUSTC_WRAPPER" , "1" ) ;
482
533
}
483
534
}
484
- Ok ( cmd)
535
+ if requires_unstable_options {
536
+ cmd. env ( "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS" , "nightly" ) ;
537
+ cmd. arg ( "-Zunstable-options" ) ;
538
+ }
539
+ Ok ( ( temp_dir_guard, cmd) )
485
540
}
486
541
}
487
542
}
0 commit comments