Skip to content

Commit

Permalink
allowing specifying of multiple mainModules (#48)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
RinkiyaKeDad authored Aug 2, 2021
1 parent 6171899 commit a316b79
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 49 deletions.
4 changes: 2 additions & 2 deletions cmd/cycles.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ var cyclesCmd = &cobra.Command{
Short: "Prints cycles in dependency chains.",
Long: `Will show all the cycles in the dependencies of the project.`,
RunE: func(cmd *cobra.Command, args []string) error {
overview := getDepInfo()
overview := getDepInfo(nil)
var cycleChains []Chain
var temp Chain
getCycleChains(overview.MainModuleName, overview.Graph, temp, &cycleChains)
getCycleChains(overview.MainModules[0], overview.Graph, temp, &cycleChains)
cycles := getCycles(cycleChains)

if !jsonOutputCycles {
Expand Down
12 changes: 6 additions & 6 deletions cmd/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var graphCmd = &cobra.Command{
For example to generate a svg image use:
twopi -Tsvg -o dag.svg graph.dot`,
RunE: func(cmd *cobra.Command, args []string) error {
overview := getDepInfo()
overview := getDepInfo(nil)
// strict ensures that there is only one edge between two vertices
// overlap = false ensures the vertices don't overlap
fileContents := "strict digraph {\ngraph [overlap=false];\n"
Expand All @@ -43,7 +43,7 @@ var graphCmd = &cobra.Command{
if dep != "" {
var chains []Chain
var temp Chain
getAllChains(overview.MainModuleName, overview.Graph, temp, &chains)
getAllChains(overview.MainModules[0], overview.Graph, temp, &chains)
fileContents += getFileContentsForSingleDep(chains, dep)
} else {
fileContents += getFileContentsForAllDeps(overview)
Expand Down Expand Up @@ -106,9 +106,9 @@ func getFileContentsForSingleDep(chains []Chain, dep string) string {
func getFileContentsForAllDeps(overview *DependencyOverview) string {

// color the main module as yellow
data := colorMainNode(overview.MainModuleName)
allDeps := getAllDeps(overview.Graph[overview.MainModuleName], overview.TransDepList)
allDeps = append(allDeps, overview.MainModuleName)
data := colorMainNode(overview.MainModules[0])
allDeps := getAllDeps(overview.DirectDepList, overview.TransDepList)
allDeps = append(allDeps, overview.MainModules[0])
sort.Strings(allDeps)
for _, dep := range allDeps {
_, ok := overview.Graph[dep]
Expand All @@ -117,7 +117,7 @@ func getFileContentsForAllDeps(overview *DependencyOverview) string {
}
// main module can never be a neighbour
for _, neighbour := range overview.Graph[dep] {
if dep == overview.MainModuleName {
if dep == overview.MainModules[0] {
// for the main module use a colored node
data += fmt.Sprintf("\"MainNode\" -> \"%s\"\n", neighbour)
} else {
Expand Down
4 changes: 2 additions & 2 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ var listCmd = &cobra.Command{
Long: `Gives a list of all the dependencies of the project.
These include both direct as well as transitive dependencies.`,
RunE: func(cmd *cobra.Command, args []string) error {
depGraph := getDepInfo()
depGraph := getDepInfo(nil)
fmt.Println("List of all dependencies:")
allDeps := getAllDeps(depGraph.Graph[depGraph.MainModuleName], depGraph.TransDepList)
allDeps := getAllDeps(depGraph.DirectDepList, depGraph.TransDepList)
printDeps(allDeps)
return nil
},
Expand Down
19 changes: 11 additions & 8 deletions cmd/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

var jsonOutput bool
var verbose bool
var mainModules []string

type Chain []string

Expand All @@ -33,22 +34,23 @@ var statsCmd = &cobra.Command{
Use: "stats",
Short: "Shows metrics about dependency chains",
Long: `Provides the following metrics:
1. Total Dependencies: Total number of dependencies of the project
2. Max Depth of Dependencies: Number of dependencies in the longest dependency chain
3. Transitive Dependencies: Total number of transitive dependencies (dependencies which are not direct dependencies of the project)`,
1. Direct Dependencies: Total number of dependencies required by the mainModule(s) directly
2. Transitive Dependencies: Total number of transitive dependencies (dependencies which are further needed by direct dependencies of the project)
3. Total Dependencies: Total number of dependencies of the mainModule(s)
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`,
RunE: func(cmd *cobra.Command, args []string) error {
depGraph := getDepInfo()
depGraph := getDepInfo(mainModules)

// get the longest chain
var longestChain Chain
var temp Chain
getLongestChain(depGraph.MainModuleName, depGraph.Graph, temp, &longestChain)
getLongestChain(depGraph.MainModules[0], depGraph.Graph, temp, &longestChain)

// get values
maxDepth := len(longestChain)
directDeps := len(depGraph.Graph[depGraph.MainModuleName])
totalDeps := len(getAllDeps(depGraph.Graph[depGraph.MainModuleName], depGraph.TransDepList))
directDeps := len(depGraph.DirectDepList)
transitiveDeps := len(depGraph.TransDepList)
totalDeps := len(getAllDeps(depGraph.DirectDepList, depGraph.TransDepList))

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

if verbose {
fmt.Println("All dependencies:")
printDeps(append(depGraph.Graph[depGraph.MainModuleName], depGraph.TransDepList...))
printDeps(getAllDeps(depGraph.DirectDepList, depGraph.TransDepList))
}

// print the longest chain
Expand Down Expand Up @@ -118,4 +120,5 @@ func init() {
rootCmd.AddCommand(statsCmd)
statsCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Get additional details")
statsCmd.Flags().BoolVarP(&jsonOutput, "json", "j", false, "Get the output in JSON format")
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")
}
45 changes: 24 additions & 21 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ func printChain(slice []string) {
type DependencyOverview struct {
// Dependency graph edges modelled as node plus adjacency nodes
Graph map[string][]string
// List of all direct dependencies
DirectDepList []string
// List of all transitive dependencies
TransDepList []string
// Name of the module from which the dependencies are computed
MainModuleName string
MainModules []string
}

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

// create a graph of dependencies from that output
depGraph := make(map[string][]string)
graph := make(map[string][]string)
scanner := bufio.NewScanner(strings.NewReader(goModGraphOutputString))

// transDeps will store all the transitive dependencies
var transDeps []string
mainModule := "notset"

for scanner.Scan() {
line := scanner.Text()
words := strings.Fields(line)
Expand All @@ -65,27 +64,30 @@ func getDepInfo() *DependencyOverview {
words[1] = (strings.Split(words[1], "@"))[0]

// we don't want to add the same dep again
if !contains(depGraph[words[0]], words[1]) {
depGraph[words[0]] = append(depGraph[words[0]], words[1])
if !contains(graph[words[0]], words[1]) {
graph[words[0]] = append(graph[words[0]], words[1])
}

if mainModule == "notset" {
mainModule = words[0]
if len(depGraph.MainModules) == 0 {
depGraph.MainModules = append(depGraph.MainModules, words[0])
}

// anything where the LHS is not mainModule
// is a transitive dependency
if words[0] != mainModule {
if !contains(transDeps, words[1]) {
transDeps = append(transDeps, words[1])
// if the LHS is a mainModule
// then RHS is a direct dep else transitive dep
if contains(depGraph.MainModules, words[0]) && contains(depGraph.MainModules, words[1]) {
continue
} else if contains(depGraph.MainModules, words[0]) {
if !contains(depGraph.DirectDepList, words[1]) {
depGraph.DirectDepList = append(depGraph.DirectDepList, words[1])
}
} else if !contains(depGraph.MainModules, words[0]) {
if !contains(depGraph.TransDepList, words[1]) {
depGraph.TransDepList = append(depGraph.TransDepList, words[1])
}
}
}
return &DependencyOverview{
Graph: depGraph,
TransDepList: transDeps,
MainModuleName: mainModule,
}
depGraph.Graph = graph
return &depGraph
}

func printDeps(deps []string) {
Expand All @@ -97,6 +99,7 @@ func printDeps(deps []string) {
fmt.Println()
}

// we need this since a dependency can be both a direct and an indirect dependency
func getAllDeps(directDeps []string, transDeps []string) []string {
var allDeps []string
for _, dep := range directDeps {
Expand Down
28 changes: 18 additions & 10 deletions cmd/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,13 @@ func Test_getChains_simple(t *testing.T) {
graph["F"] = []string{"H"}

transDeps := []string{"E", "G", "F", "H"}
directDeps := []string{"B", "C", "D"}
mainModules := []string{"A"}
overview := &DependencyOverview{
Graph: graph,
TransDepList: transDeps,
MainModuleName: "A",
Graph: graph,
TransDepList: transDeps,
DirectDepList: directDeps,
MainModules: mainModules,
}

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

transDeps := []string{"D", "E", "F", "G", "H"}
directDeps := []string{"B", "C"}
mainModules := []string{"A"}
overview := &DependencyOverview{
Graph: graph,
TransDepList: transDeps,
MainModuleName: "A",
Graph: graph,
TransDepList: transDeps,
DirectDepList: directDeps,
MainModules: mainModules,
}

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

transDeps := []string{"C", "B", "E", "F", "D"}

directDeps := []string{"B", "C"}
mainModules := []string{"A"}
overview := &DependencyOverview{
Graph: graph,
TransDepList: transDeps,
MainModuleName: "A",
Graph: graph,
TransDepList: transDeps,
DirectDepList: directDeps,
MainModules: mainModules,
}

var cycleChains []Chain
Expand Down

0 comments on commit a316b79

Please sign in to comment.