@@ -905,6 +905,66 @@ extension BuildPlan {
905
905
extension BuildPlan {
906
906
fileprivate typealias Destination = BuildParameters . Destination
907
907
908
+ fileprivate enum TraversalNode : Hashable {
909
+ case package ( ResolvedPackage )
910
+ case product( ResolvedProduct , BuildParameters . Destination )
911
+ case module( ResolvedModule , BuildParameters . Destination )
912
+
913
+ var destination : BuildParameters . Destination {
914
+ switch self {
915
+ case . package :
916
+ . target
917
+ case . product( _, let destination) :
918
+ destination
919
+ case . module( _, let destination) :
920
+ destination
921
+ }
922
+ }
923
+
924
+ init (
925
+ product: ResolvedProduct ,
926
+ context destination: BuildParameters . Destination
927
+ ) {
928
+ switch product. type {
929
+ case . macro, . plugin:
930
+ self = . product( product, . host)
931
+ case . test:
932
+ self = . product( product, product. modules. contains ( where: Self . hasMacroDependency) ? . host : destination)
933
+ default :
934
+ self = . product( product, destination)
935
+ }
936
+ }
937
+
938
+ init (
939
+ module: ResolvedModule ,
940
+ context destination: BuildParameters . Destination
941
+ ) {
942
+ switch module. type {
943
+ case . macro, . plugin:
944
+ // Macros and plugins are ways built for host
945
+ self = . module( module, . host)
946
+ case . test:
947
+ self = . module( module, Self . hasMacroDependency ( module: module) ? . host : destination)
948
+ default :
949
+ // By default assume the destination of the context.
950
+ // This means that i.e. test products that reference macros
951
+ // would force all of their successors to be `host`
952
+ self = . module( module, destination)
953
+ }
954
+ }
955
+
956
+ static func hasMacroDependency( module: ResolvedModule ) -> Bool {
957
+ module. dependencies. contains ( where: {
958
+ switch $0 {
959
+ case . product( let productDependency, _) :
960
+ productDependency. type == . macro
961
+ case . module( let moduleDependency, _) :
962
+ moduleDependency. type == . macro
963
+ }
964
+ } )
965
+ }
966
+ }
967
+
908
968
/// Traverse the modules graph and find a destination for every product and module.
909
969
/// All non-macro/plugin products and modules have `target` destination with one
910
970
/// notable exception - test products/modules with direct macro dependency.
@@ -913,65 +973,16 @@ extension BuildPlan {
913
973
onProduct: ( ResolvedProduct , Destination ) throws -> Void ,
914
974
onModule: ( ResolvedModule , Destination ) async throws -> Void
915
975
) async rethrows {
916
- enum Node : Hashable {
917
- case package ( ResolvedPackage )
918
- case product( ResolvedProduct , Destination )
919
- case module( ResolvedModule , Destination )
920
-
921
- static func `for`(
922
- product: ResolvedProduct ,
923
- context destination: Destination
924
- ) -> Node {
925
- switch product. type {
926
- case . macro, . plugin:
927
- . product( product, . host)
928
- case . test:
929
- . product( product, product. modules. contains ( where: self . hasMacroDependency) ? . host : destination)
930
- default :
931
- . product( product, destination)
932
- }
933
- }
934
-
935
- static func `for`(
936
- module: ResolvedModule ,
937
- context destination: Destination
938
- ) -> Node {
939
- switch module. type {
940
- case . macro, . plugin:
941
- // Macros and plugins are ways built for host
942
- . module( module, . host)
943
- case . test:
944
- . module( module, self . hasMacroDependency ( module: module) ? . host : destination)
945
- default :
946
- // By default assume the destination of the context.
947
- // This means that i.e. test products that reference macros
948
- // would force all of their successors to be `host`
949
- . module( module, destination)
950
- }
951
- }
952
-
953
- static func hasMacroDependency( module: ResolvedModule ) -> Bool {
954
- module. dependencies. contains ( where: {
955
- switch $0 {
956
- case . product( let productDependency, _) :
957
- productDependency. type == . macro
958
- case . module( let moduleDependency, _) :
959
- moduleDependency. type == . macro
960
- }
961
- } )
962
- }
963
- }
964
-
965
- func successors( for package : ResolvedPackage ) -> [ Node ] {
966
- var successors : [ Node ] = [ ]
976
+ func successors( for package : ResolvedPackage ) -> [ TraversalNode ] {
977
+ var successors : [ TraversalNode ] = [ ]
967
978
for product in package . products {
968
979
if case . test = product. underlying. type,
969
980
!graph. rootPackages. contains ( id: package . id)
970
981
{
971
982
continue
972
983
}
973
984
974
- successors. append ( . for ( product: product, context: . target) )
985
+ successors. append ( . init ( product: product, context: . target) )
975
986
}
976
987
977
988
for module in package . modules {
@@ -981,7 +992,7 @@ extension BuildPlan {
981
992
continue
982
993
}
983
994
984
- successors. append ( . for ( module: module, context: . target) )
995
+ successors. append ( . init ( module: module, context: . target) )
985
996
}
986
997
987
998
return successors
@@ -990,35 +1001,35 @@ extension BuildPlan {
990
1001
func successors(
991
1002
for product: ResolvedProduct ,
992
1003
destination: Destination
993
- ) -> [ Node ] {
1004
+ ) -> [ TraversalNode ] {
994
1005
guard destination == . host else {
995
1006
return [ ]
996
1007
}
997
1008
998
1009
return product. modules. map { module in
999
- . for ( module: module, context: destination)
1010
+ TraversalNode ( module: module, context: destination)
1000
1011
}
1001
1012
}
1002
1013
1003
1014
func successors(
1004
1015
for module: ResolvedModule ,
1005
1016
destination: Destination
1006
- ) -> [ Node ] {
1017
+ ) -> [ TraversalNode ] {
1007
1018
guard destination == . host else {
1008
1019
return [ ]
1009
1020
}
1010
1021
1011
- return module. dependencies. reduce ( into: [ Node ] ( ) ) { partial, dependency in
1022
+ return module. dependencies. reduce ( into: [ TraversalNode ] ( ) ) { partial, dependency in
1012
1023
switch dependency {
1013
1024
case . product( let product, conditions: _) :
1014
- partial. append ( . for ( product: product, context: destination) )
1025
+ partial. append ( . init ( product: product, context: destination) )
1015
1026
case . module( let module, _) :
1016
- partial. append ( . for ( module: module, context: destination) )
1027
+ partial. append ( . init ( module: module, context: destination) )
1017
1028
}
1018
1029
}
1019
1030
}
1020
1031
1021
- try await depthFirstSearch ( graph. packages. map { Node . package ( $0) } ) { node in
1032
+ try await depthFirstSearch ( graph. packages. map { TraversalNode . package ( $0) } ) { node in
1022
1033
switch node {
1023
1034
case . package ( let package ) :
1024
1035
successors ( for: package )
@@ -1041,6 +1052,71 @@ extension BuildPlan {
1041
1052
// No de-duplication is necessary we only want unique nodes.
1042
1053
}
1043
1054
}
1055
+
1056
+ /// Traverses the modules graph, computes destination of every module reference and
1057
+ /// provides the data to the caller by means of `onModule` callback. The products
1058
+ /// are completely transparent to this method and are represented by their module dependencies.
1059
+ package func traverseModules(
1060
+ _ onModule: (
1061
+ ( ResolvedModule , BuildParameters . Destination ) ,
1062
+ _ parent: ( ResolvedModule , BuildParameters . Destination ) ? ,
1063
+ _ depth: Int
1064
+ ) -> Void
1065
+ ) {
1066
+ func successors( for package : ResolvedPackage ) -> [ TraversalNode ] {
1067
+ package . modules. compactMap {
1068
+ if case . test = $0. underlying. type,
1069
+ !self . graph. rootPackages. contains ( id: package . id)
1070
+ {
1071
+ return nil
1072
+ }
1073
+ return . init( module: $0, context: . target)
1074
+ }
1075
+ }
1076
+
1077
+ func successors(
1078
+ for module: ResolvedModule ,
1079
+ destination: Destination
1080
+ ) -> [ TraversalNode ] {
1081
+ module. dependencies. reduce ( into: [ TraversalNode] ( ) ) { partial, dependency in
1082
+ switch dependency {
1083
+ case . product( let product, conditions: _) :
1084
+ let parent = TraversalNode ( product: product, context: destination)
1085
+ for module in product. modules {
1086
+ partial. append ( . init( module: module, context: parent. destination) )
1087
+ }
1088
+ case . module( let module, _) :
1089
+ partial. append ( . init( module: module, context: destination) )
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ depthFirstSearch ( self . graph. packages. map { TraversalNode . package ( $0) } ) {
1095
+ switch $0 {
1096
+ case . package ( let package ) :
1097
+ successors ( for: package )
1098
+ case . module( let module, let destination) :
1099
+ successors ( for: module, destination: destination)
1100
+ case . product:
1101
+ [ ]
1102
+ }
1103
+ } onNext: { current, parent, depth in
1104
+ let parentModule : ( ResolvedModule , BuildParameters . Destination ) ? = switch parent {
1105
+ case . package , . product, nil :
1106
+ nil
1107
+ case . module( let module, let destination) :
1108
+ ( module, destination)
1109
+ }
1110
+
1111
+ switch current {
1112
+ case . package , . product:
1113
+ break
1114
+
1115
+ case . module( let module, let destination) :
1116
+ onModule ( ( module, destination) , parentModule, depth)
1117
+ }
1118
+ }
1119
+ }
1044
1120
}
1045
1121
1046
1122
extension Basics . Diagnostic {
0 commit comments