@@ -14,7 +14,6 @@ use jobserver::Client;
1414
1515use core:: { Package , PackageId , PackageSet , Profile , Resolve , Target } ;
1616use core:: { Dependency , Profiles , TargetKind , Workspace } ;
17- use core:: dependency:: Kind as DepKind ;
1817use util:: { self , internal, profile, Cfg , CfgExpr , Config , ProcessBuilder } ;
1918use util:: errors:: { CargoResult , CargoResultExt } ;
2019
@@ -25,6 +24,9 @@ use super::layout::Layout;
2524use super :: links:: Links ;
2625use super :: { BuildConfig , Compilation , Kind } ;
2726
27+ mod unit_dependencies;
28+ use self :: unit_dependencies:: build_unit_dependencies;
29+
2830/// All information needed to define a Unit.
2931///
3032/// A unit is an object that has enough information so that cargo knows how to build it.
@@ -102,6 +104,7 @@ pub struct Context<'a, 'cfg: 'a> {
102104 profiles : & ' a Profiles ,
103105 incremental_env : Option < bool > ,
104106
107+ unit_dependencies : Option < HashMap < Unit < ' a > , Vec < Unit < ' a > > > > ,
105108 /// For each Unit, a list all files produced as a triple of
106109 ///
107110 /// - File name that will be produced by the build process (in `deps`)
@@ -204,6 +207,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
204207 jobserver,
205208 build_script_overridden : HashSet :: new ( ) ,
206209
210+ unit_dependencies : None ,
207211 // TODO: Pre-Calculate these with a topo-sort, rather than lazy-calculating
208212 target_filenames : HashMap :: new ( ) ,
209213 target_metadatas : HashMap :: new ( ) ,
@@ -232,6 +236,12 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
232236 Ok ( ( ) )
233237 }
234238
239+ pub fn build_unit_dependencies ( & mut self , units : & [ Unit < ' a > ] ) -> CargoResult < ( ) > {
240+ assert ! ( self . unit_dependencies. is_none( ) ) ;
241+ self . unit_dependencies = Some ( build_unit_dependencies ( units, self ) ?) ;
242+ Ok ( ( ) )
243+ }
244+
235245 /// Ensure that we've collected all target-specific information to compile
236246 /// all the units mentioned in `units`.
237247 pub fn probe_target_info ( & mut self ) -> CargoResult < ( ) > {
@@ -361,7 +371,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
361371 if is_plugin {
362372 self . used_in_plugin . insert ( * unit) ;
363373 }
364- for unit in self . dep_targets ( unit) ? {
374+ for unit in self . dep_targets ( unit) {
365375 self . walk_used_in_plugin_map ( & unit, is_plugin || unit. target . for_host ( ) , visited) ?;
366376 }
367377 Ok ( ( ) )
@@ -526,9 +536,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
526536 . hash ( & mut hasher) ;
527537
528538 // Mix in the target-metadata of all the dependencies of this target
529- if let Ok ( deps) = self . dep_targets ( unit) {
530- let mut deps_metadata = deps. into_iter ( )
531- . map ( |dep_unit| self . target_metadata ( & dep_unit) )
539+ {
540+ let mut deps_metadata = self . dep_targets ( unit)
541+ . iter ( )
542+ . map ( |dep_unit| self . target_metadata ( dep_unit) )
532543 . collect :: < Vec < _ > > ( ) ;
533544 deps_metadata. sort ( ) ;
534545 deps_metadata. hash ( & mut hasher) ;
@@ -770,236 +781,25 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
770781
771782 /// For a package, return all targets which are registered as dependencies
772783 /// for that package.
773- pub fn dep_targets ( & self , unit : & Unit < ' a > ) -> CargoResult < Vec < Unit < ' a > > > {
774- if unit. profile . run_custom_build {
775- return self . dep_run_custom_build ( unit) ;
776- } else if unit. profile . doc && !unit. profile . test {
777- return self . doc_deps ( unit) ;
778- }
779-
780- let id = unit. pkg . package_id ( ) ;
781- let deps = self . resolve . deps ( id) ;
782- let mut ret = deps. filter ( |dep| {
783- unit. pkg
784- . dependencies ( )
785- . iter ( )
786- . filter ( |d| d. name ( ) == dep. name ( ) && d. version_req ( ) . matches ( dep. version ( ) ) )
787- . any ( |d| {
788- // If this target is a build command, then we only want build
789- // dependencies, otherwise we want everything *other than* build
790- // dependencies.
791- if unit. target . is_custom_build ( ) != d. is_build ( ) {
792- return false ;
793- }
794-
795- // If this dependency is *not* a transitive dependency, then it
796- // only applies to test/example targets
797- if !d. is_transitive ( ) && !unit. target . is_test ( ) && !unit. target . is_example ( )
798- && !unit. profile . test
799- {
800- return false ;
801- }
802-
803- // If this dependency is only available for certain platforms,
804- // make sure we're only enabling it for that platform.
805- if !self . dep_platform_activated ( d, unit. kind ) {
806- return false ;
807- }
808-
809- // If the dependency is optional, then we're only activating it
810- // if the corresponding feature was activated
811- if d. is_optional ( ) && !self . resolve . features ( id) . contains ( & * d. name ( ) ) {
812- return false ;
813- }
814-
815- // If we've gotten past all that, then this dependency is
816- // actually used!
817- true
818- } )
819- } ) . filter_map ( |id| match self . get_package ( id) {
820- Ok ( pkg) => pkg. targets ( ) . iter ( ) . find ( |t| t. is_lib ( ) ) . map ( |t| {
821- let unit = Unit {
822- pkg,
823- target : t,
824- profile : self . lib_or_check_profile ( unit, t) ,
825- kind : unit. kind . for_target ( t) ,
826- } ;
827- Ok ( unit)
828- } ) ,
829- Err ( e) => Some ( Err ( e) ) ,
830- } )
831- . collect :: < CargoResult < Vec < _ > > > ( ) ?;
832-
833- // If this target is a build script, then what we've collected so far is
834- // all we need. If this isn't a build script, then it depends on the
835- // build script if there is one.
836- if unit. target . is_custom_build ( ) {
837- return Ok ( ret) ;
838- }
839- ret. extend ( self . dep_build_script ( unit) ) ;
840-
841- // If this target is a binary, test, example, etc, then it depends on
842- // the library of the same package. The call to `resolve.deps` above
843- // didn't include `pkg` in the return values, so we need to special case
844- // it here and see if we need to push `(pkg, pkg_lib_target)`.
845- if unit. target . is_lib ( ) && !unit. profile . doc {
846- return Ok ( ret) ;
847- }
848- ret. extend ( self . maybe_lib ( unit) ) ;
849-
850- // Integration tests/benchmarks require binaries to be built
851- if unit. profile . test && ( unit. target . is_test ( ) || unit. target . is_bench ( ) ) {
852- ret. extend (
853- unit. pkg
854- . targets ( )
855- . iter ( )
856- . filter ( |t| {
857- let no_required_features = Vec :: new ( ) ;
858-
859- t. is_bin ( ) &&
860- // Skip binaries with required features that have not been selected.
861- t. required_features ( ) . unwrap_or ( & no_required_features) . iter ( ) . all ( |f| {
862- self . resolve . features ( id) . contains ( f)
863- } )
864- } )
865- . map ( |t| Unit {
866- pkg : unit. pkg ,
867- target : t,
868- profile : self . lib_or_check_profile ( unit, t) ,
869- kind : unit. kind . for_target ( t) ,
870- } ) ,
871- ) ;
872- }
873- Ok ( ret)
874- }
875-
876- /// Returns the dependencies needed to run a build script.
877- ///
878- /// The `unit` provided must represent an execution of a build script, and
879- /// the returned set of units must all be run before `unit` is run.
880- pub fn dep_run_custom_build ( & self , unit : & Unit < ' a > ) -> CargoResult < Vec < Unit < ' a > > > {
784+ // TODO: this ideally should be `-> &[Unit<'a>]`
785+ pub fn dep_targets ( & self , unit : & Unit < ' a > ) -> Vec < Unit < ' a > > {
881786 // If this build script's execution has been overridden then we don't
882787 // actually depend on anything, we've reached the end of the dependency
883788 // chain as we've got all the info we're gonna get.
884- let key = ( unit. pkg . package_id ( ) . clone ( ) , unit. kind ) ;
885- if self . build_script_overridden . contains ( & key) {
886- return Ok ( Vec :: new ( ) ) ;
887- }
888-
889- // When not overridden, then the dependencies to run a build script are:
890789 //
891- // 1. Compiling the build script itself
892- // 2. For each immediate dependency of our package which has a `links`
893- // key, the execution of that build script.
894- let not_custom_build = unit. pkg
895- . targets ( )
896- . iter ( )
897- . find ( |t| !t. is_custom_build ( ) )
898- . unwrap ( ) ;
899- let tmp = Unit {
900- target : not_custom_build,
901- profile : & self . profiles . dev ,
902- ..* unit
903- } ;
904- let deps = self . dep_targets ( & tmp) ?;
905- Ok ( deps. iter ( )
906- . filter_map ( |unit| {
907- if !unit. target . linkable ( ) || unit. pkg . manifest ( ) . links ( ) . is_none ( ) {
908- return None ;
909- }
910- self . dep_build_script ( unit)
911- } )
912- . chain ( Some ( Unit {
913- profile : self . build_script_profile ( unit. pkg . package_id ( ) ) ,
914- kind : Kind :: Host , // build scripts always compiled for the host
915- ..* unit
916- } ) )
917- . collect ( ) )
918- }
919-
920- /// Returns the dependencies necessary to document a package
921- fn doc_deps ( & self , unit : & Unit < ' a > ) -> CargoResult < Vec < Unit < ' a > > > {
922- let deps = self . resolve
923- . deps ( unit. pkg . package_id ( ) )
924- . filter ( |dep| {
925- unit. pkg
926- . dependencies ( )
927- . iter ( )
928- . filter ( |d| d. name ( ) == dep. name ( ) )
929- . any ( |dep| match dep. kind ( ) {
930- DepKind :: Normal => self . dep_platform_activated ( dep, unit. kind ) ,
931- _ => false ,
932- } )
933- } )
934- . map ( |dep| self . get_package ( dep) ) ;
935-
936- // To document a library, we depend on dependencies actually being
937- // built. If we're documenting *all* libraries, then we also depend on
938- // the documentation of the library being built.
939- let mut ret = Vec :: new ( ) ;
940- for dep in deps {
941- let dep = dep?;
942- let lib = match dep. targets ( ) . iter ( ) . find ( |t| t. is_lib ( ) ) {
943- Some ( lib) => lib,
944- None => continue ,
945- } ;
946- ret. push ( Unit {
947- pkg : dep,
948- target : lib,
949- profile : self . lib_or_check_profile ( unit, lib) ,
950- kind : unit. kind . for_target ( lib) ,
951- } ) ;
952- if self . build_config . doc_all {
953- ret. push ( Unit {
954- pkg : dep,
955- target : lib,
956- profile : & self . profiles . doc ,
957- kind : unit. kind . for_target ( lib) ,
958- } ) ;
790+ // Note there's a subtlety about this piece of code! The
791+ // `build_script_overridden` map here is populated in
792+ // `custom_build::build_map` which you need to call before inspecting
793+ // dependencies. However, that code itself calls this method and
794+ // gets a full pre-filtered set of dependencies. This is not super
795+ // obvious, and clear, but it does work at the moment.
796+ if unit. profile . run_custom_build {
797+ let key = ( unit. pkg . package_id ( ) . clone ( ) , unit. kind ) ;
798+ if self . build_script_overridden . contains ( & key) {
799+ return Vec :: new ( ) ;
959800 }
960801 }
961-
962- // Be sure to build/run the build script for documented libraries as
963- ret. extend ( self . dep_build_script ( unit) ) ;
964-
965- // If we document a binary, we need the library available
966- if unit. target . is_bin ( ) {
967- ret. extend ( self . maybe_lib ( unit) ) ;
968- }
969- Ok ( ret)
970- }
971-
972- /// If a build script is scheduled to be run for the package specified by
973- /// `unit`, this function will return the unit to run that build script.
974- ///
975- /// Overriding a build script simply means that the running of the build
976- /// script itself doesn't have any dependencies, so even in that case a unit
977- /// of work is still returned. `None` is only returned if the package has no
978- /// build script.
979- fn dep_build_script ( & self , unit : & Unit < ' a > ) -> Option < Unit < ' a > > {
980- unit. pkg
981- . targets ( )
982- . iter ( )
983- . find ( |t| t. is_custom_build ( ) )
984- . map ( |t| Unit {
985- pkg : unit. pkg ,
986- target : t,
987- profile : & self . profiles . custom_build ,
988- kind : unit. kind ,
989- } )
990- }
991-
992- fn maybe_lib ( & self , unit : & Unit < ' a > ) -> Option < Unit < ' a > > {
993- unit. pkg
994- . targets ( )
995- . iter ( )
996- . find ( |t| t. linkable ( ) )
997- . map ( |t| Unit {
998- pkg : unit. pkg ,
999- target : t,
1000- profile : self . lib_or_check_profile ( unit, t) ,
1001- kind : unit. kind . for_target ( t) ,
1002- } )
802+ self . unit_dependencies . as_ref ( ) . unwrap ( ) [ unit] . clone ( )
1003803 }
1004804
1005805 fn dep_platform_activated ( & self , dep : & Dependency , kind : Kind ) -> bool {
@@ -1066,15 +866,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
1066866 }
1067867 }
1068868
1069- pub fn lib_or_check_profile ( & self , unit : & Unit , target : & Target ) -> & ' a Profile {
1070- if !target. is_custom_build ( ) && !target. for_host ( )
1071- && ( unit. profile . check || ( unit. profile . doc && !unit. profile . test ) )
1072- {
1073- return & self . profiles . check ;
1074- }
1075- self . lib_profile ( )
1076- }
1077-
1078869 pub fn build_script_profile ( & self , _pkg : & PackageId ) -> & ' a Profile {
1079870 // TODO: should build scripts always be built with the same library
1080871 // profile? How is this controlled at the CLI layer?
0 commit comments