src/cmd/go/internal/modload/init.go GO 2,318 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,318.
1// Copyright 2018 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// Package modload provides module and package loading functionality.6package modload78import (9	"bytes"10	"context"11	"errors"12	"fmt"13	"internal/godebugs"14	"internal/lazyregexp"15	"io"16	"maps"17	"os"18	"path"19	"path/filepath"20	"slices"21	"strconv"22	"strings"23	"sync"2425	"cmd/go/internal/base"26	"cmd/go/internal/cfg"27	"cmd/go/internal/fips140"28	"cmd/go/internal/fsys"29	"cmd/go/internal/gover"30	"cmd/go/internal/lockedfile"31	"cmd/go/internal/modfetch"32	"cmd/go/internal/search"3334	"golang.org/x/mod/modfile"35	"golang.org/x/mod/module"36)3738// Variables set by other packages.39//40// TODO(#40775): See if these can be plumbed as explicit parameters.41var (42	// ExplicitWriteGoMod prevents LoadPackages, ListModules, and other functions43	// from updating go.mod and go.sum or reporting errors when updates are44	// needed. A package should set this if it would cause go.mod to be written45	// multiple times (for example, 'go get' calls LoadPackages multiple times) or46	// if it needs some other operation to be successful before go.mod and go.sum47	// can be written (for example, 'go mod download' must download modules before48	// adding sums to go.sum). Packages that set this are responsible for calling49	// WriteGoMod explicitly.50	ExplicitWriteGoMod bool51)5253// Variables set in Init.54var (55	gopath string56)5758// NewForModroot creates a new module loader in single-module mode for the module at59// the given modroot..60func NewForModroot(ctx context.Context, modroot string) *Loader {61	ld := NewLoader()62	ld.modRoots = []string{modroot}63	LoadModFile(ld, ctx)64	return ld65}6667// NewForWorkspace creates a new loader for workspace mode from the given module mode loader ld,68// applying ld's updated requirements to the main module to the corresponding module in the workspace.69func (ld *Loader) NewForWorkspace(ctx context.Context) (*Loader, error) {70	// Find the identity of the main module that will be updated before we reset modload state.71	mm := ld.MainModules.mustGetSingleMainModule(ld)72	// Get the updated modfile we will use for that module.73	_, _, updatedmodfile, err := UpdateGoModFromReqs(ld, ctx, WriteOpts{})74	if err != nil {75		return nil, err76	}7778	// Create a new loader in workspace mode79	ld = NewLoader()80	ld.ForceUseModules = true8182	// Load in workspace mode.83	ld.InitWorkfile()84	LoadModFile(ld, ctx)8586	// Update the content of the previous main module, and recompute the requirements.87	*ld.MainModules.ModFile(mm) = *updatedmodfile88	ld.requirements = requirementsFromModFiles(ld, ld.MainModules.workFile, slices.Collect(maps.Values(ld.MainModules.modFiles)))8990	return ld, err91}9293type MainModuleSet struct {94	// versions are the module.Version values of each of the main modules.95	// For each of them, the Path fields are ordinary module paths and the Version96	// fields are empty strings.97	// versions is clipped (len=cap).98	versions []module.Version99100	// modRoot maps each module in versions to its absolute filesystem path.101	modRoot map[module.Version]string102103	// pathPrefix is the path prefix for packages in the module, without a trailing104	// slash. For most modules, pathPrefix is just version.Path, but the105	// standard-library module "std" has an empty prefix.106	pathPrefix map[module.Version]string107108	// inGorootSrc caches whether modRoot is within GOROOT/src.109	// The "std" module is special within GOROOT/src, but not otherwise.110	inGorootSrc map[module.Version]bool111112	modFiles map[module.Version]*modfile.File113114	tools map[string]bool115116	modContainingCWD module.Version117118	workFile *modfile.WorkFile119120	workFileReplaceMap map[module.Version]module.Version121	// highest replaced version of each module path; empty string for wildcard-only replacements122	highestReplaced map[string]string123124	indexMu sync.RWMutex125	indices map[module.Version]*modFileIndex126}127128func (mms *MainModuleSet) PathPrefix(m module.Version) string {129	return mms.pathPrefix[m]130}131132// Versions returns the module.Version values of each of the main modules.133// For each of them, the Path fields are ordinary module paths and the Version134// fields are empty strings.135// Callers should not modify the returned slice.136func (mms *MainModuleSet) Versions() []module.Version {137	if mms == nil {138		return nil139	}140	return mms.versions141}142143// Tools returns the tools defined by all the main modules.144// The key is the absolute package path of the tool.145func (mms *MainModuleSet) Tools() map[string]bool {146	if mms == nil {147		return nil148	}149	return mms.tools150}151152func (mms *MainModuleSet) Contains(path string) bool {153	if mms == nil {154		return false155	}156	for _, v := range mms.versions {157		if v.Path == path {158			return true159		}160	}161	return false162}163164func (mms *MainModuleSet) ModRoot(m module.Version) string {165	if mms == nil {166		return ""167	}168	return mms.modRoot[m]169}170171func (mms *MainModuleSet) InGorootSrc(m module.Version) bool {172	if mms == nil {173		return false174	}175	return mms.inGorootSrc[m]176}177178func (mms *MainModuleSet) mustGetSingleMainModule(ld *Loader) module.Version {179	mm, err := mms.getSingleMainModule(ld)180	if err != nil {181		panic(err)182	}183	return mm184}185186func (mms *MainModuleSet) getSingleMainModule(ld *Loader) (module.Version, error) {187	if mms == nil || len(mms.versions) == 0 {188		return module.Version{}, errors.New("internal error: mustGetSingleMainModule called in context with no main modules")189	}190	if len(mms.versions) != 1 {191		if ld.inWorkspaceMode() {192			return module.Version{}, errors.New("internal error: mustGetSingleMainModule called in workspace mode")193		} else {194			return module.Version{}, errors.New("internal error: multiple main modules present outside of workspace mode")195		}196	}197	return mms.versions[0], nil198}199200func (mms *MainModuleSet) GetSingleIndexOrNil(ld *Loader) *modFileIndex {201	if mms == nil {202		return nil203	}204	if len(mms.versions) == 0 {205		return nil206	}207	return mms.indices[mms.mustGetSingleMainModule(ld)]208}209210func (mms *MainModuleSet) Index(m module.Version) *modFileIndex {211	mms.indexMu.RLock()212	defer mms.indexMu.RUnlock()213	return mms.indices[m]214}215216func (mms *MainModuleSet) SetIndex(m module.Version, index *modFileIndex) {217	mms.indexMu.Lock()218	defer mms.indexMu.Unlock()219	mms.indices[m] = index220}221222func (mms *MainModuleSet) ModFile(m module.Version) *modfile.File {223	return mms.modFiles[m]224}225226func (mms *MainModuleSet) WorkFile() *modfile.WorkFile {227	return mms.workFile228}229230func (mms *MainModuleSet) Len() int {231	if mms == nil {232		return 0233	}234	return len(mms.versions)235}236237// ModContainingCWD returns the main module containing the working directory,238// or module.Version{} if none of the main modules contain the working239// directory.240func (mms *MainModuleSet) ModContainingCWD() module.Version {241	return mms.modContainingCWD242}243244func (mms *MainModuleSet) HighestReplaced() map[string]string {245	return mms.highestReplaced246}247248// GoVersion returns the go version set on the single module, in module mode,249// or the go.work file in workspace mode.250func (mms *MainModuleSet) GoVersion(ld *Loader) string {251	if ld.inWorkspaceMode() {252		return gover.FromGoWork(mms.workFile)253	}254	if mms != nil && len(mms.versions) == 1 {255		f := mms.ModFile(mms.mustGetSingleMainModule(ld))256		if f == nil {257			// Special case: we are outside a module, like 'go run x.go'.258			// Assume the local Go version.259			// TODO(#49228): Clean this up; see loadModFile.260			return gover.Local()261		}262		return gover.FromGoMod(f)263	}264	return gover.DefaultGoModVersion265}266267// Godebugs returns the godebug lines set on the single module, in module mode,268// or on the go.work file in workspace mode.269// The caller must not modify the result.270func (mms *MainModuleSet) Godebugs(ld *Loader) []*modfile.Godebug {271	if ld.inWorkspaceMode() {272		if mms.workFile != nil {273			return mms.workFile.Godebug274		}275		return nil276	}277	if mms != nil && len(mms.versions) == 1 {278		f := mms.ModFile(mms.mustGetSingleMainModule(ld))279		if f == nil {280			// Special case: we are outside a module, like 'go run x.go'.281			return nil282		}283		return f.Godebug284	}285	return nil286}287288func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version {289	return mms.workFileReplaceMap290}291292type Root int293294const (295	// AutoRoot is the default for most commands. modload.Init will look for296	// a go.mod file in the current directory or any parent. If none is found,297	// modules may be disabled (GO111MODULE=auto) or commands may run in a298	// limited module mode.299	AutoRoot Root = iota300301	// NoRoot is used for commands that run in module mode and ignore any go.mod302	// file the current directory or in parent directories.303	NoRoot304305	// NeedRoot is used for commands that must run in module mode and don't306	// make sense without a main module.307	NeedRoot308)309310// ModFile returns the parsed go.mod file.311//312// Note that after calling LoadPackages or LoadModGraph,313// the require statements in the modfile.File are no longer314// the source of truth and will be ignored: edits made directly315// will be lost at the next call to WriteGoMod.316// To make permanent changes to the require statements317// in go.mod, edit it before loading.318func ModFile(ld *Loader) *modfile.File {319	Init(ld)320	modFile := ld.MainModules.ModFile(ld.MainModules.mustGetSingleMainModule(ld))321	if modFile == nil {322		die(ld)323	}324	return modFile325}326327func BinDir(ld *Loader) string {328	Init(ld)329	if cfg.GOBIN != "" {330		return cfg.GOBIN331	}332	if gopath == "" {333		return ""334	}335	return filepath.Join(gopath, "bin")336}337338// InitWorkfile initializes the workFilePath variable for commands that339// operate in workspace mode. It should not be called by other commands,340// for example 'go mod tidy', that don't operate in workspace mode.341func (ld *Loader) InitWorkfile() {342	// Initialize fsys early because we need overlay to read go.work file.343	fips140.Init()344	if err := fsys.Init(); err != nil {345		base.Fatal(err)346	}347	ld.workFilePath = ld.FindGoWork(base.Cwd())348}349350// FindGoWork returns the name of the go.work file for this command,351// or the empty string if there isn't one.352// Most code should use Init and Enabled rather than use this directly.353// It is exported mainly for Go toolchain switching, which must process354// the go.work very early at startup.355func (ld *Loader) FindGoWork(wd string) string {356	if ld.RootMode == NoRoot {357		return ""358	}359360	switch gowork := cfg.Getenv("GOWORK"); gowork {361	case "off":362		return ""363	case "", "auto":364		return findWorkspaceFile(wd)365	default:366		if !filepath.IsAbs(gowork) {367			base.Fatalf("go: invalid GOWORK: not an absolute path")368		}369		return gowork370	}371}372373// WorkFilePath returns the absolute path of the go.work file, or "" if not in374// workspace mode. WorkFilePath must be called after InitWorkfile.375func WorkFilePath(ld *Loader) string {376	return ld.workFilePath377}378379// Reset clears all the initialized, cached state about the use of modules,380// so that we can start over.381func (ld *Loader) Reset() {382	ld.setState(NewLoader())383}384385func (ld *Loader) setState(new *Loader) (old *Loader) {386	old = &Loader{387		initialized:     ld.initialized,388		ForceUseModules: ld.ForceUseModules,389		RootMode:        ld.RootMode,390		modRoots:        ld.modRoots,391		modulesEnabled:  cfg.ModulesEnabled,392		MainModules:     ld.MainModules,393		requirements:    ld.requirements,394		workFilePath:    ld.workFilePath,395		fetcher:         ld.fetcher,396		packageCache:    ld.packageCache,397	}398	ld.initialized = new.initialized399	ld.ForceUseModules = new.ForceUseModules400	ld.RootMode = new.RootMode401	ld.modRoots = new.modRoots402	cfg.ModulesEnabled = new.modulesEnabled403	ld.MainModules = new.MainModules404	ld.requirements = new.requirements405	ld.workFilePath = new.workFilePath406	// The modfetch package's global state is used to compute407	// the go.sum file, so save and restore it along with the408	// modload state.409	old.fetcher = ld.fetcher.SetState(new.fetcher)410	ld.packageCache = new.packageCache411412	return old413}414415type Loader struct {416	initialized               bool417	allowMissingModuleImports bool418419	// ForceUseModules may be set to force modules to be enabled when420	// GO111MODULE=auto or to report an error when GO111MODULE=off.421	ForceUseModules bool422423	// RootMode determines whether a module root is needed.424	RootMode Root425426	// These are primarily used to initialize the MainModules, and should427	// be eventually superseded by them but are still used in cases where428	// the module roots are required but MainModules has not been429	// initialized yet. Set to the modRoots of the main modules.430	// modRoots != nil implies len(modRoots) > 0431	modRoots       []string432	modulesEnabled bool433	MainModules    *MainModuleSet434435	// pkgLoader is the most recently-used package loader.436	// It holds details about individual packages.437	//438	// This variable should only be accessed directly in top-level exported439	// functions. All other functions that require or produce a *packageLoader should pass440	// or return it as an explicit parameter.441	pkgLoader *packageLoader442443	// requirements is the requirement graph for the main module.444	//445	// It is always non-nil if the main module's go.mod file has been446	// loaded.447	//448	// This variable should only be read from the loadModFile449	// function, and should only be written in the loadModFile and450	// commitRequirements functions.  All other functions that need or451	// produce a *Requirements should accept and/or return an explicit452	// parameter.453	requirements *Requirements454455	// Set to the path to the go.work file, or "" if workspace mode is456	// disabled457	workFilePath string458	fetcher      *modfetch.Fetcher459460	// PackageCache is a lookup cache for LoadImport,461	// so that if we look up a package multiple times462	// we return the same pointer each time.463	packageCache map[string]any464}465466func NewLoader() *Loader {467	s := new(Loader)468	s.fetcher = modfetch.NewFetcher()469	s.packageCache = make(map[string]any)470	return s471}472473func NewDisabledState() *Loader {474	fips140.Init()475	return &Loader{initialized: true, modulesEnabled: false, packageCache: make(map[string]any)}476}477478func (ld *Loader) Fetcher() *modfetch.Fetcher {479	return ld.fetcher480}481482func (ld *Loader) PackageCache() map[string]any { return ld.packageCache }483484// Init determines whether module mode is enabled, locates the root of the485// current module (if any), sets environment variables for Git subprocesses, and486// configures the cfg, codehost, load, modfetch, and search packages for use487// with modules.488func Init(ld *Loader) {489	if ld.initialized {490		return491	}492	ld.initialized = true493494	fips140.Init()495496	// Keep in sync with WillBeEnabled. We perform extra validation here, and497	// there are lots of diagnostics and side effects, so we can't use498	// WillBeEnabled directly.499	var mustUseModules bool500	env := cfg.Getenv("GO111MODULE")501	switch env {502	default:503		base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)504	case "auto":505		mustUseModules = ld.ForceUseModules506	case "on", "":507		mustUseModules = true508	case "off":509		if ld.ForceUseModules {510			base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")511		}512		mustUseModules = false513		return514	}515516	if err := fsys.Init(); err != nil {517		base.Fatal(err)518	}519520	// Disable any prompting for passwords by Git.521	// Only has an effect for 2.3.0 or later, but avoiding522	// the prompt in earlier versions is just too hard.523	// If user has explicitly set GIT_TERMINAL_PROMPT=1, keep524	// prompting.525	// See golang.org/issue/9341 and golang.org/issue/12706.526	if os.Getenv("GIT_TERMINAL_PROMPT") == "" {527		os.Setenv("GIT_TERMINAL_PROMPT", "0")528	}529530	if os.Getenv("GCM_INTERACTIVE") == "" {531		os.Setenv("GCM_INTERACTIVE", "never")532	}533	if ld.modRoots != nil {534		// modRoot set before Init was called ("go mod init" does this).535		// No need to search for go.mod.536	} else if ld.RootMode == NoRoot {537		if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {538			base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")539		}540		ld.modRoots = nil541	} else if ld.workFilePath != "" {542		// We're in workspace mode, which implies module mode.543		if cfg.ModFile != "" {544			base.Fatalf("go: -modfile cannot be used in workspace mode")545		}546	} else {547		if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {548			if cfg.ModFile != "" {549				base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")550			}551			if ld.RootMode == NeedRoot {552				base.Fatal(NewNoMainModulesError(ld))553			}554			if !mustUseModules {555				// GO111MODULE is 'auto', and we can't find a module root.556				// Stay in GOPATH mode.557				return558			}559		} else if search.InDir(modRoot, os.TempDir()) == "." {560			// If you create /tmp/go.mod for experimenting,561			// then any tests that create work directories under /tmp562			// will find it and get modules when they're not expecting them.563			// It's a bit of a peculiar thing to disallow but quite mysterious564			// when it happens. See golang.org/issue/26708.565			fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())566			if ld.RootMode == NeedRoot {567				base.Fatal(NewNoMainModulesError(ld))568			}569			if !mustUseModules {570				return571			}572		} else {573			ld.modRoots = []string{modRoot}574		}575	}576	if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {577		base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)578	}579580	// We're in module mode. Set any global variables that need to be set.581	cfg.ModulesEnabled = true582	setDefaultBuildMod(ld)583	list := filepath.SplitList(cfg.BuildContext.GOPATH)584	if len(list) > 0 && list[0] != "" {585		gopath = list[0]586		if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {587			fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in $GOPATH %v\n", gopath)588			if ld.RootMode == NeedRoot {589				base.Fatal(NewNoMainModulesError(ld))590			}591			if !mustUseModules {592				return593			}594		}595	}596}597598// WillBeEnabled checks whether modules should be enabled but does not599// initialize modules by installing hooks. If Init has already been called,600// WillBeEnabled returns the same result as Enabled.601//602// This function is needed to break a cycle. The main package needs to know603// whether modules are enabled in order to install the module or GOPATH version604// of 'go get', but Init reads the -modfile flag in 'go get', so it shouldn't605// be called until the command is installed and flags are parsed. Instead of606// calling Init and Enabled, the main package can call this function.607func (ld *Loader) WillBeEnabled() bool {608	if ld.modRoots != nil || cfg.ModulesEnabled {609		// Already enabled.610		return true611	}612	if ld.initialized {613		// Initialized, not enabled.614		return false615	}616617	// Keep in sync with Init. Init does extra validation and prints warnings or618	// exits, so it can't call this function directly.619	env := cfg.Getenv("GO111MODULE")620	switch env {621	case "on", "":622		return true623	case "auto":624		break625	default:626		return false627	}628629	return FindGoMod(base.Cwd()) != "" || ld.FindGoWork(base.Cwd()) != ""630}631632// FindGoMod returns the name of the go.mod file for this command,633// or the empty string if there isn't one.634// Most code should use Init and Enabled rather than use this directly.635// It is exported mainly for Go toolchain switching, which must process636// the go.mod very early at startup.637func FindGoMod(wd string) string {638	modRoot := findModuleRoot(wd)639	if modRoot == "" {640		// GO111MODULE is 'auto', and we can't find a module root.641		// Stay in GOPATH mode.642		return ""643	}644	if search.InDir(modRoot, os.TempDir()) == "." {645		// If you create /tmp/go.mod for experimenting,646		// then any tests that create work directories under /tmp647		// will find it and get modules when they're not expecting them.648		// It's a bit of a peculiar thing to disallow but quite mysterious649		// when it happens. See golang.org/issue/26708.650		return ""651	}652	return filepath.Join(modRoot, "go.mod")653}654655// Enabled reports whether modules are (or must be) enabled.656// If modules are enabled but there is no main module, Enabled returns true657// and then the first use of module information will call die658// (usually through MustModRoot).659func (ld *Loader) Enabled() bool {660	Init(ld)661	return ld.modRoots != nil || cfg.ModulesEnabled662}663664func (ld *Loader) vendorDir() (string, error) {665	if ld.inWorkspaceMode() {666		return filepath.Join(filepath.Dir(WorkFilePath(ld)), "vendor"), nil667	}668	mainModule, err := ld.MainModules.getSingleMainModule(ld)669	if err != nil {670		return "", err671	}672	// Even if -mod=vendor, we could be operating with no mod root (and thus no673	// vendor directory). As long as there are no dependencies that is expected674	// to work. See script/vendor_outside_module.txt.675	modRoot := ld.MainModules.ModRoot(mainModule)676	if modRoot == "" {677		return "", errors.New("vendor directory does not exist when in single module mode outside of a module")678	}679	return filepath.Join(modRoot, "vendor"), nil680}681682func (ld *Loader) VendorDirOrEmpty() string {683	dir, err := ld.vendorDir()684	if err != nil {685		return ""686	}687	return dir688}689690func VendorDir(ld *Loader) string {691	dir, err := ld.vendorDir()692	if err != nil {693		panic(err)694	}695	return dir696}697698func (ld *Loader) inWorkspaceMode() bool {699	if !ld.initialized {700		panic("inWorkspaceMode called before modload.Init called")701	}702	if !ld.Enabled() {703		return false704	}705	return ld.workFilePath != ""706}707708// HasModRoot reports whether a main module or main modules are present.709// HasModRoot may return false even if Enabled returns true: for example, 'get'710// does not require a main module.711func (ld *Loader) HasModRoot() bool {712	Init(ld)713	return ld.modRoots != nil714}715716// MustHaveModRoot checks that a main module or main modules are present,717// and calls base.Fatalf if there are no main modules.718func (ld *Loader) MustHaveModRoot() {719	Init(ld)720	if !ld.HasModRoot() {721		die(ld)722	}723}724725// ModFilePath returns the path that would be used for the go.mod726// file, if in module mode. ModFilePath calls base.Fatalf if there is no main727// module, even if -modfile is set.728func (ld *Loader) ModFilePath() string {729	ld.MustHaveModRoot()730	return modFilePath(findModuleRoot(base.Cwd()))731}732733func modFilePath(modRoot string) string {734	// TODO(matloob): This seems incompatible with workspaces735	// (unless the user's intention is to replace all workspace modules' modfiles?).736	// Should we produce an error in workspace mode if cfg.ModFile is set?737	if cfg.ModFile != "" {738		return cfg.ModFile739	}740	return filepath.Join(modRoot, "go.mod")741}742743func die(ld *Loader) {744	if cfg.Getenv("GO111MODULE") == "off" {745		base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")746	}747	if !ld.inWorkspaceMode() {748		if dir, name := findAltConfig(base.Cwd()); dir != "" {749			rel, err := filepath.Rel(base.Cwd(), dir)750			if err != nil {751				rel = dir752			}753			cdCmd := ""754			if rel != "." {755				cdCmd = fmt.Sprintf("cd %s && ", rel)756			}757			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)758		}759	}760	base.Fatal(NewNoMainModulesError(ld))761}762763var ErrNoModRoot = errors.New("no module root")764765// noMainModulesError returns the appropriate error if there is no main module or766// main modules depending on whether the go command is in workspace mode.767type noMainModulesError struct {768	inWorkspaceMode bool769}770771func (e noMainModulesError) Error() string {772	if e.inWorkspaceMode {773		return "no modules were found in the current workspace; see 'go help work'"774	}775	return "go.mod file not found in current directory or any parent directory; see 'go help modules'"776}777778func (e noMainModulesError) Unwrap() error {779	return ErrNoModRoot780}781782func NewNoMainModulesError(ld *Loader) noMainModulesError {783	return noMainModulesError{784		inWorkspaceMode: ld.inWorkspaceMode(),785	}786}787788type goModDirtyError struct{}789790func (goModDirtyError) Error() string {791	if cfg.BuildModExplicit {792		return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%v; to update it:\n\tgo mod tidy", cfg.BuildMod)793	}794	if cfg.BuildModReason != "" {795		return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%s\n\t(%s)\n\tto update it:\n\tgo mod tidy", cfg.BuildMod, cfg.BuildModReason)796	}797	return "updates to go.mod needed; to update it:\n\tgo mod tidy"798}799800var errGoModDirty error = goModDirtyError{}801802// LoadWorkFile parses and checks the go.work file at the given path,803// and returns the absolute paths of the workspace modules' modroots.804// It does not modify the global state of the modload package.805func LoadWorkFile(path string) (workFile *modfile.WorkFile, modRoots []string, err error) {806	workDir := filepath.Dir(path)807	wf, err := ReadWorkFile(path)808	if err != nil {809		return nil, nil, err810	}811	seen := map[string]bool{}812	for _, d := range wf.Use {813		modRoot := d.Path814		if !filepath.IsAbs(modRoot) {815			modRoot = filepath.Join(workDir, modRoot)816		}817818		if seen[modRoot] {819			return nil, nil, fmt.Errorf("error loading go.work:\n%s:%d: path %s appears multiple times in workspace", base.ShortPath(path), d.Syntax.Start.Line, modRoot)820		}821		seen[modRoot] = true822		modRoots = append(modRoots, modRoot)823	}824825	for _, g := range wf.Godebug {826		if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {827			return nil, nil, fmt.Errorf("error loading go.work:\n%s:%d: %w", base.ShortPath(path), g.Syntax.Start.Line, err)828		}829	}830831	return wf, modRoots, nil832}833834// ReadWorkFile reads and parses the go.work file at the given path.835func ReadWorkFile(path string) (*modfile.WorkFile, error) {836	path = base.ShortPath(path) // use short path in any errors837	workData, err := fsys.ReadFile(path)838	if err != nil {839		return nil, fmt.Errorf("reading go.work: %w", err)840	}841842	f, err := modfile.ParseWork(path, workData, nil)843	if err != nil {844		return nil, fmt.Errorf("errors parsing go.work:\n%w", err)845	}846	if f.Go != nil && gover.Compare(f.Go.Version, gover.Local()) > 0 && cfg.CmdName != "work edit" {847		base.Fatal(&gover.TooNewError{What: base.ShortPath(path), GoVersion: f.Go.Version})848	}849	return f, nil850}851852// WriteWorkFile cleans and writes out the go.work file to the given path.853func WriteWorkFile(path string, wf *modfile.WorkFile) error {854	wf.SortBlocks()855	wf.Cleanup()856	out := modfile.Format(wf.Syntax)857858	return os.WriteFile(path, out, 0o666)859}860861// UpdateWorkGoVersion updates the go line in wf to be at least goVers,862// reporting whether it changed the file.863func UpdateWorkGoVersion(wf *modfile.WorkFile, goVers string) (changed bool) {864	old := gover.FromGoWork(wf)865	if gover.Compare(old, goVers) >= 0 {866		return false867	}868869	wf.AddGoStmt(goVers)870871	if wf.Toolchain == nil {872		return true873	}874875	// Drop the toolchain line if it is implied by the go line,876	// if its version is older than the version in the go line,877	// or if it is asking for a toolchain older than Go 1.21,878	// which will not understand the toolchain line.879	// Previously, a toolchain line set to the local toolchain880	// version was added so that future operations on the go file881	// would use the same toolchain logic for reproducibility.882	// This behavior seemed to cause user confusion without much883	// benefit so it was removed. See #65847.884	toolchain := wf.Toolchain.Name885	toolVers := gover.FromToolchain(toolchain)886	if toolchain == "go"+goVers || gover.Compare(toolVers, goVers) < 0 || gover.Compare(toolVers, gover.GoStrictVersion) < 0 {887		wf.DropToolchainStmt()888	}889890	return true891}892893// UpdateWorkFile updates comments on directory directives in the go.work894// file to include the associated module path.895func UpdateWorkFile(wf *modfile.WorkFile) {896	missingModulePaths := map[string]string{} // module directory listed in file -> abspath modroot897898	for _, d := range wf.Use {899		if d.Path == "" {900			continue // d is marked for deletion.901		}902		modRoot := d.Path903		if d.ModulePath == "" {904			missingModulePaths[d.Path] = modRoot905		}906	}907908	// Clean up and annotate directories.909	// TODO(matloob): update x/mod to actually add module paths.910	for moddir, absmodroot := range missingModulePaths {911		_, f, err := ReadModFile(filepath.Join(absmodroot, "go.mod"), nil)912		if err != nil {913			continue // Error will be reported if modules are loaded.914		}915		wf.AddUse(moddir, f.Module.Mod.Path)916	}917}918919// LoadModFile sets Target and, if there is a main module, parses the initial920// build list from its go.mod file.921//922// LoadModFile may make changes in memory, like adding a go directive and923// ensuring requirements are consistent. The caller is responsible for ensuring924// those changes are written to disk by calling LoadPackages or ListModules925// (unless ExplicitWriteGoMod is set) or by calling WriteGoMod directly.926//927// As a side-effect, LoadModFile may change cfg.BuildMod to "vendor" if928// -mod wasn't set explicitly and automatic vendoring should be enabled.929//930// If LoadModFile or CreateModFile has already been called, LoadModFile returns931// the existing in-memory requirements (rather than re-reading them from disk).932//933// LoadModFile checks the roots of the module graph for consistency with each934// other, but unlike LoadModGraph does not load the full module graph or check935// it for global consistency. Most callers outside of the modload package should936// use LoadModGraph instead.937func LoadModFile(ld *Loader, ctx context.Context) *Requirements {938	rs, err := loadModFile(ld, ctx, nil)939	if err != nil {940		base.Fatal(err)941	}942	return rs943}944945func loadModFile(ld *Loader, ctx context.Context, opts *PackageOpts) (*Requirements, error) {946	if ld.requirements != nil {947		return ld.requirements, nil948	}949950	Init(ld)951	var workFile *modfile.WorkFile952	if ld.inWorkspaceMode() {953		var err error954		workFile, ld.modRoots, err = LoadWorkFile(ld.workFilePath)955		if err != nil {956			return nil, err957		}958		for _, modRoot := range ld.modRoots {959			sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum"960			ld.Fetcher().AddWorkspaceGoSumFile(sumFile)961		}962		ld.Fetcher().SetGoSumFile(ld.workFilePath + ".sum")963	} else if len(ld.modRoots) == 0 {964		// We're in module mode, but not inside a module.965		//966		// Commands like 'go build', 'go run', 'go list' have no go.mod file to967		// read or write. They would need to find and download the latest versions968		// of a potentially large number of modules with no way to save version969		// information. We can succeed slowly (but not reproducibly), but that's970		// not usually a good experience.971		//972		// Instead, we forbid resolving import paths to modules other than std and973		// cmd. Users may still build packages specified with .go files on the974		// command line, but they'll see an error if those files import anything975		// outside std.976		//977		// This can be overridden by calling AllowMissingModuleImports.978		// For example, 'go get' does this, since it is expected to resolve paths.979		//980		// See golang.org/issue/32027.981	} else {982		ld.Fetcher().SetGoSumFile(strings.TrimSuffix(modFilePath(ld.modRoots[0]), ".mod") + ".sum")983	}984	if len(ld.modRoots) == 0 {985		// TODO(#49228): Instead of creating a fake module with an empty modroot,986		// make MainModules.Len() == 0 mean that we're in module mode but not inside987		// any module.988		mainModule := module.Version{Path: "command-line-arguments"}989		ld.MainModules = makeMainModules(ld, []module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, nil)990		var (991			goVersion string992			pruning   modPruning993			roots     []module.Version994			direct    = map[string]bool{"go": true}995		)996		if ld.inWorkspaceMode() {997			// Since we are in a workspace, the Go version for the synthetic998			// "command-line-arguments" module must not exceed the Go version999			// for the workspace.1000			goVersion = ld.MainModules.GoVersion(ld)1001			pruning = workspace1002			roots = []module.Version{1003				mainModule,1004				{Path: "go", Version: goVersion},1005				{Path: "toolchain", Version: gover.LocalToolchain()},1006			}1007		} else {1008			goVersion = gover.Local()1009			pruning = pruningForGoVersion(goVersion)1010			roots = []module.Version{1011				{Path: "go", Version: goVersion},1012				{Path: "toolchain", Version: gover.LocalToolchain()},1013			}1014		}1015		rawGoVersion.Store(mainModule, goVersion)1016		ld.requirements = newRequirements(ld, pruning, roots, direct)1017		if cfg.BuildMod == "vendor" {1018			// For issue 56536: Some users may have GOFLAGS=-mod=vendor set.1019			// Make sure it behaves as though the fake module is vendored1020			// with no dependencies.1021			ld.requirements.initVendor(ld, nil)1022		}1023		return ld.requirements, nil1024	}10251026	var modFiles []*modfile.File1027	var mainModules []module.Version1028	var indices []*modFileIndex1029	var errs []error1030	for _, modroot := range ld.modRoots {1031		gomod := modFilePath(modroot)1032		var fixed bool1033		data, f, err := ReadModFile(gomod, fixVersion(ld, ctx, &fixed))1034		if err != nil {1035			if ld.inWorkspaceMode() {1036				if tooNew, ok := err.(*gover.TooNewError); ok && !strings.HasPrefix(cfg.CmdName, "work ") {1037					// Switching to a newer toolchain won't help - the go.work has the wrong version.1038					// Report this more specific error, unless we are a command like 'go work use'1039					// or 'go work sync', which will fix the problem after the caller sees the TooNewError1040					// and switches to a newer toolchain.1041					err = errWorkTooOld(gomod, workFile, tooNew.GoVersion)1042				} else {1043					err = fmt.Errorf("cannot load module %s listed in go.work file: %w",1044						base.ShortPath(filepath.Dir(gomod)), base.ShortPathError(err))1045				}1046			}1047			errs = append(errs, err)1048			continue1049		}1050		if ld.inWorkspaceMode() && !strings.HasPrefix(cfg.CmdName, "work ") {1051			// Refuse to use workspace if its go version is too old.1052			// Disable this check if we are a workspace command like work use or work sync,1053			// which will fix the problem.1054			mv := gover.FromGoMod(f)1055			wv := gover.FromGoWork(workFile)1056			if gover.Compare(mv, wv) > 0 && gover.Compare(mv, gover.GoStrictVersion) >= 0 {1057				errs = append(errs, errWorkTooOld(gomod, workFile, mv))1058				continue1059			}1060		}10611062		if !ld.inWorkspaceMode() {1063			ok := true1064			for _, g := range f.Godebug {1065				if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {1066					errs = append(errs, fmt.Errorf("error loading go.mod:\n%s:%d: %v", base.ShortPath(gomod), g.Syntax.Start.Line, err))1067					ok = false1068				}1069			}1070			if !ok {1071				continue1072			}1073		}10741075		modFiles = append(modFiles, f)1076		mainModule := f.Module.Mod1077		mainModules = append(mainModules, mainModule)1078		indices = append(indices, indexModFile(data, f, mainModule, fixed))10791080		if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {1081			if pathErr, ok := err.(*module.InvalidPathError); ok {1082				pathErr.Kind = "module"1083			}1084			errs = append(errs, err)1085		}1086	}1087	if len(errs) > 0 {1088		return nil, errors.Join(errs...)1089	}10901091	ld.MainModules = makeMainModules(ld, mainModules, ld.modRoots, modFiles, indices, workFile)1092	setDefaultBuildMod(ld) // possibly enable automatic vendoring1093	rs := requirementsFromModFiles(ld, workFile, modFiles)10941095	if cfg.BuildMod == "vendor" {1096		readVendorList(VendorDir(ld))1097		versions := ld.MainModules.Versions()1098		indexes := make([]*modFileIndex, 0, len(versions))1099		modFiles := make([]*modfile.File, 0, len(versions))1100		for _, m := range versions {1101			indexes = append(indexes, ld.MainModules.Index(m))1102			modFiles = append(modFiles, ld.MainModules.ModFile(m))1103		}1104		checkVendorConsistency(ld, indexes, modFiles)1105		rs.initVendor(ld, vendorList)1106	}11071108	if ld.inWorkspaceMode() {1109		// We don't need to update the mod file so return early.1110		ld.requirements = rs1111		return rs, nil1112	}11131114	mainModule := ld.MainModules.mustGetSingleMainModule(ld)11151116	if rs.hasRedundantRoot(ld) {1117		// If any module path appears more than once in the roots, we know that the1118		// go.mod file needs to be updated even though we have not yet loaded any1119		// transitive dependencies.1120		var err error1121		rs, err = updateRoots(ld, ctx, rs.direct, rs, nil, nil, false)1122		if err != nil {1123			return nil, err1124		}1125	}11261127	if ld.MainModules.Index(mainModule).goVersion == "" && rs.pruning != workspace {1128		// TODO(#45551): Do something more principled instead of checking1129		// cfg.CmdName directly here.1130		if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {1131			// go line is missing from go.mod; add one there and add to derived requirements.1132			v := gover.Local()1133			if opts != nil && opts.TidyGoVersion != "" {1134				v = opts.TidyGoVersion1135			}1136			addGoStmt(ld.MainModules.ModFile(mainModule), mainModule, v)1137			rs = overrideRoots(ld, ctx, rs, []module.Version{{Path: "go", Version: v}})11381139			// We need to add a 'go' version to the go.mod file, but we must assume1140			// that its existing contents match something between Go 1.11 and 1.16.1141			// Go 1.11 through 1.16 do not support graph pruning, but the latest Go1142			// version uses a pruned module graph — so we need to convert the1143			// requirements to support pruning.1144			if gover.Compare(v, gover.ExplicitIndirectVersion) >= 0 {1145				var err error1146				rs, err = convertPruning(ld, ctx, rs, pruned)1147				if err != nil {1148					return nil, err1149				}1150			}1151		} else {1152			rawGoVersion.Store(mainModule, gover.DefaultGoModVersion)1153		}1154	}11551156	ld.requirements = rs1157	return ld.requirements, nil1158}11591160func errWorkTooOld(gomod string, wf *modfile.WorkFile, goVers string) error {1161	verb := "lists"1162	if wf == nil || wf.Go == nil {1163		// A go.work file implicitly requires go1.181164		// even when it doesn't list any version.1165		verb = "implicitly requires"1166	}1167	return fmt.Errorf("module %s listed in go.work file requires go >= %s, but go.work %s go %s; to download and use go %s:\n\tgo work use",1168		base.ShortPath(filepath.Dir(gomod)), goVers, verb, gover.FromGoWork(wf), goVers)1169}11701171// CheckReservedModulePath checks whether the module path is a reserved module path1172// that can't be used for a user's module.1173func CheckReservedModulePath(path string) error {1174	if gover.IsToolchain(path) {1175		return errors.New("module path is reserved")1176	}11771178	return nil1179}11801181// CreateModFile initializes a new module by creating a go.mod file.1182//1183// If modPath is empty, CreateModFile will attempt to infer the path from the1184// directory location within GOPATH.1185//1186// If a vendoring configuration file is present, CreateModFile will attempt to1187// translate it to go.mod directives. The resulting build list may not be1188// exactly the same as in the legacy configuration (for example, we can't get1189// packages at multiple versions from the same module).1190func CreateModFile(ld *Loader, ctx context.Context, modPath string) {1191	modRoot := base.Cwd()1192	ld.modRoots = []string{modRoot}1193	Init(ld)1194	modFilePath := modFilePath(modRoot)1195	if _, err := fsys.Stat(modFilePath); err == nil {1196		base.Fatalf("go: %s already exists", modFilePath)1197	}11981199	if modPath == "" {1200		var err error1201		modPath, err = findModulePath(modRoot)1202		if err != nil {1203			base.Fatal(err)1204		}1205	}1206	checkModulePath(modPath)12071208	if cfg.ModFile != "" {1209		fmt.Fprintf(os.Stderr, "go: creating new go.mod (using -modfile path %s): module %s\n", base.ShortPath(modFilePath), modPath)1210	} else {1211		fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)1212	}1213	modFile := new(modfile.File)1214	modFile.AddModuleStmt(modPath)1215	ld.MainModules = makeMainModules(ld, []module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, nil)1216	addGoStmt(modFile, modFile.Module.Mod, gover.Local()) // Add the go directive before converted module requirements.12171218	rs := requirementsFromModFiles(ld, nil, []*modfile.File{modFile})1219	rs, err := updateRoots(ld, ctx, rs.direct, rs, nil, nil, false)1220	if err != nil {1221		base.Fatal(err)1222	}1223	ld.requirements = rs1224	if err := commitRequirements(ld, ctx, WriteOpts{}); err != nil {1225		base.Fatal(err)1226	}12271228	// Suggest running 'go mod tidy' unless the project is empty. Even if we1229	// imported all the correct requirements above, we're probably missing1230	// some sums, so the next build command in -mod=readonly will likely fail.1231	//1232	// We look for non-hidden .go files or subdirectories to determine whether1233	// this is an existing project. Walking the tree for packages would be more1234	// accurate, but could take much longer.1235	empty := true1236	files, _ := os.ReadDir(modRoot)1237	for _, f := range files {1238		name := f.Name()1239		if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") {1240			continue1241		}1242		if strings.HasSuffix(name, ".go") || f.IsDir() {1243			empty = false1244			break1245		}1246	}1247	if !empty {1248		fmt.Fprintf(os.Stderr, "go: to add module requirements and sums:\n\tgo mod tidy\n")1249	}1250}12511252func checkModulePath(modPath string) {1253	if err := module.CheckImportPath(modPath); err != nil {1254		if pathErr, ok := err.(*module.InvalidPathError); ok {1255			pathErr.Kind = "module"1256			// Same as build.IsLocalPath()1257			if pathErr.Path == "." || pathErr.Path == ".." ||1258				strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {1259				pathErr.Err = errors.New("is a local import path")1260			}1261		}1262		base.Fatal(err)1263	}1264	if err := CheckReservedModulePath(modPath); err != nil {1265		base.Fatalf(`go: invalid module path %q: `, modPath)1266	}1267	if _, _, ok := module.SplitPathVersion(modPath); !ok {1268		if strings.HasPrefix(modPath, "gopkg.in/") {1269			invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath))1270			base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)1271		}1272		invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath))1273		base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)1274	}1275}12761277// fixVersion returns a modfile.VersionFixer implemented using the Query function.1278//1279// It resolves commit hashes and branch names to versions,1280// canonicalizes versions that appeared in early vgo drafts,1281// and does nothing for versions that already appear to be canonical.1282//1283// The VersionFixer sets 'fixed' if it ever returns a non-canonical version.1284func fixVersion(ld *Loader, ctx context.Context, fixed *bool) modfile.VersionFixer {1285	return func(path, vers string) (resolved string, err error) {1286		defer func() {1287			if err == nil && resolved != vers {1288				*fixed = true1289			}1290		}()12911292		// Special case: remove the old -gopkgin- hack.1293		if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {1294			vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]1295		}12961297		// fixVersion is called speculatively on every1298		// module, version pair from every go.mod file.1299		// Avoid the query if it looks OK.1300		_, pathMajor, ok := module.SplitPathVersion(path)1301		if !ok {1302			return "", &module.ModuleError{1303				Path: path,1304				Err: &module.InvalidVersionError{1305					Version: vers,1306					Err:     fmt.Errorf("malformed module path %q", path),1307				},1308			}1309		}1310		if vers != "" && module.CanonicalVersion(vers) == vers {1311			if err := module.CheckPathMajor(vers, pathMajor); err != nil {1312				return "", module.VersionError(module.Version{Path: path, Version: vers}, err)1313			}1314			return vers, nil1315		}13161317		info, err := Query(ld, ctx, path, vers, "", nil)1318		if err != nil {1319			return "", err1320		}1321		return info.Version, nil1322	}1323}13241325// AllowMissingModuleImports allows import paths to be resolved to modules1326// when there is no module root. Normally, this is forbidden because it's slow1327// and there's no way to make the result reproducible, but some commands1328// like 'go get' are expected to do this.1329//1330// This function affects the default cfg.BuildMod when outside of a module,1331// so it can only be called prior to Init.1332func (ld *Loader) AllowMissingModuleImports() {1333	if ld.initialized {1334		panic("AllowMissingModuleImports after Init")1335	}1336	ld.allowMissingModuleImports = true1337}13381339// makeMainModules creates a MainModuleSet and associated variables according to1340// the given main modules.1341func makeMainModules(ld *Loader, ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFile *modfile.WorkFile) *MainModuleSet {1342	for _, m := range ms {1343		if m.Version != "" {1344			panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m))1345		}1346	}1347	modRootContainingCWD := findModuleRoot(base.Cwd())1348	mainModules := &MainModuleSet{1349		versions:        slices.Clip(ms),1350		inGorootSrc:     map[module.Version]bool{},1351		pathPrefix:      map[module.Version]string{},1352		modRoot:         map[module.Version]string{},1353		modFiles:        map[module.Version]*modfile.File{},1354		indices:         map[module.Version]*modFileIndex{},1355		highestReplaced: map[string]string{},1356		tools:           map[string]bool{},1357		workFile:        workFile,1358	}1359	var workFileReplaces []*modfile.Replace1360	if workFile != nil {1361		workFileReplaces = workFile.Replace1362		mainModules.workFileReplaceMap = toReplaceMap(workFile.Replace)1363	}1364	mainModulePaths := make(map[string]bool)1365	for _, m := range ms {1366		if mainModulePaths[m.Path] {1367			base.Errorf("go: module %s appears multiple times in workspace", m.Path)1368		}1369		mainModulePaths[m.Path] = true1370	}1371	replacedByWorkFile := make(map[string]bool)1372	replacements := make(map[module.Version]module.Version)1373	for _, r := range workFileReplaces {1374		if mainModulePaths[r.Old.Path] && r.Old.Version == "" {1375			base.Errorf("go: workspace module %v is replaced at all versions in the go.work file. To fix, remove the replacement from the go.work file or specify the version at which to replace the module.", r.Old.Path)1376		}1377		replacedByWorkFile[r.Old.Path] = true1378		v, ok := mainModules.highestReplaced[r.Old.Path]1379		if !ok || gover.ModCompare(r.Old.Path, r.Old.Version, v) > 0 {1380			mainModules.highestReplaced[r.Old.Path] = r.Old.Version1381		}1382		replacements[r.Old] = r.New1383	}1384	for i, m := range ms {1385		mainModules.pathPrefix[m] = m.Path1386		mainModules.modRoot[m] = rootDirs[i]1387		mainModules.modFiles[m] = modFiles[i]1388		mainModules.indices[m] = indices[i]13891390		if mainModules.modRoot[m] == modRootContainingCWD {1391			mainModules.modContainingCWD = m1392		}13931394		if rel := search.InDir(rootDirs[i], cfg.GOROOTsrc); rel != "" {1395			mainModules.inGorootSrc[m] = true1396			if m.Path == "std" {1397				// The "std" module in GOROOT/src is the Go standard library. Unlike other1398				// modules, the packages in the "std" module have no import-path prefix.1399				//1400				// Modules named "std" outside of GOROOT/src do not receive this special1401				// treatment, so it is possible to run 'go test .' in other GOROOTs to1402				// test individual packages using a combination of the modified package1403				// and the ordinary standard library.1404				// (See https://golang.org/issue/30756.)1405				mainModules.pathPrefix[m] = ""1406			}1407		}14081409		if modFiles[i] != nil {1410			curModuleReplaces := make(map[module.Version]bool)1411			for _, r := range modFiles[i].Replace {1412				if replacedByWorkFile[r.Old.Path] {1413					continue1414				}1415				newV := r.New1416				if WorkFilePath(ld) != "" && newV.Version == "" && !filepath.IsAbs(newV.Path) {1417					// Since we are in a workspace, we may be loading replacements from1418					// multiple go.mod files. Relative paths in those replacement are1419					// relative to the go.mod file, not the workspace, so the same string1420					// may refer to two different paths and different strings may refer to1421					// the same path. Convert them all to be absolute instead.1422					//1423					// (We could do this outside of a workspace too, but it would mean that1424					// replacement paths in error strings needlessly differ from what's in1425					// the go.mod file.)1426					newV.Path = filepath.Join(rootDirs[i], newV.Path)1427				}1428				if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != newV {1429					base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -replace %v=[override]\" to resolve", r.Old, prev, newV, r.Old)1430				}1431				curModuleReplaces[r.Old] = true1432				replacements[r.Old] = newV14331434				v, ok := mainModules.highestReplaced[r.Old.Path]1435				if !ok || gover.ModCompare(r.Old.Path, r.Old.Version, v) > 0 {1436					mainModules.highestReplaced[r.Old.Path] = r.Old.Version1437				}1438			}14391440			for _, t := range modFiles[i].Tool {1441				if err := module.CheckImportPath(t.Path); err != nil {1442					if e, ok := err.(*module.InvalidPathError); ok {1443						e.Kind = "tool"1444					}1445					base.Fatal(err)1446				}14471448				mainModules.tools[t.Path] = true1449			}1450		}1451	}14521453	return mainModules1454}14551456// requirementsFromModFiles returns the set of non-excluded requirements from1457// the global modFile.1458func requirementsFromModFiles(ld *Loader, workFile *modfile.WorkFile, modFiles []*modfile.File) *Requirements {1459	var roots []module.Version1460	direct := map[string]bool{}1461	var pruning modPruning1462	if ld.inWorkspaceMode() {1463		pruning = workspace1464		roots = make([]module.Version, len(ld.MainModules.Versions()), 2+len(ld.MainModules.Versions()))1465		copy(roots, ld.MainModules.Versions())1466		goVersion := gover.FromGoWork(workFile)1467		var toolchain string1468		if workFile.Toolchain != nil {1469			toolchain = workFile.Toolchain.Name1470		}1471		roots = appendGoAndToolchainRoots(roots, goVersion, toolchain, direct)1472		direct = directRequirements(modFiles)1473	} else {1474		pruning = pruningForGoVersion(ld.MainModules.GoVersion(ld))1475		if len(modFiles) != 1 {1476			panic(fmt.Errorf("requirementsFromModFiles called with %v modfiles outside workspace mode", len(modFiles)))1477		}1478		modFile := modFiles[0]1479		roots, direct = rootsFromModFile(ld, ld.MainModules.mustGetSingleMainModule(ld), modFile, withToolchainRoot)1480	}14811482	gover.ModSort(roots)1483	rs := newRequirements(ld, pruning, roots, direct)1484	return rs1485}14861487type addToolchainRoot bool14881489const (1490	omitToolchainRoot addToolchainRoot = false1491	withToolchainRoot                  = true1492)14931494func directRequirements(modFiles []*modfile.File) map[string]bool {1495	direct := make(map[string]bool)1496	for _, modFile := range modFiles {1497		for _, r := range modFile.Require {1498			if !r.Indirect {1499				direct[r.Mod.Path] = true1500			}1501		}1502	}1503	return direct1504}15051506func rootsFromModFile(ld *Loader, m module.Version, modFile *modfile.File, addToolchainRoot addToolchainRoot) (roots []module.Version, direct map[string]bool) {1507	direct = make(map[string]bool)1508	padding := 2 // Add padding for the toolchain and go version, added upon return.1509	if !addToolchainRoot {1510		padding = 11511	}1512	roots = make([]module.Version, 0, padding+len(modFile.Require))1513	for _, r := range modFile.Require {1514		if index := ld.MainModules.Index(m); index != nil && index.exclude[r.Mod] {1515			if cfg.BuildMod == "mod" {1516				fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)1517			} else {1518				fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)1519			}1520			continue1521		}15221523		roots = append(roots, r.Mod)1524		if !r.Indirect {1525			direct[r.Mod.Path] = true1526		}1527	}1528	goVersion := gover.FromGoMod(modFile)1529	var toolchain string1530	if addToolchainRoot && modFile.Toolchain != nil {1531		toolchain = modFile.Toolchain.Name1532	}1533	roots = appendGoAndToolchainRoots(roots, goVersion, toolchain, direct)1534	return roots, direct1535}15361537func appendGoAndToolchainRoots(roots []module.Version, goVersion, toolchain string, direct map[string]bool) []module.Version {1538	// Add explicit go and toolchain versions, inferring as needed.1539	roots = append(roots, module.Version{Path: "go", Version: goVersion})1540	direct["go"] = true // Every module directly uses the language and runtime.15411542	if toolchain != "" {1543		roots = append(roots, module.Version{Path: "toolchain", Version: toolchain})1544		// Leave the toolchain as indirect: nothing in the user's module directly1545		// imports a package from the toolchain, and (like an indirect dependency in1546		// a module without graph pruning) we may remove the toolchain line1547		// automatically if the 'go' version is changed so that it implies the exact1548		// same toolchain.1549	}1550	return roots1551}15521553// setDefaultBuildMod sets a default value for cfg.BuildMod if the -mod flag1554// wasn't provided. setDefaultBuildMod may be called multiple times.1555func setDefaultBuildMod(ld *Loader) {1556	if cfg.BuildModExplicit {1557		if ld.inWorkspaceMode() && cfg.BuildMod != "readonly" && cfg.BuildMod != "vendor" {1558			switch cfg.CmdName {1559			case "work sync", "mod graph", "mod verify", "mod why":1560				// These commands run with BuildMod set to mod, but they don't take the1561				// -mod flag, so we should never get here.1562				panic("in workspace mode and -mod was set explicitly, but command doesn't support setting -mod")1563			default:1564				base.Fatalf("go: -mod may only be set to readonly or vendor when in workspace mode, but it is set to %q"+1565					"\n\tRemove the -mod flag to use the default readonly value, "+1566					"\n\tor set GOWORK=off to disable workspace mode.", cfg.BuildMod)1567			}1568		}1569		// Don't override an explicit '-mod=' argument.1570		return1571	}15721573	// TODO(#40775): commands should pass in the module mode as an option1574	// to modload functions instead of relying on an implicit setting1575	// based on command name.1576	switch cfg.CmdName {1577	case "get", "mod download", "mod init", "mod tidy", "work sync":1578		// These commands are intended to update go.mod and go.sum.1579		cfg.BuildMod = "mod"1580		return1581	case "mod graph", "mod verify", "mod why":1582		// These commands should not update go.mod or go.sum, but they should be1583		// able to fetch modules not in go.sum and should not report errors if1584		// go.mod is inconsistent. They're useful for debugging, and they need1585		// to work in buggy situations.1586		cfg.BuildMod = "mod"1587		return1588	case "mod vendor", "work vendor":1589		cfg.BuildMod = "readonly"1590		return1591	}1592	if ld.modRoots == nil {1593		if ld.allowMissingModuleImports {1594			cfg.BuildMod = "mod"1595		} else {1596			cfg.BuildMod = "readonly"1597		}1598		return1599	}16001601	if len(ld.modRoots) >= 1 {1602		var goVersion string1603		var versionSource string1604		if ld.inWorkspaceMode() {1605			versionSource = "go.work"1606			if wfg := ld.MainModules.WorkFile().Go; wfg != nil {1607				goVersion = wfg.Version1608			}1609		} else {1610			versionSource = "go.mod"1611			index := ld.MainModules.GetSingleIndexOrNil(ld)1612			if index != nil {1613				goVersion = index.goVersion1614			}1615		}1616		vendorDir := ""1617		if ld.workFilePath != "" {1618			vendorDir = filepath.Join(filepath.Dir(ld.workFilePath), "vendor")1619		} else {1620			if len(ld.modRoots) != 1 {1621				panic(fmt.Errorf("outside workspace mode, but have %v modRoots", ld.modRoots))1622			}1623			vendorDir = filepath.Join(ld.modRoots[0], "vendor")1624		}1625		if fi, err := fsys.Stat(vendorDir); err == nil && fi.IsDir() {1626			if goVersion != "" {1627				if gover.Compare(goVersion, "1.14") < 0 {1628					// The go version is less than 1.14. Don't set -mod=vendor by default.1629					// Since a vendor directory exists, we should record why we didn't use it.1630					// This message won't normally be shown, but it may appear with import errors.1631					cfg.BuildModReason = fmt.Sprintf("Go version in "+versionSource+" is %s, so vendor directory was not used.", goVersion)1632				} else {1633					vendoredWorkspace, err := modulesTextIsForWorkspace(vendorDir)1634					if err != nil {1635						base.Fatalf("go: reading modules.txt for vendor directory: %v", err)1636					}1637					if vendoredWorkspace != (versionSource == "go.work") {1638						if vendoredWorkspace {1639							cfg.BuildModReason = "Outside workspace mode, but vendor directory is for a workspace."1640						} else {1641							cfg.BuildModReason = "In workspace mode, but vendor directory is not for a workspace"1642						}1643					} else {1644						// The Go version is at least 1.14, a vendor directory exists, and1645						// the modules.txt was generated in the same mode the command is running in.1646						// Set -mod=vendor by default.1647						cfg.BuildMod = "vendor"1648						cfg.BuildModReason = "Go version in " + versionSource + " is at least 1.14 and vendor directory exists."1649						return1650					}1651				}1652			} else {1653				cfg.BuildModReason = fmt.Sprintf("Go version in %s is unspecified, so vendor directory was not used.", versionSource)1654			}1655		}1656	}16571658	cfg.BuildMod = "readonly"1659}16601661func modulesTextIsForWorkspace(vendorDir string) (bool, error) {1662	f, err := fsys.Open(filepath.Join(vendorDir, "modules.txt"))1663	if errors.Is(err, os.ErrNotExist) {1664		// Some vendor directories exist that don't contain modules.txt.1665		// This mostly happens when converting to modules.1666		// We want to preserve the behavior that mod=vendor is set (even though1667		// readVendorList does nothing in that case).1668		return false, nil1669	}1670	if err != nil {1671		return false, err1672	}1673	defer f.Close()1674	var buf [512]byte1675	n, err := f.Read(buf[:])1676	if err != nil && err != io.EOF {1677		return false, err1678	}1679	line, _, _ := strings.Cut(string(buf[:n]), "\n")1680	if annotations, ok := strings.CutPrefix(line, "## "); ok {1681		for entry := range strings.SplitSeq(annotations, ";") {1682			entry = strings.TrimSpace(entry)1683			if entry == "workspace" {1684				return true, nil1685			}1686		}1687	}1688	return false, nil1689}16901691func mustHaveCompleteRequirements(ld *Loader) bool {1692	return cfg.BuildMod != "mod" && !ld.inWorkspaceMode()1693}16941695// addGoStmt adds a go directive to the go.mod file if it does not already1696// include one. The 'go' version added, if any, is the latest version supported1697// by this toolchain.1698func addGoStmt(modFile *modfile.File, mod module.Version, v string) {1699	if modFile.Go != nil && modFile.Go.Version != "" {1700		return1701	}1702	forceGoStmt(modFile, mod, v)1703}17041705func forceGoStmt(modFile *modfile.File, mod module.Version, v string) {1706	if err := modFile.AddGoStmt(v); err != nil {1707		base.Fatalf("go: internal error: %v", err)1708	}1709	rawGoVersion.Store(mod, v)1710}17111712var altConfigs = []string{1713	".git/config",1714}17151716func findModuleRoot(dir string) (roots string) {1717	if dir == "" {1718		panic("dir not set")1719	}1720	dir = filepath.Clean(dir)17211722	// Look for enclosing go.mod.1723	for {1724		if fi, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {1725			return dir1726		}1727		d := filepath.Dir(dir)1728		if d == dir {1729			break1730		}1731		dir = d1732	}1733	return ""1734}17351736func findWorkspaceFile(dir string) (root string) {1737	if dir == "" {1738		panic("dir not set")1739	}1740	dir = filepath.Clean(dir)17411742	// Look for enclosing go.mod.1743	for {1744		f := filepath.Join(dir, "go.work")1745		if fi, err := fsys.Stat(f); err == nil && !fi.IsDir() {1746			return f1747		}1748		d := filepath.Dir(dir)1749		if d == dir {1750			break1751		}1752		if d == cfg.GOROOT {1753			// As a special case, don't cross GOROOT to find a go.work file.1754			// The standard library and commands built in go always use the vendored1755			// dependencies, so avoid using a most likely irrelevant go.work file.1756			return ""1757		}1758		dir = d1759	}1760	return ""1761}17621763func findAltConfig(dir string) (root, name string) {1764	if dir == "" {1765		panic("dir not set")1766	}1767	dir = filepath.Clean(dir)1768	if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel != "" {1769		// Don't suggest creating a module from $GOROOT/.git/config1770		// or a config file found in any parent of $GOROOT (see #34191).1771		return "", ""1772	}1773	for {1774		for _, name := range altConfigs {1775			if fi, err := fsys.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {1776				return dir, name1777			}1778		}1779		d := filepath.Dir(dir)1780		if d == dir {1781			break1782		}1783		dir = d1784	}1785	return "", ""1786}17871788func findModulePath(dir string) (string, error) {1789	// TODO(bcmills): once we have located a plausible module path, we should1790	// query version control (if available) to verify that it matches the major1791	// version of the most recent tag.1792	// See https://golang.org/issue/29433, https://golang.org/issue/27009, and1793	// https://golang.org/issue/31549.17941795	// Cast about for import comments,1796	// first in top-level directory, then in subdirectories.1797	list, _ := os.ReadDir(dir)1798	for _, info := range list {1799		if info.Type().IsRegular() && strings.HasSuffix(info.Name(), ".go") {1800			if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {1801				return com, nil1802			}1803		}1804	}1805	for _, info1 := range list {1806		if info1.IsDir() {1807			files, _ := os.ReadDir(filepath.Join(dir, info1.Name()))1808			for _, info2 := range files {1809				if info2.Type().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {1810					if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {1811						return path.Dir(com), nil1812					}1813				}1814			}1815		}1816	}18171818	// Look for path in GOPATH.1819	var badPathErr error1820	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {1821		if gpdir == "" {1822			continue1823		}1824		if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {1825			path := filepath.ToSlash(rel)1826			// gorelease will alert users publishing their modules to fix their paths.1827			if err := module.CheckImportPath(path); err != nil {1828				badPathErr = err1829				break1830			}1831			return path, nil1832		}1833	}18341835	reason := "outside GOPATH, module path must be specified"1836	if badPathErr != nil {1837		// return a different error message if the module was in GOPATH, but1838		// the module path determined above would be an invalid path.1839		reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)1840	}1841	msg := `cannot determine module path for source directory %s (%s)18421843Example usage:1844	'go mod init example.com/m' to initialize a v0 or v1 module1845	'go mod init example.com/m/v2' to initialize a v2 module18461847Run 'go help mod init' for more information.1848`1849	return "", fmt.Errorf(msg, dir, reason)1850}18511852var importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)18531854func findImportComment(file string) string {1855	data, err := os.ReadFile(file)1856	if err != nil {1857		return ""1858	}1859	m := importCommentRE.FindSubmatch(data)1860	if m == nil {1861		return ""1862	}1863	path, err := strconv.Unquote(string(m[1]))1864	if err != nil {1865		return ""1866	}1867	return path1868}18691870// WriteOpts control the behavior of WriteGoMod.1871type WriteOpts struct {1872	DropToolchain     bool // go get toolchain@none1873	ExplicitToolchain bool // go get has set explicit toolchain version18741875	AddTools  []string // go get -tool example.com/m11876	DropTools []string // go get -tool example.com/m1@none18771878	// TODO(bcmills): Make 'go mod tidy' update the go version in the Requirements1879	// instead of writing directly to the modfile.File1880	TidyWroteGo bool // Go.Version field already updated by 'go mod tidy'1881}18821883// WriteGoMod writes the current build list back to go.mod.1884func WriteGoMod(ld *Loader, ctx context.Context, opts WriteOpts) error {1885	ld.requirements = LoadModFile(ld, ctx)1886	return commitRequirements(ld, ctx, opts)1887}18881889var errNoChange = errors.New("no update needed")18901891// UpdateGoModFromReqs returns a modified go.mod file using the current1892// requirements. It does not commit these changes to disk.1893func UpdateGoModFromReqs(ld *Loader, ctx context.Context, opts WriteOpts) (before, after []byte, modFile *modfile.File, err error) {1894	if ld.MainModules.Len() != 1 || ld.MainModules.ModRoot(ld.MainModules.Versions()[0]) == "" {1895		// We aren't in a module, so we don't have anywhere to write a go.mod file.1896		return nil, nil, nil, errNoChange1897	}1898	mainModule := ld.MainModules.mustGetSingleMainModule(ld)1899	modFile = ld.MainModules.ModFile(mainModule)1900	if modFile == nil {1901		// command-line-arguments has no .mod file to write.1902		return nil, nil, nil, errNoChange1903	}1904	before, err = modFile.Format()1905	if err != nil {1906		return nil, nil, nil, err1907	}19081909	var list []*modfile.Require1910	toolchain := ""1911	goVersion := ""1912	for _, m := range ld.requirements.rootModules {1913		if m.Path == "go" {1914			goVersion = m.Version1915			continue1916		}1917		if m.Path == "toolchain" {1918			toolchain = m.Version1919			continue1920		}1921		list = append(list, &modfile.Require{1922			Mod:      m,1923			Indirect: !ld.requirements.direct[m.Path],1924		})1925	}19261927	// Update go line.1928	// Every MVS graph we consider should have go as a root,1929	// and toolchain is either implied by the go line or explicitly a root.1930	if goVersion == "" {1931		base.Fatalf("go: internal error: missing go root module in WriteGoMod")1932	}1933	if gover.Compare(goVersion, gover.Local()) > 0 {1934		// We cannot assume that we know how to update a go.mod to a newer version.1935		return nil, nil, nil, &gover.TooNewError{What: "updating go.mod", GoVersion: goVersion}1936	}1937	wroteGo := opts.TidyWroteGo1938	if !wroteGo && modFile.Go == nil || modFile.Go.Version != goVersion {1939		alwaysUpdate := cfg.BuildMod == "mod" || cfg.CmdName == "mod tidy" || cfg.CmdName == "get"1940		if modFile.Go == nil && goVersion == gover.DefaultGoModVersion && !alwaysUpdate {1941			// The go.mod has no go line, the implied default Go version matches1942			// what we've computed for the graph, and we're not in one of the1943			// traditional go.mod-updating programs, so leave it alone.1944		} else {1945			wroteGo = true1946			forceGoStmt(modFile, mainModule, goVersion)1947		}1948	}19491950	// Add Go 1.24 requirement if we're running go get and there are tool directives.1951	tools := map[string]bool{}1952	for _, t := range modFile.Tool {1953		tools[t.Path] = true1954	}1955	for _, t := range opts.DropTools {1956		delete(tools, t)1957	}1958	for _, t := range opts.AddTools {1959		tools[t] = true1960	}1961	if len(tools) > 0 && gover.Compare(goVersion, gover.GoModToolVersion) < 0 && cfg.CmdName == "get" {1962		if opts.ExplicitToolchain {1963			return nil, nil, nil, errors.New(gover.GoModToolVersion + " is required for tool directives in go.mod: go get go@" + gover.GoModToolVersion + ".0")1964		}1965		// TODO: If we start enforcing that the go version is > 1.24 on modules1966		// that have tool directives, add a requirement instead of calling forceGoStmt.1967		goVersion = gover.GoModToolVersion1968		forceGoStmt(modFile, mainModule, gover.GoModToolVersion)1969	}19701971	if toolchain == "" {1972		toolchain = "go" + goVersion1973	}1974	toolVers := gover.FromToolchain(toolchain)1975	if opts.DropToolchain || toolchain == "go"+goVersion || (gover.Compare(toolVers, gover.GoStrictVersion) < 0 && !opts.ExplicitToolchain) {1976		// go get toolchain@none or toolchain matches go line or isn't valid; drop it.1977		// TODO(#57001): 'go get' should reject explicit toolchains below GoStrictVersion.1978		modFile.DropToolchainStmt()1979	} else {1980		modFile.AddToolchainStmt(toolchain)1981	}19821983	for _, path := range opts.AddTools {1984		modFile.AddTool(path)1985	}19861987	for _, path := range opts.DropTools {1988		modFile.DropTool(path)1989	}19901991	// Update require blocks.1992	if gover.Compare(goVersion, gover.SeparateIndirectVersion) < 0 {1993		modFile.SetRequire(list)1994	} else if gover.Compare(goVersion, gover.SimplifyRequireVersion) < 0 {1995		modFile.SetRequireSeparateIndirect(list)1996	} else {1997		modFile.SetRequireAtMostTwo(list)1998	}1999	modFile.Cleanup()2000	after, err = modFile.Format()

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.