1515//! (for example, with and without tests), so we actually build a dependency
1616//! graph of `Unit`s, which capture these properties.
1717
18- use std:: collections:: HashMap ;
18+ use std:: collections:: { HashMap , HashSet } ;
1919
2020use CargoResult ;
2121use core:: dependency:: Kind as DepKind ;
@@ -26,8 +26,10 @@ use super::{BuildContext, CompileMode, Kind, Unit};
2626pub fn build_unit_dependencies < ' a , ' cfg > (
2727 roots : & [ Unit < ' a > ] ,
2828 bcx : & BuildContext < ' a , ' cfg > ,
29- mut deps : & mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
29+ deps : & mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
3030) -> CargoResult < ( ) > {
31+ assert ! ( deps. len( ) == 0 , "can only build unit deps once" ) ;
32+
3133 for unit in roots. iter ( ) {
3234 // Dependencies of tests/benches should not have `panic` set.
3335 // We check the global test mode to see if we are running in `cargo
@@ -40,34 +42,36 @@ pub fn build_unit_dependencies<'a, 'cfg>(
4042 } else {
4143 ProfileFor :: Any
4244 } ;
43- deps_of ( unit, bcx, & mut deps, profile_for) ?;
45+ deps_of ( unit, bcx, deps, profile_for) ?;
4446 }
4547 trace ! ( "ALL UNIT DEPENDENCIES {:#?}" , deps) ;
4648
49+ connect_run_custom_build_deps ( bcx, deps) ;
50+
4751 Ok ( ( ) )
4852}
4953
50- fn deps_of < ' a , ' b , ' cfg > (
54+ fn deps_of < ' a , ' cfg > (
5155 unit : & Unit < ' a > ,
5256 bcx : & BuildContext < ' a , ' cfg > ,
53- deps : & ' b mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
57+ deps : & mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
5458 profile_for : ProfileFor ,
55- ) -> CargoResult < & ' b [ Unit < ' a > ] > {
59+ ) -> CargoResult < ( ) > {
5660 // Currently the `deps` map does not include `profile_for`. This should
5761 // be safe for now. `TestDependency` only exists to clear the `panic`
5862 // flag, and you'll never ask for a `unit` with `panic` set as a
5963 // `TestDependency`. `CustomBuild` should also be fine since if the
6064 // requested unit's settings are the same as `Any`, `CustomBuild` can't
6165 // affect anything else in the hierarchy.
6266 if !deps. contains_key ( unit) {
63- let unit_deps = compute_deps ( unit, bcx, deps , profile_for) ?;
67+ let unit_deps = compute_deps ( unit, bcx, profile_for) ?;
6468 let to_insert: Vec < _ > = unit_deps. iter ( ) . map ( |& ( unit, _) | unit) . collect ( ) ;
6569 deps. insert ( * unit, to_insert) ;
6670 for ( unit, profile_for) in unit_deps {
6771 deps_of ( & unit, bcx, deps, profile_for) ?;
6872 }
6973 }
70- Ok ( deps [ unit ] . as_ref ( ) )
74+ Ok ( ( ) )
7175}
7276
7377/// For a package, return all targets which are registered as dependencies
@@ -77,11 +81,10 @@ fn deps_of<'a, 'b, 'cfg>(
7781fn compute_deps < ' a , ' b , ' cfg > (
7882 unit : & Unit < ' a > ,
7983 bcx : & BuildContext < ' a , ' cfg > ,
80- deps : & ' b mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
8184 profile_for : ProfileFor ,
8285) -> CargoResult < Vec < ( Unit < ' a > , ProfileFor ) > > {
8386 if unit. mode . is_run_custom_build ( ) {
84- return compute_deps_custom_build ( unit, bcx, deps ) ;
87+ return compute_deps_custom_build ( unit, bcx) ;
8588 } else if unit. mode . is_doc ( ) && !unit. mode . is_any_test ( ) {
8689 // Note: This does not include Doctest.
8790 return compute_deps_doc ( unit, bcx) ;
@@ -194,39 +197,27 @@ fn compute_deps<'a, 'b, 'cfg>(
194197fn compute_deps_custom_build < ' a , ' cfg > (
195198 unit : & Unit < ' a > ,
196199 bcx : & BuildContext < ' a , ' cfg > ,
197- deps : & mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
198200) -> CargoResult < Vec < ( Unit < ' a > , ProfileFor ) > > {
199201 // When not overridden, then the dependencies to run a build script are:
200202 //
201203 // 1. Compiling the build script itself
202204 // 2. For each immediate dependency of our package which has a `links`
203205 // key, the execution of that build script.
204- let deps = deps
205- . iter ( )
206- . find ( |( key, _deps) | key. pkg == unit. pkg && !key. target . is_custom_build ( ) )
207- . expect ( "can't find package deps" )
208- . 1 ;
209- Ok ( deps. iter ( )
210- . filter_map ( |unit| {
211- if !unit. target . linkable ( ) || unit. pkg . manifest ( ) . links ( ) . is_none ( ) {
212- return None ;
213- }
214- dep_build_script ( unit, bcx)
215- } )
216- . chain ( Some ( (
217- new_unit (
218- bcx,
219- unit. pkg ,
220- unit. target ,
221- ProfileFor :: CustomBuild ,
222- Kind :: Host , // build scripts always compiled for the host
223- CompileMode :: Build ,
224- ) ,
225- // All dependencies of this unit should use profiles for custom
226- // builds.
227- ProfileFor :: CustomBuild ,
228- ) ) )
229- . collect ( ) )
206+ //
207+ // We don't have a great way of handling (2) here right now so this is
208+ // deferred until after the graph of all unit dependencies has been
209+ // constructed.
210+ let unit = new_unit (
211+ bcx,
212+ unit. pkg ,
213+ unit. target ,
214+ ProfileFor :: CustomBuild ,
215+ Kind :: Host , // build scripts always compiled for the host
216+ CompileMode :: Build ,
217+ ) ;
218+ // All dependencies of this unit should use profiles for custom
219+ // builds.
220+ Ok ( vec ! [ ( unit, ProfileFor :: CustomBuild ) ] )
230221}
231222
232223/// Returns the dependencies necessary to document a package
@@ -371,3 +362,74 @@ fn new_unit<'a>(
371362 mode,
372363 }
373364}
365+
366+ /// Fill in missing dependencies for units of the `RunCustomBuild`
367+ ///
368+ /// As mentioned above in `compute_deps_custom_build` each build script
369+ /// execution has two dependencies. The first is compiling the build script
370+ /// itself (already added) and the second is that all crates the package of the
371+ /// build script depends on with `links` keys, their build script execution. (a
372+ /// bit confusing eh?)
373+ ///
374+ /// Here we take the entire `deps` map and add more dependencies from execution
375+ /// of one build script to execution of another build script.
376+ fn connect_run_custom_build_deps < ' a > (
377+ bcx : & BuildContext ,
378+ deps : & mut HashMap < Unit < ' a > , Vec < Unit < ' a > > > ,
379+ ) {
380+ let mut new_deps = Vec :: new ( ) ;
381+
382+ {
383+ // First up build a reverse dependency map. This is a mapping of all
384+ // `RunCustomBuild` known steps to the unit which depends on them. For
385+ // example a library might depend on a build script, so this map will
386+ // have the build script as the key and the library would be in the
387+ // value's set.
388+ let mut reverse_deps = HashMap :: new ( ) ;
389+ for ( unit, deps) in deps. iter ( ) {
390+ for dep in deps {
391+ if dep. mode == CompileMode :: RunCustomBuild {
392+ reverse_deps. entry ( dep)
393+ . or_insert ( HashSet :: new ( ) )
394+ . insert ( unit) ;
395+ }
396+ }
397+ }
398+
399+ // And next we take a look at all build scripts executions listed in the
400+ // dependency map. Our job here is to take everything that depends on
401+ // this build script (from our reverse map above) and look at the other
402+ // package dependencies of these parents.
403+ //
404+ // If we depend on a linkable target and the build script mentions
405+ // `links`, then we depend on that package's build script! Here we use
406+ // `dep_build_script` to manufacture an appropriate build script unit to
407+ // depend on.
408+ for unit in deps. keys ( ) . filter ( |k| k. mode == CompileMode :: RunCustomBuild ) {
409+ let reverse_deps = match reverse_deps. get ( unit) {
410+ Some ( set) => set,
411+ None => continue ,
412+ } ;
413+
414+ let to_add = reverse_deps
415+ . iter ( )
416+ . flat_map ( |reverse_dep| deps[ reverse_dep] . iter ( ) )
417+ . filter ( |other| {
418+ other. pkg != unit. pkg &&
419+ other. target . linkable ( ) &&
420+ other. pkg . manifest ( ) . links ( ) . is_some ( )
421+ } )
422+ . filter_map ( |other| dep_build_script ( other, bcx) . map ( |p| p. 0 ) )
423+ . collect :: < HashSet < _ > > ( ) ;
424+
425+ if !to_add. is_empty ( ) {
426+ new_deps. push ( ( * unit, to_add) ) ;
427+ }
428+ }
429+ }
430+
431+ // And finally, add in all the missing dependencies!
432+ for ( unit, new_deps) in new_deps {
433+ deps. get_mut ( & unit) . unwrap ( ) . extend ( new_deps) ;
434+ }
435+ }
0 commit comments