Source file src/cmd/go/internal/modcmd/tidy.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  // go mod tidy
     6  
     7  package modcmd
     8  
     9  import (
    10  	"cmd/go/internal/base"
    11  	"cmd/go/internal/cfg"
    12  	"cmd/go/internal/gover"
    13  	"cmd/go/internal/imports"
    14  	"cmd/go/internal/modload"
    15  	"cmd/go/internal/toolchain"
    16  	"context"
    17  	"fmt"
    18  
    19  	"golang.org/x/mod/modfile"
    20  )
    21  
    22  var cmdTidy = &base.Command{
    23  	UsageLine: "go mod tidy [-e] [-v] [-x] [-diff] [-go=version] [-compat=version]",
    24  	Short:     "add missing and remove unused modules",
    25  	Long: `
    26  Tidy makes sure go.mod matches the source code in the module.
    27  It adds any missing modules necessary to build the current module's
    28  packages and dependencies, and it removes unused modules that
    29  don't provide any relevant packages. It also adds any missing entries
    30  to go.sum and removes any unnecessary ones.
    31  
    32  The -v flag causes tidy to print information about removed modules
    33  to standard error.
    34  
    35  The -e flag causes tidy to attempt to proceed despite errors
    36  encountered while loading packages.
    37  
    38  The -diff flag causes tidy not to modify go.mod or go.sum but
    39  instead print the necessary changes as a unified diff. It exits
    40  with a non-zero code if the diff is not empty.
    41  
    42  The -go flag causes tidy to update the 'go' directive in the go.mod
    43  file to the given version, which may change which module dependencies
    44  are retained as explicit requirements in the go.mod file.
    45  (Go versions 1.17 and higher retain more requirements in order to
    46  support lazy module loading.)
    47  
    48  The -compat flag preserves any additional checksums needed for the
    49  'go' command from the indicated major Go release to successfully load
    50  the module graph, and causes tidy to error out if that version of the
    51  'go' command would load any imported package from a different module
    52  version. By default, tidy acts as if the -compat flag were set to the
    53  version prior to the one indicated by the 'go' directive in the go.mod
    54  file.
    55  
    56  The -x flag causes tidy to print the commands download executes.
    57  
    58  See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
    59  	`,
    60  	Run: runTidy,
    61  }
    62  
    63  var (
    64  	tidyE      bool          // if true, report errors but proceed anyway.
    65  	tidyDiff   bool          // if true, do not update go.mod or go.sum and show changes. Return corresponding exit code.
    66  	tidyGo     goVersionFlag // go version to write to the tidied go.mod file (toggles lazy loading)
    67  	tidyCompat goVersionFlag // go version for which the tidied go.mod and go.sum files should be “compatible”
    68  )
    69  
    70  func init() {
    71  	cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
    72  	cmdTidy.Flag.BoolVar(&cfg.BuildX, "x", false, "")
    73  	cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
    74  	cmdTidy.Flag.BoolVar(&tidyDiff, "diff", false, "")
    75  	cmdTidy.Flag.Var(&tidyGo, "go", "")
    76  	cmdTidy.Flag.Var(&tidyCompat, "compat", "")
    77  	base.AddChdirFlag(&cmdTidy.Flag)
    78  	base.AddModCommonFlags(&cmdTidy.Flag)
    79  }
    80  
    81  // A goVersionFlag is a flag.Value representing a supported Go version.
    82  //
    83  // (Note that the -go argument to 'go mod edit' is *not* a goVersionFlag.
    84  // It intentionally allows newer-than-supported versions as arguments.)
    85  type goVersionFlag struct {
    86  	v string
    87  }
    88  
    89  func (f *goVersionFlag) String() string { return f.v }
    90  func (f *goVersionFlag) Get() any       { return f.v }
    91  
    92  func (f *goVersionFlag) Set(s string) error {
    93  	if s != "" {
    94  		latest := gover.Local()
    95  		if !modfile.GoVersionRE.MatchString(s) {
    96  			return fmt.Errorf("expecting a Go version like %q", latest)
    97  		}
    98  		if gover.Compare(s, latest) > 0 {
    99  			return fmt.Errorf("maximum supported Go version is %s", latest)
   100  		}
   101  	}
   102  
   103  	f.v = s
   104  	return nil
   105  }
   106  
   107  func runTidy(ctx context.Context, cmd *base.Command, args []string) {
   108  	moduleLoaderState := modload.NewState()
   109  	if len(args) > 0 {
   110  		base.Fatalf("go: 'go mod tidy' accepts no arguments")
   111  	}
   112  
   113  	// Tidy aims to make 'go test' reproducible for any package in 'all', so we
   114  	// need to include test dependencies. For modules that specify go 1.15 or
   115  	// earlier this is a no-op (because 'all' saturates transitive test
   116  	// dependencies).
   117  	//
   118  	// However, with lazy loading (go 1.16+) 'all' includes only the packages that
   119  	// are transitively imported by the main module, not the test dependencies of
   120  	// those packages. In order to make 'go test' reproducible for the packages
   121  	// that are in 'all' but outside of the main module, we must explicitly
   122  	// request that their test dependencies be included.
   123  	moduleLoaderState.ForceUseModules = true
   124  	moduleLoaderState.RootMode = modload.NeedRoot
   125  
   126  	goVersion := tidyGo.String()
   127  	if goVersion != "" && gover.Compare(gover.Local(), goVersion) < 0 {
   128  		toolchain.SwitchOrFatal(moduleLoaderState, ctx, &gover.TooNewError{
   129  			What:      "-go flag",
   130  			GoVersion: goVersion,
   131  		})
   132  	}
   133  
   134  	modload.LoadPackages(moduleLoaderState, ctx, modload.PackageOpts{
   135  		TidyGoVersion:            tidyGo.String(),
   136  		Tags:                     imports.AnyTags(),
   137  		Tidy:                     true,
   138  		TidyDiff:                 tidyDiff,
   139  		TidyCompatibleVersion:    tidyCompat.String(),
   140  		VendorModulesInGOROOTSrc: true,
   141  		ResolveMissingImports:    true,
   142  		LoadTests:                true,
   143  		AllowErrors:              tidyE,
   144  		SilenceMissingStdImports: true,
   145  		Switcher:                 toolchain.NewSwitcher(moduleLoaderState),
   146  	}, "all")
   147  }
   148  

View as plain text