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.
9 "cmd/go/internal/modload"
10 "cmd/go/internal/module"
15 var cmdWhy
= &base
.Command
{
16 UsageLine
: "go mod why [-m] [-vendor] packages...",
17 Short
: "explain why packages or modules are needed",
19 Why shows a shortest path in the import graph from the main module to
20 each of the listed packages. If the -m flag is given, why treats the
21 arguments as a list of modules and finds a path to any package in each
24 By default, why queries the graph of packages matched by "go list all",
25 which includes tests for reachable packages. The -vendor flag causes why
26 to exclude tests of dependencies.
28 The output is a sequence of stanzas, one for each package or module
29 name on the command line, separated by blank lines. Each stanza begins
30 with a comment line "# package" or "# module" giving the target
31 package or module. Subsequent lines give a path through the import
32 graph, one package per line. If the package or module is not
33 referenced from the main module, the stanza will display a single
34 parenthesized note indicating that fact.
38 $ go mod why golang.org/x/text/language golang.org/x/text/encoding
39 # golang.org/x/text/language
42 golang.org/x/text/language
44 # golang.org/x/text/encoding
45 (main module does not need package golang.org/x/text/encoding)
51 whyM
= cmdWhy
.Flag
.Bool("m", false, "")
52 whyVendor
= cmdWhy
.Flag
.Bool("vendor", false, "")
56 cmdWhy
.Run
= runWhy
// break init cycle
59 func runWhy(cmd
*base
.Command
, args
[]string) {
60 loadALL
:= modload
.LoadALL
62 loadALL
= modload
.LoadVendor
67 for _
, arg
:= range args
{
68 if strings
.Contains(arg
, "@") {
69 base
.Fatalf("go mod why: module query not allowed")
72 mods
:= modload
.ListModules(args
, listU
, listVersions
)
73 byModule
:= make(map[module
.Version
][]string)
74 for _
, path
:= range loadALL() {
75 m
:= modload
.PackageModule(path
)
77 byModule
[m
] = append(byModule
[m
], path
)
81 for _
, m
:= range mods
{
83 bestDepth
:= 1000000000
84 for _
, path
:= range byModule
[module
.Version
{Path
: m
.Path
, Version
: m
.Version
}] {
85 d
:= modload
.WhyDepth(path
)
86 if d
> 0 && d
< bestDepth
{
91 why
:= modload
.Why(best
)
95 vendoring
= " to vendor"
97 why
= "(main module does not need" + vendoring
+ " module " + m
.Path
+ ")\n"
99 fmt
.Printf("%s# %s\n%s", sep
, m
.Path
, why
)
103 matches
:= modload
.ImportPaths(args
) // resolve to packages
104 loadALL() // rebuild graph, from main module (not from named packages)
106 for _
, m
:= range matches
{
107 for _
, path
:= range m
.Pkgs
{
108 why
:= modload
.Why(path
)
112 vendoring
= " to vendor"
114 why
= "(main module does not need" + vendoring
+ " package " + path
+ ")\n"
116 fmt
.Printf("%s# %s\n%s", sep
, path
, why
)