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