@@ -16,11 +16,13 @@ use la_arena::ArenaMap;
1616use paths:: { AbsPath , AbsPathBuf , Utf8PathBuf } ;
1717use rustc_hash:: { FxHashMap , FxHashSet } ;
1818use serde:: Deserialize as _;
19+ use stdx:: never;
1920use toolchain:: Tool ;
2021
2122use crate :: {
2223 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,
2426} ;
2527
2628/// Output of the build script and proc-macro building steps for a workspace.
@@ -30,6 +32,15 @@ pub struct WorkspaceBuildScripts {
3032 error : Option < String > ,
3133}
3234
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+
3344/// Output of the build script and proc-macro building step for a concrete package.
3445#[ derive( Debug , Clone , Default , PartialEq , Eq ) ]
3546pub ( crate ) struct BuildScriptOutput {
@@ -43,15 +54,15 @@ pub(crate) struct BuildScriptOutput {
4354 /// Directory where a build script might place its output.
4455 pub ( crate ) out_dir : Option < AbsPathBuf > ,
4556 /// 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 ,
4758}
4859
4960impl BuildScriptOutput {
5061 fn is_empty ( & self ) -> bool {
5162 self . cfgs . is_empty ( )
5263 && self . envs . is_empty ( )
5364 && self . out_dir . is_none ( )
54- && self . proc_macro_dylib_path . is_none ( )
65+ && self . proc_macro_dylib_path == ProcMacroDylibPath :: NotBuilt
5566 }
5667}
5768
@@ -67,7 +78,7 @@ impl WorkspaceBuildScripts {
6778 let current_dir = workspace. workspace_root ( ) ;
6879
6980 let allowed_features = workspace. workspace_features ( ) ;
70- let cmd = Self :: build_command (
81+ let ( _guard , cmd) = Self :: build_command (
7182 config,
7283 & allowed_features,
7384 workspace. manifest_path ( ) ,
@@ -88,7 +99,7 @@ impl WorkspaceBuildScripts {
8899 ) -> io:: Result < Vec < WorkspaceBuildScripts > > {
89100 assert_eq ! ( config. invocation_strategy, InvocationStrategy :: Once ) ;
90101
91- let cmd = Self :: build_command (
102+ let ( _guard , cmd) = Self :: build_command (
92103 config,
93104 & Default :: default ( ) ,
94105 // This is not gonna be used anyways, so just construct a dummy here
@@ -126,6 +137,8 @@ impl WorkspaceBuildScripts {
126137 |package, cb| {
127138 if let Some ( & ( package, workspace) ) = by_id. get ( package) {
128139 cb ( & workspaces[ workspace] [ package] . name , & mut res[ workspace] . outputs [ package] ) ;
140+ } else {
141+ never ! ( "Received compiler message for unknown package: {}" , package) ;
129142 }
130143 } ,
131144 progress,
@@ -140,12 +153,9 @@ impl WorkspaceBuildScripts {
140153 if tracing:: enabled!( tracing:: Level :: INFO ) {
141154 for ( idx, workspace) in workspaces. iter ( ) . enumerate ( ) {
142155 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] ;
144157 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, ) ;
149159 }
150160 }
151161 }
@@ -198,39 +208,58 @@ impl WorkspaceBuildScripts {
198208 let path = dir_entry. path ( ) ;
199209 let extension = path. extension ( ) ?;
200210 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+ } ;
205238 }
206239 }
207240 None
208241 } )
209242 . collect ( ) ;
210243 for p in rustc. packages ( ) {
211244 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
222255 }
223- }
224256 }
225257
226258 if tracing:: enabled!( tracing:: Level :: INFO ) {
227259 for package in rustc. packages ( ) {
228260 let package_build_data = & bs. outputs [ package] ;
229261 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, ) ;
234263 }
235264 }
236265 }
@@ -263,6 +292,12 @@ impl WorkspaceBuildScripts {
263292 |package, cb| {
264293 if let Some ( & package) = by_id. get ( package) {
265294 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+ ) ;
266301 }
267302 } ,
268303 progress,
@@ -272,10 +307,7 @@ impl WorkspaceBuildScripts {
272307 for package in workspace. packages ( ) {
273308 let package_build_data = & outputs[ package] ;
274309 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, ) ;
279311 }
280312 }
281313 }
@@ -348,15 +380,21 @@ impl WorkspaceBuildScripts {
348380 progress ( format ! (
349381 "building compile-time-deps: proc-macro {name} built"
350382 ) ) ;
383+ if data. proc_macro_dylib_path == ProcMacroDylibPath :: NotBuilt {
384+ data. proc_macro_dylib_path = ProcMacroDylibPath :: NotProcMacro ;
385+ }
351386 if message. target . kind . contains ( & cargo_metadata:: TargetKind :: ProcMacro )
352387 {
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+ } ;
360398 }
361399 } ) ;
362400 }
@@ -393,14 +431,15 @@ impl WorkspaceBuildScripts {
393431 current_dir : & AbsPath ,
394432 sysroot : & Sysroot ,
395433 toolchain : Option < & semver:: Version > ,
396- ) -> io:: Result < Command > {
434+ ) -> io:: Result < ( Option < temp_dir :: TempDir > , Command ) > {
397435 match config. run_build_script_command . as_deref ( ) {
398436 Some ( [ program, args @ ..] ) => {
399437 let mut cmd = toolchain:: command ( program, current_dir, & config. extra_env ) ;
400438 cmd. args ( args) ;
401- Ok ( cmd)
439+ Ok ( ( None , cmd) )
402440 }
403441 _ => {
442+ let mut requires_unstable_options = false ;
404443 let mut cmd = sysroot. tool ( Tool :: Cargo , current_dir, & config. extra_env ) ;
405444
406445 cmd. args ( [ "check" , "--quiet" , "--workspace" , "--message-format=json" ] ) ;
@@ -416,7 +455,19 @@ impl WorkspaceBuildScripts {
416455 if let Some ( target) = & config. target {
417456 cmd. args ( [ "--target" , target] ) ;
418457 }
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+ }
420471 match & config. features {
421472 CargoFeatures :: All => {
422473 cmd. arg ( "--all-features" ) ;
@@ -438,6 +489,7 @@ impl WorkspaceBuildScripts {
438489 }
439490
440491 if manifest_path. is_rust_manifest ( ) {
492+ requires_unstable_options = true ;
441493 cmd. arg ( "-Zscript" ) ;
442494 }
443495
@@ -447,7 +499,7 @@ impl WorkspaceBuildScripts {
447499 // available in current toolchain's cargo, use it to build compile time deps only.
448500 const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION : semver:: Version = semver:: Version {
449501 major : 1 ,
450- minor : 89 ,
502+ minor : 189 ,
451503 patch : 0 ,
452504 pre : semver:: Prerelease :: EMPTY ,
453505 build : semver:: BuildMetadata :: EMPTY ,
@@ -457,8 +509,7 @@ impl WorkspaceBuildScripts {
457509 toolchain. is_some_and ( |v| * v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION ) ;
458510
459511 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 ;
462513 cmd. arg ( "--compile-time-deps" ) ;
463514 // we can pass this unconditionally, because we won't actually build the
464515 // binaries, and as such, this will succeed even on targets without libtest
@@ -481,7 +532,11 @@ impl WorkspaceBuildScripts {
481532 cmd. env ( "RA_RUSTC_WRAPPER" , "1" ) ;
482533 }
483534 }
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) )
485540 }
486541 }
487542 }
0 commit comments