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.AddModCommonFlags(&cmdWhy.Flag)
    62  }
    63  
    64  func runWhy(ctx context.Context, cmd *base.Command, args []string) {
    65  	modload.InitWorkfile()
    66  	modload.ForceUseModules = true
    67  	modload.RootMode = modload.NeedRoot
    68  	modload.ExplicitWriteGoMod = true // don't write go.mod in ListModules
    69  
    70  	loadOpts := modload.PackageOpts{
    71  		Tags:                     imports.AnyTags(),
    72  		VendorModulesInGOROOTSrc: true,
    73  		LoadTests:                !*whyVendor,
    74  		SilencePackageErrors:     true,
    75  		UseVendorAll:             *whyVendor,
    76  	}
    77  
    78  	if *whyM {
    79  		for _, arg := range args {
    80  			if strings.Contains(arg, "@") {
    81  				base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg)
    82  			}
    83  		}
    84  
    85  		mods, err := modload.ListModules(ctx, args, 0)
    86  		if err != nil {
    87  			base.Fatalf("go: %v", err)
    88  		}
    89  
    90  		byModule := make(map[string][]string)
    91  		_, pkgs := modload.LoadPackages(ctx, loadOpts, "all")
    92  		for _, path := range pkgs {
    93  			m := modload.PackageModule(path)
    94  			if m.Path != "" {
    95  				byModule[m.Path] = append(byModule[m.Path], path)
    96  			}
    97  		}
    98  		sep := ""
    99  		for _, m := range mods {
   100  			best := ""
   101  			bestDepth := 1000000000
   102  			for _, path := range byModule[m.Path] {
   103  				d := modload.WhyDepth(path)
   104  				if d > 0 && d < bestDepth {
   105  					best = path
   106  					bestDepth = d
   107  				}
   108  			}
   109  			why := modload.Why(best)
   110  			if why == "" {
   111  				vendoring := ""
   112  				if *whyVendor {
   113  					vendoring = " to vendor"
   114  				}
   115  				why = "(main module does not need" + vendoring + " module " + m.Path + ")\n"
   116  			}
   117  			fmt.Printf("%s# %s\n%s", sep, m.Path, why)
   118  			sep = "\n"
   119  		}
   120  	} else {
   121  		// Resolve to packages.
   122  		matches, _ := modload.LoadPackages(ctx, loadOpts, args...)
   123  
   124  		modload.LoadPackages(ctx, loadOpts, "all") // rebuild graph, from main module (not from named packages)
   125  
   126  		sep := ""
   127  		for _, m := range matches {
   128  			for _, path := range m.Pkgs {
   129  				why := modload.Why(path)
   130  				if why == "" {
   131  					vendoring := ""
   132  					if *whyVendor {
   133  						vendoring = " to vendor"
   134  					}
   135  					why = "(main module does not need" + vendoring + " package " + path + ")\n"
   136  				}
   137  				fmt.Printf("%s# %s\n%s", sep, path, why)
   138  				sep = "\n"
   139  			}
   140  		}
   141  	}
   142  }
   143  

View as plain text