"golang.org/x/mod/semver"
)
+// Variables set by other packages.
+//
+// TODO(#40775): See if these can be plumbed as explicit parameters.
+var (
+ // RootMode determines whether a module root is needed.
+ RootMode Root
+
+ // ForceUseModules may be set to force modules to be enabled when
+ // GO111MODULE=auto or to report an error when GO111MODULE=off.
+ ForceUseModules bool
+
+ allowMissingModuleImports bool
+)
+
+// Variables set in Init.
var (
initialized bool
+ modRoot string
+ gopath string
+)
- modRoot string
- Target module.Version
+// Variables set in initTarget (during {Load,Create}ModFile).
+var (
+ Target module.Version
// targetPrefix is the path prefix for packages in Target, without a trailing
// slash. For most modules, targetPrefix is just Target.Path, but the
// targetInGorootSrc caches whether modRoot is within GOROOT/src.
// The "std" module is special within GOROOT/src, but not otherwise.
targetInGorootSrc bool
-
- gopath string
-
- // RootMode determines whether a module root is needed.
- RootMode Root
-
- // ForceUseModules may be set to force modules to be enabled when
- // GO111MODULE=auto or to report an error when GO111MODULE=off.
- ForceUseModules bool
-
- allowMissingModuleImports bool
)
type Root int
Target = module.Version{Path: "command-line-arguments"}
targetPrefix = "command-line-arguments"
buildList = []module.Version{Target}
+ rawGoVersion.Store(Target, latestGoVersion())
return
}
// Errors returned by modfile.Parse begin with file:line.
base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
}
- modFile = f
- index = indexModFile(data, f, fixed)
-
if f.Module == nil {
// No module declaration. Must add module path.
base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
}
+ modFile = f
+ initTarget(f.Module.Mod)
+ index = indexModFile(data, f, fixed)
+
if err := checkModulePathLax(f.Module.Mod.Path); err != nil {
base.Fatalf("go: %v", err)
}
setDefaultBuildMod() // possibly enable automatic vendoring
- modFileToBuildList()
+ buildList = modFileToBuildList(modFile)
if cfg.BuildMod == "vendor" {
readVendorList()
checkVendorConsistency()
}
+ if index.goVersionV == "" && cfg.BuildMod == "mod" {
+ addGoStmt()
+ WriteGoMod()
+ }
}
// CreateModFile initializes a new module by creating a go.mod file.
fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
modFile = new(modfile.File)
modFile.AddModuleStmt(modPath)
+ initTarget(modFile.Module.Mod)
addGoStmt() // Add the go directive before converted module requirements.
convertedFrom, err := convertLegacyConfig(modPath)
base.Fatalf("go: %v", err)
}
- modFileToBuildList()
+ buildList = modFileToBuildList(modFile)
WriteGoMod()
// Suggest running 'go mod tidy' unless the project is empty. Even if we
allowMissingModuleImports = true
}
-// modFileToBuildList initializes buildList from the modFile.
-func modFileToBuildList() {
- Target = modFile.Module.Mod
- targetPrefix = Target.Path
+// initTarget sets Target and associated variables according to modFile,
+func initTarget(m module.Version) {
+ Target = m
+ targetPrefix = m.Path
+
if rel := search.InDir(base.Cwd, cfg.GOROOTsrc); rel != "" {
targetInGorootSrc = true
- if Target.Path == "std" {
+ if m.Path == "std" {
+ // The "std" module in GOROOT/src is the Go standard library. Unlike other
+ // modules, the packages in the "std" module have no import-path prefix.
+ //
+ // Modules named "std" outside of GOROOT/src do not receive this special
+ // treatment, so it is possible to run 'go test .' in other GOROOTs to
+ // test individual packages using a combination of the modified package
+ // and the ordinary standard library.
+ // (See https://golang.org/issue/30756.)
targetPrefix = ""
}
}
+}
+// modFileToBuildList returns the list of non-excluded requirements from f.
+func modFileToBuildList(f *modfile.File) []module.Version {
list := []module.Version{Target}
- for _, r := range modFile.Require {
+ for _, r := range f.Require {
if index != nil && index.exclude[r.Mod] {
if cfg.BuildMod == "mod" {
fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
list = append(list, r.Mod)
}
}
- buildList = list
+ return list
}
// setDefaultBuildMod sets a default value for cfg.BuildMod if the -mod flag
return "", nil
}
-// addGoStmt adds a go directive to the go.mod file if it does not already include one.
-// The 'go' version added, if any, is the latest version supported by this toolchain.
+// addGoStmt adds a go directive to the go.mod file if it does not already
+// include one. The 'go' version added, if any, is the latest version supported
+// by this toolchain.
func addGoStmt() {
if modFile.Go != nil && modFile.Go.Version != "" {
return
}
+ v := latestGoVersion()
+ if err := modFile.AddGoStmt(v); err != nil {
+ base.Fatalf("go: internal error: %v", err)
+ }
+ rawGoVersion.Store(Target, v)
+}
+
+// latestGoVersion returns the latest version of the Go language supported by
+// this toolchain.
+func latestGoVersion() string {
tags := build.Default.ReleaseTags
version := tags[len(tags)-1]
if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
base.Fatalf("go: unrecognized default version %q", version)
}
- if err := modFile.AddGoStmt(version[2:]); err != nil {
- base.Fatalf("go: internal error: %v", err)
- }
+ return version[2:]
}
var altConfigs = []string{
return
}
- if cfg.BuildMod != "readonly" {
- addGoStmt()
- }
-
if loaded != nil {
reqs := MinReqs()
min, err := reqs.Required(Target)
}
buildList, err := mvs.BuildList(Target, reqs)
if err != nil {
- panic(fmt.Sprintf("unexpected error reloading build list: %v", err))
+ // This call to mvs.BuildList should not fail if we have already read the
+ // complete build list. However, the initial “build list” initialized by
+ // modFileToBuildList is not complete: it contains only the explicit
+ // dependencies of the main module. So this call can fair if this is the
+ // first time we have actually loaded the real build list.
+ base.Fatalf("go: %v", err)
}
actualMods := make(map[string]module.Version)
--- /dev/null
+cp go.mod go.mod.orig
+
+# With -mod=readonly, we should not update the go version in use.
+#
+# We started adding the go version automatically in Go 1.12, so a module without
+# one encountered in the wild (such as in the module cache) should assume Go
+# 1.11 semantics.
+
+# For Go 1.11 modules, 'all' should include dependencies of tests.
+# (They are pruned out as of Go 1.16.)
+
+go list -mod=readonly all
+stdout '^example.com/dep$'
+stdout '^example.com/testdep$'
+cp stdout list-1.txt
+cmp go.mod go.mod.orig
+
+# For Go 1.11 modules, automatic vendoring should not take effect.
+# (That behavior was added in Go 1.14.)
+
+go list all # should default to -mod=readonly, not -mod=vendor.
+cmp stdout list-1.txt
+
+# When we set -mod=mod, the go version should be updated immediately,
+# narrowing the "all" pattern reported by that command.
+
+go list -mod=mod all
+! stdout '^example.com/testdep$'
+cp stdout list-2.txt
+cmpenv go.mod go.mod.want
+
+go list -mod=mod all
+cmp stdout list-2.txt
+
+# The updated version should have been written back to go.mod, so
+# automatic vendoring should come into effect (and fail).
+! go list all
+stderr '^go: inconsistent vendoring'
+
+cp go.mod.orig go.mod
+
+# In readonly or vendor mode (not -mod=mod), the inferred Go version is 1.11.
+# For Go 1.11 modules, Go 1.13 features should not be enabled.
+
+! go build -mod=readonly .
+stderr '^# example\.com/m\n\.[/\\]m\.go:5:11: underscores in numeric literals requires go1\.13 or later \(-lang was set to go1\.11; check go\.mod\)$'
+cmp go.mod go.mod.orig
+
+
+-- go.mod --
+module example.com/m
+
+require example.com/dep v0.1.0
+
+replace (
+ example.com/dep v0.1.0 => ./dep
+ example.com/testdep v0.1.0 => ./testdep
+)
+-- go.mod.want --
+module example.com/m
+
+go $goversion
+
+require example.com/dep v0.1.0
+
+replace (
+ example.com/dep v0.1.0 => ./dep
+ example.com/testdep v0.1.0 => ./testdep
+)
+-- vendor/example.com/dep/dep.go --
+package dep
+import _ "example.com/bananas"
+-- vendor/modules.txt --
+HAHAHA this is broken.
+
+-- m.go --
+package m
+
+import _ "example.com/dep"
+
+const x = 1_000
+
+-- dep/go.mod --
+module example.com/dep
+
+require example.com/testdep v0.1.0
+-- dep/dep.go --
+package dep
+-- dep/dep_test.go --
+package dep_test
+
+import _ "example.com/testdep"
+
+-- testdep/go.mod --
+module example.com/testdep
+-- testdep/testdep.go --
+package testdep