1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// go mod graph 6 7package modcmd 8 9import ( 10 "bufio" 11 "context" 12 "os" 13 14 "cmd/go/internal/base" 15 "cmd/go/internal/cfg" 16 "cmd/go/internal/gover" 17 "cmd/go/internal/modload" 18 "cmd/go/internal/toolchain" 19 20 "golang.org/x/mod/module" 21) 22 23var cmdGraph = &base.Command{ 24 UsageLine: "go mod graph [-go=version] [-x]", 25 Short: "print module requirement graph", 26 Long: ` 27Graph prints the module requirement graph (with replacements applied) 28in text form. Each line in the output has two space-separated fields: a module 29and one of its requirements. Each module is identified as a string of the form 30path@version, except for the main module, which has no @version suffix. 31 32The -go flag causes graph to report the module graph as loaded by the 33given Go version, instead of the version indicated by the 'go' directive 34in the go.mod file. 35 36The -x flag causes graph to print the commands graph executes. 37 38See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'. 39 `, 40 Run: runGraph, 41} 42 43var ( 44 graphGo goVersionFlag 45) 46 47func init() { 48 cmdGraph.Flag.Var(&graphGo, "go", "") 49 cmdGraph.Flag.BoolVar(&cfg.BuildX, "x", false, "") 50 base.AddChdirFlag(&cmdGraph.Flag) 51 base.AddModCommonFlags(&cmdGraph.Flag) 52} 53 54func runGraph(ctx context.Context, cmd *base.Command, args []string) { 55 modload.InitWorkfile() 56 57 if len(args) > 0 { 58 base.Fatalf("go: 'go mod graph' accepts no arguments") 59 } 60 modload.ForceUseModules = true 61 modload.RootMode = modload.NeedRoot 62 63 goVersion := graphGo.String() 64 if goVersion != "" && gover.Compare(gover.Local(), goVersion) < 0 { 65 toolchain.SwitchOrFatal(ctx, &gover.TooNewError{ 66 What: "-go flag", 67 GoVersion: goVersion, 68 }) 69 } 70 71 mg, err := modload.LoadModGraph(ctx, goVersion) 72 if err != nil { 73 base.Fatal(err) 74 } 75 76 w := bufio.NewWriter(os.Stdout) 77 defer w.Flush() 78 79 format := func(m module.Version) { 80 w.WriteString(m.Path) 81 if m.Version != "" { 82 w.WriteString("@") 83 w.WriteString(m.Version) 84 } 85 } 86 87 mg.WalkBreadthFirst(func(m module.Version) { 88 reqs, _ := mg.RequiredBy(m) 89 for _, r := range reqs { 90 format(m) 91 w.WriteByte(' ') 92 format(r) 93 w.WriteByte('\n') 94 } 95 }) 96} 97