Skip to content

Commit a316b79

Browse files
authored
allowing specifying of multiple mainModules (#48)
* initial commit Signed-off-by: RinkiyaKeDad <[email protected]> * made code changes need to test more Signed-off-by: RinkiyaKeDad <[email protected]> * reverted getDepInfo function signature Signed-off-by: RinkiyaKeDad <[email protected]> * change -m flag help message Signed-off-by: RinkiyaKeDad <[email protected]> * fixed wrong max depth error Signed-off-by: RinkiyaKeDad <[email protected]> * updated description of max length of deps Signed-off-by: RinkiyaKeDad <[email protected]>
1 parent 6171899 commit a316b79

File tree

6 files changed

+63
-49
lines changed

6 files changed

+63
-49
lines changed

cmd/cycles.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ var cyclesCmd = &cobra.Command{
3131
Short: "Prints cycles in dependency chains.",
3232
Long: `Will show all the cycles in the dependencies of the project.`,
3333
RunE: func(cmd *cobra.Command, args []string) error {
34-
overview := getDepInfo()
34+
overview := getDepInfo(nil)
3535
var cycleChains []Chain
3636
var temp Chain
37-
getCycleChains(overview.MainModuleName, overview.Graph, temp, &cycleChains)
37+
getCycleChains(overview.MainModules[0], overview.Graph, temp, &cycleChains)
3838
cycles := getCycles(cycleChains)
3939

4040
if !jsonOutputCycles {

cmd/graph.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var graphCmd = &cobra.Command{
3434
For example to generate a svg image use:
3535
twopi -Tsvg -o dag.svg graph.dot`,
3636
RunE: func(cmd *cobra.Command, args []string) error {
37-
overview := getDepInfo()
37+
overview := getDepInfo(nil)
3838
// strict ensures that there is only one edge between two vertices
3939
// overlap = false ensures the vertices don't overlap
4040
fileContents := "strict digraph {\ngraph [overlap=false];\n"
@@ -43,7 +43,7 @@ var graphCmd = &cobra.Command{
4343
if dep != "" {
4444
var chains []Chain
4545
var temp Chain
46-
getAllChains(overview.MainModuleName, overview.Graph, temp, &chains)
46+
getAllChains(overview.MainModules[0], overview.Graph, temp, &chains)
4747
fileContents += getFileContentsForSingleDep(chains, dep)
4848
} else {
4949
fileContents += getFileContentsForAllDeps(overview)
@@ -106,9 +106,9 @@ func getFileContentsForSingleDep(chains []Chain, dep string) string {
106106
func getFileContentsForAllDeps(overview *DependencyOverview) string {
107107

108108
// color the main module as yellow
109-
data := colorMainNode(overview.MainModuleName)
110-
allDeps := getAllDeps(overview.Graph[overview.MainModuleName], overview.TransDepList)
111-
allDeps = append(allDeps, overview.MainModuleName)
109+
data := colorMainNode(overview.MainModules[0])
110+
allDeps := getAllDeps(overview.DirectDepList, overview.TransDepList)
111+
allDeps = append(allDeps, overview.MainModules[0])
112112
sort.Strings(allDeps)
113113
for _, dep := range allDeps {
114114
_, ok := overview.Graph[dep]
@@ -117,7 +117,7 @@ func getFileContentsForAllDeps(overview *DependencyOverview) string {
117117
}
118118
// main module can never be a neighbour
119119
for _, neighbour := range overview.Graph[dep] {
120-
if dep == overview.MainModuleName {
120+
if dep == overview.MainModules[0] {
121121
// for the main module use a colored node
122122
data += fmt.Sprintf("\"MainNode\" -> \"%s\"\n", neighbour)
123123
} else {

cmd/list.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ var listCmd = &cobra.Command{
2929
Long: `Gives a list of all the dependencies of the project.
3030
These include both direct as well as transitive dependencies.`,
3131
RunE: func(cmd *cobra.Command, args []string) error {
32-
depGraph := getDepInfo()
32+
depGraph := getDepInfo(nil)
3333
fmt.Println("List of all dependencies:")
34-
allDeps := getAllDeps(depGraph.Graph[depGraph.MainModuleName], depGraph.TransDepList)
34+
allDeps := getAllDeps(depGraph.DirectDepList, depGraph.TransDepList)
3535
printDeps(allDeps)
3636
return nil
3737
},

cmd/stats.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
var jsonOutput bool
2727
var verbose bool
28+
var mainModules []string
2829

2930
type Chain []string
3031

@@ -33,22 +34,23 @@ var statsCmd = &cobra.Command{
3334
Use: "stats",
3435
Short: "Shows metrics about dependency chains",
3536
Long: `Provides the following metrics:
36-
1. Total Dependencies: Total number of dependencies of the project
37-
2. Max Depth of Dependencies: Number of dependencies in the longest dependency chain
38-
3. Transitive Dependencies: Total number of transitive dependencies (dependencies which are not direct dependencies of the project)`,
37+
1. Direct Dependencies: Total number of dependencies required by the mainModule(s) directly
38+
2. Transitive Dependencies: Total number of transitive dependencies (dependencies which are further needed by direct dependencies of the project)
39+
3. Total Dependencies: Total number of dependencies of the mainModule(s)
40+
4. Max Depth of Dependencies: Length of the longest chain starting from the first mainModule; defaults to length from the first module encountered in "go mod graph" output`,
3941
RunE: func(cmd *cobra.Command, args []string) error {
40-
depGraph := getDepInfo()
42+
depGraph := getDepInfo(mainModules)
4143

4244
// get the longest chain
4345
var longestChain Chain
4446
var temp Chain
45-
getLongestChain(depGraph.MainModuleName, depGraph.Graph, temp, &longestChain)
47+
getLongestChain(depGraph.MainModules[0], depGraph.Graph, temp, &longestChain)
4648

4749
// get values
4850
maxDepth := len(longestChain)
49-
directDeps := len(depGraph.Graph[depGraph.MainModuleName])
50-
totalDeps := len(getAllDeps(depGraph.Graph[depGraph.MainModuleName], depGraph.TransDepList))
51+
directDeps := len(depGraph.DirectDepList)
5152
transitiveDeps := len(depGraph.TransDepList)
53+
totalDeps := len(getAllDeps(depGraph.DirectDepList, depGraph.TransDepList))
5254

5355
if !jsonOutput {
5456
fmt.Printf("Direct Dependencies: %d \n", directDeps)
@@ -59,7 +61,7 @@ var statsCmd = &cobra.Command{
5961

6062
if verbose {
6163
fmt.Println("All dependencies:")
62-
printDeps(append(depGraph.Graph[depGraph.MainModuleName], depGraph.TransDepList...))
64+
printDeps(getAllDeps(depGraph.DirectDepList, depGraph.TransDepList))
6365
}
6466

6567
// print the longest chain
@@ -118,4 +120,5 @@ func init() {
118120
rootCmd.AddCommand(statsCmd)
119121
statsCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Get additional details")
120122
statsCmd.Flags().BoolVarP(&jsonOutput, "json", "j", false, "Get the output in JSON format")
123+
statsCmd.Flags().StringSliceVarP(&mainModules, "mainModules", "m", []string{}, "Enter modules whose dependencies should be considered direct dependencies; defaults to the first module encountered in `go mod graph` output")
121124
}

cmd/utils.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,16 @@ func printChain(slice []string) {
3434
type DependencyOverview struct {
3535
// Dependency graph edges modelled as node plus adjacency nodes
3636
Graph map[string][]string
37+
// List of all direct dependencies
38+
DirectDepList []string
3739
// List of all transitive dependencies
3840
TransDepList []string
3941
// Name of the module from which the dependencies are computed
40-
MainModuleName string
42+
MainModules []string
4143
}
4244

43-
func getDepInfo() *DependencyOverview {
45+
func getDepInfo(mainModules []string) *DependencyOverview {
46+
depGraph := DependencyOverview{MainModules: mainModules}
4447
// get output of "go mod graph" in a string
4548
goModGraph := exec.Command("go", "mod", "graph")
4649
goModGraphOutput, err := goModGraph.Output()
@@ -50,13 +53,9 @@ func getDepInfo() *DependencyOverview {
5053
goModGraphOutputString := string(goModGraphOutput)
5154

5255
// create a graph of dependencies from that output
53-
depGraph := make(map[string][]string)
56+
graph := make(map[string][]string)
5457
scanner := bufio.NewScanner(strings.NewReader(goModGraphOutputString))
5558

56-
// transDeps will store all the transitive dependencies
57-
var transDeps []string
58-
mainModule := "notset"
59-
6059
for scanner.Scan() {
6160
line := scanner.Text()
6261
words := strings.Fields(line)
@@ -65,27 +64,30 @@ func getDepInfo() *DependencyOverview {
6564
words[1] = (strings.Split(words[1], "@"))[0]
6665

6766
// we don't want to add the same dep again
68-
if !contains(depGraph[words[0]], words[1]) {
69-
depGraph[words[0]] = append(depGraph[words[0]], words[1])
67+
if !contains(graph[words[0]], words[1]) {
68+
graph[words[0]] = append(graph[words[0]], words[1])
7069
}
7170

72-
if mainModule == "notset" {
73-
mainModule = words[0]
71+
if len(depGraph.MainModules) == 0 {
72+
depGraph.MainModules = append(depGraph.MainModules, words[0])
7473
}
7574

76-
// anything where the LHS is not mainModule
77-
// is a transitive dependency
78-
if words[0] != mainModule {
79-
if !contains(transDeps, words[1]) {
80-
transDeps = append(transDeps, words[1])
75+
// if the LHS is a mainModule
76+
// then RHS is a direct dep else transitive dep
77+
if contains(depGraph.MainModules, words[0]) && contains(depGraph.MainModules, words[1]) {
78+
continue
79+
} else if contains(depGraph.MainModules, words[0]) {
80+
if !contains(depGraph.DirectDepList, words[1]) {
81+
depGraph.DirectDepList = append(depGraph.DirectDepList, words[1])
82+
}
83+
} else if !contains(depGraph.MainModules, words[0]) {
84+
if !contains(depGraph.TransDepList, words[1]) {
85+
depGraph.TransDepList = append(depGraph.TransDepList, words[1])
8186
}
8287
}
8388
}
84-
return &DependencyOverview{
85-
Graph: depGraph,
86-
TransDepList: transDeps,
87-
MainModuleName: mainModule,
88-
}
89+
depGraph.Graph = graph
90+
return &depGraph
8991
}
9092

9193
func printDeps(deps []string) {
@@ -97,6 +99,7 @@ func printDeps(deps []string) {
9799
fmt.Println()
98100
}
99101

102+
// we need this since a dependency can be both a direct and an indirect dependency
100103
func getAllDeps(directDeps []string, transDeps []string) []string {
101104
var allDeps []string
102105
for _, dep := range directDeps {

cmd/utils_test.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ func Test_getChains_simple(t *testing.T) {
4545
graph["F"] = []string{"H"}
4646

4747
transDeps := []string{"E", "G", "F", "H"}
48+
directDeps := []string{"B", "C", "D"}
49+
mainModules := []string{"A"}
4850
overview := &DependencyOverview{
49-
Graph: graph,
50-
TransDepList: transDeps,
51-
MainModuleName: "A",
51+
Graph: graph,
52+
TransDepList: transDeps,
53+
DirectDepList: directDeps,
54+
MainModules: mainModules,
5255
}
5356

5457
var cycleChains []Chain
@@ -137,10 +140,13 @@ func Test_getChains_cycle(t *testing.T) {
137140
graph["H"] = []string{"D"}
138141

139142
transDeps := []string{"D", "E", "F", "G", "H"}
143+
directDeps := []string{"B", "C"}
144+
mainModules := []string{"A"}
140145
overview := &DependencyOverview{
141-
Graph: graph,
142-
TransDepList: transDeps,
143-
MainModuleName: "A",
146+
Graph: graph,
147+
TransDepList: transDeps,
148+
DirectDepList: directDeps,
149+
MainModules: mainModules,
144150
}
145151

146152
var cycleChains []Chain
@@ -228,11 +234,13 @@ func Test_getChains_cycle_2(t *testing.T) {
228234
graph["D"] = []string{"C"}
229235

230236
transDeps := []string{"C", "B", "E", "F", "D"}
231-
237+
directDeps := []string{"B", "C"}
238+
mainModules := []string{"A"}
232239
overview := &DependencyOverview{
233-
Graph: graph,
234-
TransDepList: transDeps,
235-
MainModuleName: "A",
240+
Graph: graph,
241+
TransDepList: transDeps,
242+
DirectDepList: directDeps,
243+
MainModules: mainModules,
236244
}
237245

238246
var cycleChains []Chain

0 commit comments

Comments
 (0)