Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/go/internal/modcmd

     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  	"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 // break init cycle
    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  		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 mod why: module query not allowed")
    82  			}
    83  		}
    84  
    85  		mods, err := modload.ListModules(ctx, args, 0)
    86  		if err != nil {
    87  			base.Fatalf("go mod why: %v", err)
    88  		}
    89  
    90  		byModule := make(map[module.Version][]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] = append(byModule[m], path)
    96  			}
    97  		}
    98  		sep := ""
    99  		for _, m := range mods {
   100  			best := ""
   101  			bestDepth := 1000000000
   102  			for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] {
   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