github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/internal/modload/init.go (about)

     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 modload
     6  
     7  import (
     8  	"bytes"
     9  	"cmd/go/internal/base"
    10  	"cmd/go/internal/cache"
    11  	"cmd/go/internal/cfg"
    12  	"cmd/go/internal/load"
    13  	"cmd/go/internal/modconv"
    14  	"cmd/go/internal/modfetch"
    15  	"cmd/go/internal/modfetch/codehost"
    16  	"cmd/go/internal/modfile"
    17  	"cmd/go/internal/module"
    18  	"cmd/go/internal/mvs"
    19  	"cmd/go/internal/renameio"
    20  	"cmd/go/internal/search"
    21  	"encoding/json"
    22  	"fmt"
    23  	"go/build"
    24  	"io/ioutil"
    25  	"os"
    26  	"path"
    27  	"path/filepath"
    28  	"regexp"
    29  	"runtime/debug"
    30  	"strconv"
    31  	"strings"
    32  )
    33  
    34  var (
    35  	cwd            string // TODO(bcmills): Is this redundant with base.Cwd?
    36  	MustUseModules = mustUseModules()
    37  	initialized    bool
    38  
    39  	modRoot     string
    40  	modFile     *modfile.File
    41  	modFileData []byte
    42  	excluded    map[module.Version]bool
    43  	Target      module.Version
    44  
    45  	gopath string
    46  
    47  	CmdModInit   bool   // running 'go mod init'
    48  	CmdModModule string // module argument for 'go mod init'
    49  )
    50  
    51  // ModFile returns the parsed go.mod file.
    52  //
    53  // Note that after calling ImportPaths or LoadBuildList,
    54  // the require statements in the modfile.File are no longer
    55  // the source of truth and will be ignored: edits made directly
    56  // will be lost at the next call to WriteGoMod.
    57  // To make permanent changes to the require statements
    58  // in go.mod, edit it before calling ImportPaths or LoadBuildList.
    59  func ModFile() *modfile.File {
    60  	Init()
    61  	if modFile == nil {
    62  		die()
    63  	}
    64  	return modFile
    65  }
    66  
    67  func BinDir() string {
    68  	Init()
    69  	return filepath.Join(gopath, "bin")
    70  }
    71  
    72  // mustUseModules reports whether we are invoked as vgo
    73  // (as opposed to go).
    74  // If so, we only support builds with go.mod files.
    75  func mustUseModules() bool {
    76  	name := os.Args[0]
    77  	name = name[strings.LastIndex(name, "/")+1:]
    78  	name = name[strings.LastIndex(name, `\`)+1:]
    79  	return strings.HasPrefix(name, "vgo")
    80  }
    81  
    82  var inGOPATH bool // running in GOPATH/src
    83  
    84  // Init determines whether module mode is enabled, locates the root of the
    85  // current module (if any), sets environment variables for Git subprocesses, and
    86  // configures the cfg, codehost, load, modfetch, and search packages for use
    87  // with modules.
    88  func Init() {
    89  	if initialized {
    90  		return
    91  	}
    92  	initialized = true
    93  
    94  	env := os.Getenv("GO111MODULE")
    95  	switch env {
    96  	default:
    97  		base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
    98  	case "", "auto":
    99  		// leave MustUseModules alone
   100  	case "on":
   101  		MustUseModules = true
   102  	case "off":
   103  		if !MustUseModules {
   104  			return
   105  		}
   106  	}
   107  
   108  	// Disable any prompting for passwords by Git.
   109  	// Only has an effect for 2.3.0 or later, but avoiding
   110  	// the prompt in earlier versions is just too hard.
   111  	// If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
   112  	// prompting.
   113  	// See golang.org/issue/9341 and golang.org/issue/12706.
   114  	if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
   115  		os.Setenv("GIT_TERMINAL_PROMPT", "0")
   116  	}
   117  
   118  	// Disable any ssh connection pooling by Git.
   119  	// If a Git subprocess forks a child into the background to cache a new connection,
   120  	// that child keeps stdout/stderr open. After the Git subprocess exits,
   121  	// os /exec expects to be able to read from the stdout/stderr pipe
   122  	// until EOF to get all the data that the Git subprocess wrote before exiting.
   123  	// The EOF doesn't come until the child exits too, because the child
   124  	// is holding the write end of the pipe.
   125  	// This is unfortunate, but it has come up at least twice
   126  	// (see golang.org/issue/13453 and golang.org/issue/16104)
   127  	// and confuses users when it does.
   128  	// If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
   129  	// assume they know what they are doing and don't step on it.
   130  	// But default to turning off ControlMaster.
   131  	if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
   132  		os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
   133  	}
   134  
   135  	var err error
   136  	cwd, err = os.Getwd()
   137  	if err != nil {
   138  		base.Fatalf("go: %v", err)
   139  	}
   140  
   141  	inGOPATH = false
   142  	for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
   143  		if gopath == "" {
   144  			continue
   145  		}
   146  		if search.InDir(cwd, filepath.Join(gopath, "src")) != "" {
   147  			inGOPATH = true
   148  			break
   149  		}
   150  	}
   151  
   152  	if inGOPATH && !MustUseModules {
   153  		if CmdModInit {
   154  			die() // Don't init a module that we're just going to ignore.
   155  		}
   156  		// No automatic enabling in GOPATH.
   157  		if root, _ := FindModuleRoot(cwd, "", false); root != "" {
   158  			cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
   159  		}
   160  		return
   161  	}
   162  
   163  	if CmdModInit {
   164  		// Running 'go mod init': go.mod will be created in current directory.
   165  		modRoot = cwd
   166  	} else {
   167  		modRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
   168  		if modRoot == "" {
   169  			if !MustUseModules {
   170  				// GO111MODULE is 'auto' (or unset), and we can't find a module root.
   171  				// Stay in GOPATH mode.
   172  				return
   173  			}
   174  		} else if search.InDir(modRoot, os.TempDir()) == "." {
   175  			// If you create /tmp/go.mod for experimenting,
   176  			// then any tests that create work directories under /tmp
   177  			// will find it and get modules when they're not expecting them.
   178  			// It's a bit of a peculiar thing to disallow but quite mysterious
   179  			// when it happens. See golang.org/issue/26708.
   180  			modRoot = ""
   181  			fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
   182  		}
   183  	}
   184  
   185  	// We're in module mode. Install the hooks to make it work.
   186  
   187  	if c := cache.Default(); c == nil {
   188  		// With modules, there are no install locations for packages
   189  		// other than the build cache.
   190  		base.Fatalf("go: cannot use modules with build cache disabled")
   191  	}
   192  
   193  	list := filepath.SplitList(cfg.BuildContext.GOPATH)
   194  	if len(list) == 0 || list[0] == "" {
   195  		base.Fatalf("missing $GOPATH")
   196  	}
   197  	gopath = list[0]
   198  	if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
   199  		base.Fatalf("$GOPATH/go.mod exists but should not")
   200  	}
   201  
   202  	oldSrcMod := filepath.Join(list[0], "src/mod")
   203  	pkgMod := filepath.Join(list[0], "pkg/mod")
   204  	infoOld, errOld := os.Stat(oldSrcMod)
   205  	_, errMod := os.Stat(pkgMod)
   206  	if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) {
   207  		os.Rename(oldSrcMod, pkgMod)
   208  	}
   209  
   210  	modfetch.PkgMod = pkgMod
   211  	codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
   212  
   213  	cfg.ModulesEnabled = true
   214  	load.ModBinDir = BinDir
   215  	load.ModLookup = Lookup
   216  	load.ModPackageModuleInfo = PackageModuleInfo
   217  	load.ModImportPaths = ImportPaths
   218  	load.ModPackageBuildInfo = PackageBuildInfo
   219  	load.ModInfoProg = ModInfoProg
   220  	load.ModImportFromFiles = ImportFromFiles
   221  	load.ModDirImportPath = DirImportPath
   222  
   223  	if modRoot == "" {
   224  		// We're in module mode, but not inside a module.
   225  		//
   226  		// If the command is 'go get' or 'go list' and all of the args are in the
   227  		// same existing module, we could use that module's download directory in
   228  		// the module cache as the module root, applying any replacements and/or
   229  		// exclusions specified by that module. However, that would leave us in a
   230  		// strange state: we want 'go get' to be consistent with 'go list', and 'go
   231  		// list' should be able to operate on multiple modules. Moreover, the 'get'
   232  		// target might specify relative file paths (e.g. in the same repository) as
   233  		// replacements, and we would not be able to apply those anyway: we would
   234  		// need to either error out or ignore just those replacements, when a build
   235  		// from an empty module could proceed without error.
   236  		//
   237  		// Instead, we'll operate as though we're in some ephemeral external module,
   238  		// ignoring all replacements and exclusions uniformly.
   239  
   240  		// Normally we check sums using the go.sum file from the main module, but
   241  		// without a main module we do not have an authoritative go.sum file.
   242  		//
   243  		// TODO(bcmills): In Go 1.13, check sums when outside the main module.
   244  		//
   245  		// One possible approach is to merge the go.sum files from all of the
   246  		// modules we download: that doesn't protect us against bad top-level
   247  		// modules, but it at least ensures consistency for transitive dependencies.
   248  	} else {
   249  		modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
   250  		search.SetModRoot(modRoot)
   251  	}
   252  }
   253  
   254  func init() {
   255  	load.ModInit = Init
   256  
   257  	// Set modfetch.PkgMod unconditionally, so that go clean -modcache can run even without modules enabled.
   258  	if list := filepath.SplitList(cfg.BuildContext.GOPATH); len(list) > 0 && list[0] != "" {
   259  		modfetch.PkgMod = filepath.Join(list[0], "pkg/mod")
   260  	}
   261  }
   262  
   263  // Enabled reports whether modules are (or must be) enabled.
   264  // If modules are enabled but there is no main module, Enabled returns true
   265  // and then the first use of module information will call die
   266  // (usually through MustModRoot).
   267  func Enabled() bool {
   268  	Init()
   269  	return modRoot != "" || MustUseModules
   270  }
   271  
   272  // ModRoot returns the root of the main module.
   273  // It calls base.Fatalf if there is no main module.
   274  func ModRoot() string {
   275  	if !HasModRoot() {
   276  		die()
   277  	}
   278  	return modRoot
   279  }
   280  
   281  // HasModRoot reports whether a main module is present.
   282  // HasModRoot may return false even if Enabled returns true: for example, 'get'
   283  // does not require a main module.
   284  func HasModRoot() bool {
   285  	Init()
   286  	return modRoot != ""
   287  }
   288  
   289  // printStackInDie causes die to print a stack trace.
   290  //
   291  // It is enabled by the testgo tag, and helps to diagnose paths that
   292  // unexpectedly require a main module.
   293  var printStackInDie = false
   294  
   295  func die() {
   296  	if printStackInDie {
   297  		debug.PrintStack()
   298  	}
   299  	if os.Getenv("GO111MODULE") == "off" {
   300  		base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
   301  	}
   302  	if inGOPATH && !MustUseModules {
   303  		base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
   304  	}
   305  	base.Fatalf("go: cannot find main module; see 'go help modules'")
   306  }
   307  
   308  // InitMod sets Target and, if there is a main module, parses the initial build
   309  // list from its go.mod file, creating and populating that file if needed.
   310  func InitMod() {
   311  	if len(buildList) > 0 {
   312  		return
   313  	}
   314  
   315  	Init()
   316  	if modRoot == "" {
   317  		Target = module.Version{Path: "command-line-arguments"}
   318  		buildList = []module.Version{Target}
   319  		return
   320  	}
   321  
   322  	if CmdModInit {
   323  		// Running go mod init: do legacy module conversion
   324  		legacyModInit()
   325  		modFileToBuildList()
   326  		WriteGoMod()
   327  		return
   328  	}
   329  
   330  	gomod := filepath.Join(modRoot, "go.mod")
   331  	data, err := ioutil.ReadFile(gomod)
   332  	if err != nil {
   333  		if os.IsNotExist(err) {
   334  			legacyModInit()
   335  			modFileToBuildList()
   336  			WriteGoMod()
   337  			return
   338  		}
   339  		base.Fatalf("go: %v", err)
   340  	}
   341  
   342  	f, err := modfile.Parse(gomod, data, fixVersion)
   343  	if err != nil {
   344  		// Errors returned by modfile.Parse begin with file:line.
   345  		base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
   346  	}
   347  	modFile = f
   348  	modFileData = data
   349  
   350  	if len(f.Syntax.Stmt) == 0 || f.Module == nil {
   351  		// Empty mod file. Must add module path.
   352  		path, err := FindModulePath(modRoot)
   353  		if err != nil {
   354  			base.Fatalf("go: %v", err)
   355  		}
   356  		f.AddModuleStmt(path)
   357  	}
   358  
   359  	if len(f.Syntax.Stmt) == 1 && f.Module != nil {
   360  		// Entire file is just a module statement.
   361  		// Populate require if possible.
   362  		legacyModInit()
   363  	}
   364  
   365  	excluded = make(map[module.Version]bool)
   366  	for _, x := range f.Exclude {
   367  		excluded[x.Mod] = true
   368  	}
   369  	modFileToBuildList()
   370  	WriteGoMod()
   371  }
   372  
   373  // modFileToBuildList initializes buildList from the modFile.
   374  func modFileToBuildList() {
   375  	Target = modFile.Module.Mod
   376  	list := []module.Version{Target}
   377  	for _, r := range modFile.Require {
   378  		list = append(list, r.Mod)
   379  	}
   380  	buildList = list
   381  }
   382  
   383  // Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
   384  func Allowed(m module.Version) bool {
   385  	return !excluded[m]
   386  }
   387  
   388  func legacyModInit() {
   389  	if modFile == nil {
   390  		path, err := FindModulePath(modRoot)
   391  		if err != nil {
   392  			base.Fatalf("go: %v", err)
   393  		}
   394  		fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", path)
   395  		modFile = new(modfile.File)
   396  		modFile.AddModuleStmt(path)
   397  	}
   398  
   399  	addGoStmt()
   400  
   401  	for _, name := range altConfigs {
   402  		cfg := filepath.Join(modRoot, name)
   403  		data, err := ioutil.ReadFile(cfg)
   404  		if err == nil {
   405  			convert := modconv.Converters[name]
   406  			if convert == nil {
   407  				return
   408  			}
   409  			fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(cfg))
   410  			cfg = filepath.ToSlash(cfg)
   411  			if err := modconv.ConvertLegacyConfig(modFile, cfg, data); err != nil {
   412  				base.Fatalf("go: %v", err)
   413  			}
   414  			if len(modFile.Syntax.Stmt) == 1 {
   415  				// Add comment to avoid re-converting every time it runs.
   416  				modFile.AddComment("// go: no requirements found in " + name)
   417  			}
   418  			return
   419  		}
   420  	}
   421  }
   422  
   423  // InitGoStmt adds a go statement, unless there already is one.
   424  func InitGoStmt() {
   425  	if modFile.Go == nil {
   426  		addGoStmt()
   427  	}
   428  }
   429  
   430  // addGoStmt adds a go statement referring to the current version.
   431  func addGoStmt() {
   432  	tags := build.Default.ReleaseTags
   433  	version := tags[len(tags)-1]
   434  	if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
   435  		base.Fatalf("go: unrecognized default version %q", version)
   436  	}
   437  	if err := modFile.AddGoStmt(version[2:]); err != nil {
   438  		base.Fatalf("go: internal error: %v", err)
   439  	}
   440  }
   441  
   442  var altConfigs = []string{
   443  	"Gopkg.lock",
   444  
   445  	"GLOCKFILE",
   446  	"Godeps/Godeps.json",
   447  	"dependencies.tsv",
   448  	"glide.lock",
   449  	"vendor.conf",
   450  	"vendor.yml",
   451  	"vendor/manifest",
   452  	"vendor/vendor.json",
   453  
   454  	".git/config",
   455  }
   456  
   457  // Exported only for testing.
   458  func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) {
   459  	dir = filepath.Clean(dir)
   460  	dir1 := dir
   461  	limit = filepath.Clean(limit)
   462  
   463  	// Look for enclosing go.mod.
   464  	for {
   465  		if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
   466  			return dir, "go.mod"
   467  		}
   468  		if dir == limit {
   469  			break
   470  		}
   471  		d := filepath.Dir(dir)
   472  		if d == dir {
   473  			break
   474  		}
   475  		dir = d
   476  	}
   477  
   478  	// Failing that, look for enclosing alternate version config.
   479  	if legacyConfigOK {
   480  		dir = dir1
   481  		for {
   482  			for _, name := range altConfigs {
   483  				if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
   484  					return dir, name
   485  				}
   486  			}
   487  			if dir == limit {
   488  				break
   489  			}
   490  			d := filepath.Dir(dir)
   491  			if d == dir {
   492  				break
   493  			}
   494  			dir = d
   495  		}
   496  	}
   497  
   498  	return "", ""
   499  }
   500  
   501  // Exported only for testing.
   502  func FindModulePath(dir string) (string, error) {
   503  	if CmdModModule != "" {
   504  		// Running go mod init x/y/z; return x/y/z.
   505  		return CmdModModule, nil
   506  	}
   507  
   508  	// Cast about for import comments,
   509  	// first in top-level directory, then in subdirectories.
   510  	list, _ := ioutil.ReadDir(dir)
   511  	for _, info := range list {
   512  		if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
   513  			if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
   514  				return com, nil
   515  			}
   516  		}
   517  	}
   518  	for _, info1 := range list {
   519  		if info1.IsDir() {
   520  			files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name()))
   521  			for _, info2 := range files {
   522  				if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
   523  					if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
   524  						return path.Dir(com), nil
   525  					}
   526  				}
   527  			}
   528  		}
   529  	}
   530  
   531  	// Look for Godeps.json declaring import path.
   532  	data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
   533  	var cfg1 struct{ ImportPath string }
   534  	json.Unmarshal(data, &cfg1)
   535  	if cfg1.ImportPath != "" {
   536  		return cfg1.ImportPath, nil
   537  	}
   538  
   539  	// Look for vendor.json declaring import path.
   540  	data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
   541  	var cfg2 struct{ RootPath string }
   542  	json.Unmarshal(data, &cfg2)
   543  	if cfg2.RootPath != "" {
   544  		return cfg2.RootPath, nil
   545  	}
   546  
   547  	// Look for path in GOPATH.
   548  	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
   549  		if gpdir == "" {
   550  			continue
   551  		}
   552  		if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
   553  			return filepath.ToSlash(rel), nil
   554  		}
   555  	}
   556  
   557  	// Look for .git/config with github origin as last resort.
   558  	data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config"))
   559  	if m := gitOriginRE.FindSubmatch(data); m != nil {
   560  		return "github.com/" + string(m[1]), nil
   561  	}
   562  
   563  	return "", fmt.Errorf("cannot determine module path for source directory %s (outside GOPATH, no import comments)", dir)
   564  }
   565  
   566  var (
   567  	gitOriginRE     = regexp.MustCompile(`(?m)^\[remote "origin"\]\r?\n\turl = (?:https://github.com/|git@github.com:|gh:)([^/]+/[^/]+?)(\.git)?\r?\n`)
   568  	importCommentRE = regexp.MustCompile(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
   569  )
   570  
   571  func findImportComment(file string) string {
   572  	data, err := ioutil.ReadFile(file)
   573  	if err != nil {
   574  		return ""
   575  	}
   576  	m := importCommentRE.FindSubmatch(data)
   577  	if m == nil {
   578  		return ""
   579  	}
   580  	path, err := strconv.Unquote(string(m[1]))
   581  	if err != nil {
   582  		return ""
   583  	}
   584  	return path
   585  }
   586  
   587  var allowWriteGoMod = true
   588  
   589  // DisallowWriteGoMod causes future calls to WriteGoMod to do nothing at all.
   590  func DisallowWriteGoMod() {
   591  	allowWriteGoMod = false
   592  }
   593  
   594  // AllowWriteGoMod undoes the effect of DisallowWriteGoMod:
   595  // future calls to WriteGoMod will update go.mod if needed.
   596  // Note that any past calls have been discarded, so typically
   597  // a call to AlowWriteGoMod should be followed by a call to WriteGoMod.
   598  func AllowWriteGoMod() {
   599  	allowWriteGoMod = true
   600  }
   601  
   602  // MinReqs returns a Reqs with minimal dependencies of Target,
   603  // as will be written to go.mod.
   604  func MinReqs() mvs.Reqs {
   605  	var direct []string
   606  	for _, m := range buildList[1:] {
   607  		if loaded.direct[m.Path] {
   608  			direct = append(direct, m.Path)
   609  		}
   610  	}
   611  	min, err := mvs.Req(Target, buildList, direct, Reqs())
   612  	if err != nil {
   613  		base.Fatalf("go: %v", err)
   614  	}
   615  	return &mvsReqs{buildList: append([]module.Version{Target}, min...)}
   616  }
   617  
   618  // WriteGoMod writes the current build list back to go.mod.
   619  func WriteGoMod() {
   620  	// If we're using -mod=vendor we basically ignored
   621  	// go.mod, so definitely don't try to write back our
   622  	// incomplete view of the world.
   623  	if !allowWriteGoMod || cfg.BuildMod == "vendor" {
   624  		return
   625  	}
   626  
   627  	// If we aren't in a module, we don't have anywhere to write a go.mod file.
   628  	if modRoot == "" {
   629  		return
   630  	}
   631  
   632  	if loaded != nil {
   633  		reqs := MinReqs()
   634  		min, err := reqs.Required(Target)
   635  		if err != nil {
   636  			base.Fatalf("go: %v", err)
   637  		}
   638  		var list []*modfile.Require
   639  		for _, m := range min {
   640  			list = append(list, &modfile.Require{
   641  				Mod:      m,
   642  				Indirect: !loaded.direct[m.Path],
   643  			})
   644  		}
   645  		modFile.SetRequire(list)
   646  	}
   647  
   648  	modFile.Cleanup() // clean file after edits
   649  	new, err := modFile.Format()
   650  	if err != nil {
   651  		base.Fatalf("go: %v", err)
   652  	}
   653  
   654  	// Always update go.sum, even if we didn't change go.mod: we may have
   655  	// downloaded modules that we didn't have before.
   656  	modfetch.WriteGoSum()
   657  
   658  	if bytes.Equal(new, modFileData) {
   659  		// We don't need to modify go.mod from what we read previously.
   660  		// Ignore any intervening edits.
   661  		return
   662  	}
   663  	if cfg.BuildMod == "readonly" {
   664  		base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
   665  	}
   666  
   667  	unlock := modfetch.SideLock()
   668  	defer unlock()
   669  
   670  	file := filepath.Join(modRoot, "go.mod")
   671  	old, err := ioutil.ReadFile(file)
   672  	if !bytes.Equal(old, modFileData) {
   673  		if bytes.Equal(old, new) {
   674  			// Some other process wrote the same go.mod file that we were about to write.
   675  			modFileData = new
   676  			return
   677  		}
   678  		if err != nil {
   679  			base.Fatalf("go: can't determine whether go.mod has changed: %v", err)
   680  		}
   681  		// The contents of the go.mod file have changed. In theory we could add all
   682  		// of the new modules to the build list, recompute, and check whether any
   683  		// module in *our* build list got bumped to a different version, but that's
   684  		// a lot of work for marginal benefit. Instead, fail the command: if users
   685  		// want to run concurrent commands, they need to start with a complete,
   686  		// consistent module definition.
   687  		base.Fatalf("go: updates to go.mod needed, but contents have changed")
   688  
   689  	}
   690  
   691  	if err := renameio.WriteFile(file, new); err != nil {
   692  		base.Fatalf("error writing go.mod: %v", err)
   693  	}
   694  	modFileData = new
   695  }
   696  
   697  func fixVersion(path, vers string) (string, error) {
   698  	// Special case: remove the old -gopkgin- hack.
   699  	if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
   700  		vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
   701  	}
   702  
   703  	// fixVersion is called speculatively on every
   704  	// module, version pair from every go.mod file.
   705  	// Avoid the query if it looks OK.
   706  	_, pathMajor, ok := module.SplitPathVersion(path)
   707  	if !ok {
   708  		return "", fmt.Errorf("malformed module path: %s", path)
   709  	}
   710  	if vers != "" && module.CanonicalVersion(vers) == vers && module.MatchPathMajor(vers, pathMajor) {
   711  		return vers, nil
   712  	}
   713  
   714  	info, err := Query(path, vers, nil)
   715  	if err != nil {
   716  		return "", err
   717  	}
   718  	return info.Version, nil
   719  }