...

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

View as plain text