1
2
3
4
5
6
7 package modcmd
8
9 import (
10 "bufio"
11 "context"
12 "os"
13 "sort"
14
15 "cmd/go/internal/base"
16 "cmd/go/internal/modload"
17
18 "golang.org/x/mod/module"
19 )
20
21 var cmdGraph = &base.Command{
22 UsageLine: "go mod graph",
23 Short: "print module requirement graph",
24 Long: `
25 Graph prints the module requirement graph (with replacements applied)
26 in text form. Each line in the output has two space-separated fields: a module
27 and one of its requirements. Each module is identified as a string of the form
28 path@version, except for the main module, which has no @version suffix.
29
30 See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.
31 `,
32 Run: runGraph,
33 }
34
35 func init() {
36 base.AddModCommonFlags(&cmdGraph.Flag)
37 }
38
39 func runGraph(ctx context.Context, cmd *base.Command, args []string) {
40 if len(args) > 0 {
41 base.Fatalf("go mod graph: graph takes no arguments")
42 }
43 modload.ForceUseModules = true
44 modload.RootMode = modload.NeedRoot
45 modload.LoadAllModules(ctx)
46
47 reqs := modload.MinReqs()
48 format := func(m module.Version) string {
49 if m.Version == "" {
50 return m.Path
51 }
52 return m.Path + "@" + m.Version
53 }
54
55 var out []string
56 var deps int
57 seen := map[module.Version]bool{modload.Target: true}
58 queue := []module.Version{modload.Target}
59 for len(queue) > 0 {
60 var m module.Version
61 m, queue = queue[0], queue[1:]
62 list, _ := reqs.Required(m)
63 for _, r := range list {
64 if !seen[r] {
65 queue = append(queue, r)
66 seen[r] = true
67 }
68 out = append(out, format(m)+" "+format(r)+"\n")
69 }
70 if m == modload.Target {
71 deps = len(out)
72 }
73 }
74
75 sort.Slice(out[deps:], func(i, j int) bool {
76 return out[deps+i][0] < out[deps+j][0]
77 })
78
79 w := bufio.NewWriter(os.Stdout)
80 for _, line := range out {
81 w.WriteString(line)
82 }
83 w.Flush()
84 }
85
View as plain text