1
2
3
4
5 package modcmd
6
7 import (
8 "context"
9 "fmt"
10 "strings"
11
12 "cmd/go/internal/base"
13 "cmd/go/internal/imports"
14 "cmd/go/internal/modload"
15
16 "golang.org/x/mod/module"
17 )
18
19 var cmdWhy = &base.Command{
20 UsageLine: "go mod why [-m] [-vendor] packages...",
21 Short: "explain why packages or modules are needed",
22 Long: `
23 Why shows a shortest path in the import graph from the main module to
24 each of the listed packages. If the -m flag is given, why treats the
25 arguments as a list of modules and finds a path to any package in each
26 of the modules.
27
28 By default, why queries the graph of packages matched by "go list all",
29 which includes tests for reachable packages. The -vendor flag causes why
30 to exclude tests of dependencies.
31
32 The output is a sequence of stanzas, one for each package or module
33 name on the command line, separated by blank lines. Each stanza begins
34 with a comment line "# package" or "# module" giving the target
35 package or module. Subsequent lines give a path through the import
36 graph, one package per line. If the package or module is not
37 referenced from the main module, the stanza will display a single
38 parenthesized note indicating that fact.
39
40 For example:
41
42 $ go mod why golang.org/x/text/language golang.org/x/text/encoding
43 # golang.org/x/text/language
44 rsc.io/quote
45 rsc.io/sampler
46 golang.org/x/text/language
47
48 # golang.org/x/text/encoding
49 (main module does not need package golang.org/x/text/encoding)
50 $
51
52 See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'.
53 `,
54 }
55
56 var (
57 whyM = cmdWhy.Flag.Bool("m", false, "")
58 whyVendor = cmdWhy.Flag.Bool("vendor", false, "")
59 )
60
61 func init() {
62 cmdWhy.Run = runWhy
63 base.AddModCommonFlags(&cmdWhy.Flag)
64 }
65
66 func runWhy(ctx context.Context, cmd *base.Command, args []string) {
67 modload.ForceUseModules = true
68 modload.RootMode = modload.NeedRoot
69
70 loadOpts := modload.PackageOpts{
71 Tags: imports.AnyTags(),
72 LoadTests: !*whyVendor,
73 SilenceErrors: true,
74 UseVendorAll: *whyVendor,
75 }
76
77 if *whyM {
78 listU := false
79 listVersions := false
80 listRetractions := false
81 for _, arg := range args {
82 if strings.Contains(arg, "@") {
83 base.Fatalf("go mod why: module query not allowed")
84 }
85 }
86 mods := modload.ListModules(ctx, args, listU, listVersions, listRetractions)
87 byModule := make(map[module.Version][]string)
88 _, pkgs := modload.LoadPackages(ctx, loadOpts, "all")
89 for _, path := range pkgs {
90 m := modload.PackageModule(path)
91 if m.Path != "" {
92 byModule[m] = append(byModule[m], path)
93 }
94 }
95 sep := ""
96 for _, m := range mods {
97 best := ""
98 bestDepth := 1000000000
99 for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] {
100 d := modload.WhyDepth(path)
101 if d > 0 && d < bestDepth {
102 best = path
103 bestDepth = d
104 }
105 }
106 why := modload.Why(best)
107 if why == "" {
108 vendoring := ""
109 if *whyVendor {
110 vendoring = " to vendor"
111 }
112 why = "(main module does not need" + vendoring + " module " + m.Path + ")\n"
113 }
114 fmt.Printf("%s# %s\n%s", sep, m.Path, why)
115 sep = "\n"
116 }
117 } else {
118
119 matches, _ := modload.LoadPackages(ctx, loadOpts, args...)
120
121 modload.LoadPackages(ctx, loadOpts, "all")
122
123 sep := ""
124 for _, m := range matches {
125 for _, path := range m.Pkgs {
126 why := modload.Why(path)
127 if why == "" {
128 vendoring := ""
129 if *whyVendor {
130 vendoring = " to vendor"
131 }
132 why = "(main module does not need" + vendoring + " package " + path + ")\n"
133 }
134 fmt.Printf("%s# %s\n%s", sep, path, why)
135 sep = "\n"
136 }
137 }
138 }
139 }
140
View as plain text