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.