9
9
"reflect"
10
10
"strings"
11
11
12
+ "github.com/dominikbraun/graph"
12
13
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
13
14
"github.com/otiai10/copy"
14
15
"kcl-lang.io/kcl-go/pkg/kcl"
@@ -567,7 +568,7 @@ func (c *KpmClient) AddDepToPkg(kclPkg *pkg.KclPkg, d *pkg.Dependency) error {
567
568
}
568
569
569
570
// download all the dependencies.
570
- changedDeps , err := c .downloadDeps (kclPkg .ModFile .Dependencies , kclPkg .Dependencies )
571
+ changedDeps , _ , err := c .downloadDeps (kclPkg .ModFile .Dependencies , kclPkg .Dependencies )
571
572
572
573
if err != nil {
573
574
return err
@@ -1068,6 +1069,59 @@ func (c *KpmClient) ParseOciOptionFromString(oci string, tag string) (*opt.OciOp
1068
1069
return ociOpt , nil
1069
1070
}
1070
1071
1072
+ // PrintDependencyGraph will print the dependency graph of kcl package dependencies
1073
+ func (c * KpmClient ) PrintDependencyGraph (kclPkg * pkg.KclPkg ) error {
1074
+ // create the main graph with a single root vertex.
1075
+ root := fmt .Sprint (kclPkg .GetPkgName ())
1076
+ mainGraph := graph .New (graph .StringHash , graph .Directed ())
1077
+ err := mainGraph .AddVertex (root )
1078
+ if err != nil {
1079
+ return err
1080
+ }
1081
+
1082
+ // get the dependency graphs and merge them into the main graph at root vertex.
1083
+ _ , depGraphs , err := c .downloadDeps (kclPkg .Dependencies , kclPkg .ModFile .Dependencies )
1084
+ if err != nil {
1085
+ return err
1086
+ }
1087
+
1088
+ for _ , g := range depGraphs {
1089
+ mainGraph , err = graph .Union (mainGraph , g )
1090
+ if err != nil {
1091
+ return err
1092
+ }
1093
+ src , err := FindSource (g )
1094
+ if err != nil {
1095
+ return err
1096
+ }
1097
+ err = mainGraph .AddEdge (root , src )
1098
+ if err != nil {
1099
+ return err
1100
+ }
1101
+ }
1102
+
1103
+ adjMap , err := mainGraph .AdjacencyMap ()
1104
+ if err != nil {
1105
+ return err
1106
+ }
1107
+
1108
+ // print the dependency graph to stdout.
1109
+ err = graph .BFS (mainGraph , root , func (source string ) bool {
1110
+ for target := range adjMap [source ] {
1111
+ reporter .ReportMsgTo (
1112
+ fmt .Sprint (source , target ),
1113
+ c .logWriter ,
1114
+ )
1115
+ }
1116
+ return false
1117
+ })
1118
+ if err != nil {
1119
+ return err
1120
+ }
1121
+
1122
+ return nil
1123
+ }
1124
+
1071
1125
// dependencyExists will check whether the dependency exists in the local filesystem.
1072
1126
func (c * KpmClient ) dependencyExists (dep * pkg.Dependency , lockDeps * pkg.Dependencies ) * pkg.Dependency {
1073
1127
@@ -1092,15 +1146,24 @@ func (c *KpmClient) dependencyExists(dep *pkg.Dependency, lockDeps *pkg.Dependen
1092
1146
}
1093
1147
1094
1148
// downloadDeps will download all the dependencies of the current kcl package.
1095
- func (c * KpmClient ) downloadDeps (deps pkg.Dependencies , lockDeps pkg.Dependencies ) (* pkg.Dependencies , error ) {
1149
+ func (c * KpmClient ) downloadDeps (deps pkg.Dependencies , lockDeps pkg.Dependencies ) (* pkg.Dependencies , []graph. Graph [ string , string ], error ) {
1096
1150
newDeps := pkg.Dependencies {
1097
1151
Deps : make (map [string ]pkg.Dependency ),
1098
1152
}
1099
1153
1154
+ depGraphs := make ([]graph.Graph [string , string ], len (deps .Deps ))
1155
+ i := 0
1156
+
1100
1157
// Traverse all dependencies in kcl.mod
1101
1158
for _ , d := range deps .Deps {
1159
+ depGraphs [i ] = graph .New (graph .StringHash , graph .Directed ())
1160
+ err := depGraphs [i ].AddVertex (fmt .Sprintf ("%s@%s" , d .Name , d .Version ))
1161
+ if err != nil {
1162
+ return nil , nil , err
1163
+ }
1164
+ i ++
1102
1165
if len (d .Name ) == 0 {
1103
- return nil , errors .InvalidDependency
1166
+ return nil , nil , errors .InvalidDependency
1104
1167
}
1105
1168
1106
1169
existDep := c .dependencyExists (& d , & lockDeps )
@@ -1112,7 +1175,7 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie
1112
1175
expectedSum := lockDeps .Deps [d .Name ].Sum
1113
1176
// Clean the cache
1114
1177
if len (c .homePath ) == 0 || len (d .FullName ) == 0 {
1115
- return nil , errors .InternalBug
1178
+ return nil , nil , errors .InternalBug
1116
1179
}
1117
1180
dir := filepath .Join (c .homePath , d .FullName )
1118
1181
os .RemoveAll (dir )
@@ -1121,15 +1184,15 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie
1121
1184
1122
1185
lockedDep , err := c .Download (& d , dir )
1123
1186
if err != nil {
1124
- return nil , err
1187
+ return nil , nil , err
1125
1188
}
1126
1189
1127
1190
if ! lockedDep .IsFromLocal () {
1128
1191
if ! c .noSumCheck && expectedSum != "" &&
1129
1192
lockedDep .Sum != expectedSum &&
1130
1193
existDep != nil &&
1131
1194
existDep .FullName == d .FullName {
1132
- return nil , reporter .NewErrorEvent (
1195
+ return nil , nil , reporter .NewErrorEvent (
1133
1196
reporter .CheckSumMismatch ,
1134
1197
errors .CheckSumMismatchError ,
1135
1198
fmt .Sprintf ("checksum for '%s' changed in lock file" , lockedDep .Name ),
@@ -1142,6 +1205,7 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie
1142
1205
lockDeps .Deps [d .Name ] = * lockedDep
1143
1206
}
1144
1207
1208
+ i = 0
1145
1209
// Recursively download the dependencies of the new dependencies.
1146
1210
for _ , d := range newDeps .Deps {
1147
1211
// Load kcl.mod file of the new downloaded dependencies.
@@ -1154,13 +1218,34 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie
1154
1218
if os .IsNotExist (err ) {
1155
1219
continue
1156
1220
}
1157
- return nil , err
1221
+ return nil , nil , err
1158
1222
}
1159
1223
1160
1224
// Download the dependencies.
1161
- nested , err := c .downloadDeps (deppkg .ModFile .Dependencies , lockDeps )
1225
+ nested , nestedDepGraphs , err := c .downloadDeps (deppkg .ModFile .Dependencies , lockDeps )
1162
1226
if err != nil {
1163
- return nil , err
1227
+ return nil , nil , err
1228
+ }
1229
+
1230
+ // merge the depGraph with the nestedDepGraphs.
1231
+ src , err := FindSource (depGraphs [i ])
1232
+ if err != nil {
1233
+ return nil , nil , err
1234
+ }
1235
+
1236
+ for _ , g := range nestedDepGraphs {
1237
+ depGraphs [i ], err = graph .Union (g , depGraphs [i ])
1238
+ if err != nil {
1239
+ return nil , nil , err
1240
+ }
1241
+ srcOfNestedg , err := FindSource (g )
1242
+ if err != nil {
1243
+ return nil , nil , err
1244
+ }
1245
+ err = depGraphs [i ].AddEdge (src , srcOfNestedg )
1246
+ if err != nil {
1247
+ return nil , nil , err
1248
+ }
1164
1249
}
1165
1250
1166
1251
// Update kcl.mod.
@@ -1169,9 +1254,10 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie
1169
1254
newDeps .Deps [d .Name ] = d
1170
1255
}
1171
1256
}
1257
+ i ++
1172
1258
}
1173
1259
1174
- return & newDeps , nil
1260
+ return & newDeps , depGraphs , nil
1175
1261
}
1176
1262
1177
1263
// pullTarFromOci will pull a kcl package tar file from oci registry.
@@ -1244,3 +1330,23 @@ func check(dep pkg.Dependency, newDepPath string) bool {
1244
1330
1245
1331
return dep .Sum == sum
1246
1332
}
1333
+
1334
+ func FindSource [K comparable , T any ](g graph.Graph [K , T ]) (K , error ) {
1335
+ var src K
1336
+ if ! g .Traits ().IsDirected {
1337
+ return src , fmt .Errorf ("cannot find source of a non-DAG graph " )
1338
+ }
1339
+
1340
+ predecessorMap , err := g .PredecessorMap ()
1341
+ if err != nil {
1342
+ return src , fmt .Errorf ("failed to get predecessor map: %w" , err )
1343
+ }
1344
+
1345
+ for vertex , predecessors := range predecessorMap {
1346
+ if len (predecessors ) == 0 {
1347
+ src = vertex
1348
+ break
1349
+ }
1350
+ }
1351
+ return src , nil
1352
+ }
0 commit comments