Source file src/cmd/go/internal/modcmd/why.go

     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  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  
    17  var cmdWhy = &base.Command{
    18  	UsageLine: "go mod why [-m] [-vendor] packages...",
    19  	Short:     "explain why packages or modules are needed",
    20  	Long: `
    21  Why shows a shortest path in the import graph from the main module to
    22  each of the listed packages. If the -m flag is given, why treats the
    23  arguments as a list of modules and finds a path to any package in each
    24  of the modules.
    25  
    26  By default, why queries the graph of packages matched by "go list all",
    27  which includes tests for reachable packages. The -vendor flag causes why
    28  to exclude tests of dependencies.
    29  
    30  The output is a sequence of stanzas, one for each package or module
    31  name on the command line, separated by blank lines. Each stanza begins
    32  with a comment line "# package" or "# module" giving the target
    33  package or module. Subsequent lines give a path through the import
    34  graph, one package per line. If the package or module is not
    35  referenced from the main module, the stanza will display a single
    36  parenthesized note indicating that fact.
    37  
    38  For 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  
    50  See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'.
    51  	`,
    52  }
    53  
    54  var (
    55  	whyM      = cmdWhy.Flag.Bool("m", false, "")
    56  	whyVendor = cmdWhy.Flag.Bool("vendor", false, "")
    57  )
    58  
    59  func init() {
    60  	cmdWhy.Run = runWhy // break init cycle
    61  	base.AddChdirFlag(&cmdWhy.Flag)
    62  	base.AddModCommonFlags(&cmdWhy.Flag)
    63  }
    64  
    65  func 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  

View as plain text