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  		LoadTests:     !*whyVendor,
    73  		SilenceErrors: true,
    74  		UseVendorAll:  *whyVendor,
    75  	}
    76  
    77  	if *whyM {
    78  		listU := false
    79  		listVersions := false
    80  		listRetractions := false
    81  		for _, arg := range args {
    82  			if strings.Contains(arg, "@") {
    83  				base.Fatalf("go mod why: module query not allowed")
    84  			}
    85  		}
    86  		mods := modload.ListModules(ctx, args, listU, listVersions, listRetractions)
    87  		byModule := make(map[module.Version][]string)
    88  		_, pkgs := modload.LoadPackages(ctx, loadOpts, "all")
    89  		for _, path := range pkgs {
    90  			m := modload.PackageModule(path)
    91  			if m.Path != "" {
    92  				byModule[m] = append(byModule[m], path)
    93  			}
    94  		}
    95  		sep := ""
    96  		for _, m := range mods {
    97  			best := ""
    98  			bestDepth := 1000000000
    99  			for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] {
   100  				d := modload.WhyDepth(path)
   101  				if d > 0 && d < bestDepth {
   102  					best = path
   103  					bestDepth = d
   104  				}
   105  			}
   106  			why := modload.Why(best)
   107  			if why == "" {
   108  				vendoring := ""
   109  				if *whyVendor {
   110  					vendoring = " to vendor"
   111  				}
   112  				why = "(main module does not need" + vendoring + " module " + m.Path + ")\n"
   113  			}
   114  			fmt.Printf("%s# %s\n%s", sep, m.Path, why)
   115  			sep = "\n"
   116  		}
   117  	} else {
   118  		// Resolve to packages.
   119  		matches, _ := modload.LoadPackages(ctx, loadOpts, args...)
   120  
   121  		modload.LoadPackages(ctx, loadOpts, "all") // rebuild graph, from main module (not from named packages)
   122  
   123  		sep := ""
   124  		for _, m := range matches {
   125  			for _, path := range m.Pkgs {
   126  				why := modload.Why(path)
   127  				if why == "" {
   128  					vendoring := ""
   129  					if *whyVendor {
   130  						vendoring = " to vendor"
   131  					}
   132  					why = "(main module does not need" + vendoring + " package " + path + ")\n"
   133  				}
   134  				fmt.Printf("%s# %s\n%s", sep, path, why)
   135  				sep = "\n"
   136  			}
   137  		}
   138  	}
   139  }
   140  

View as plain text