mirror of
https://github.com/golang/go
synced 2024-11-07 15:26:11 -07:00
cmd/go: add 'go version' statement in go.mod
We aren't planning to use this or advertise it much yet, but having support for it now will make it easier to start using in the future - older go commands will understand what 'go 1.20' means and that they don't have go 1.20. Fixes #23969. Change-Id: I729130b2690d3c0b794b49201526b53de5093c45 Reviewed-on: https://go-review.googlesource.com/125940 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
740e589bd0
commit
16962faf99
@ -778,7 +778,7 @@
|
||||
// Main bool // is this the main module?
|
||||
// Indirect bool // is this module only an indirect dependency of main module?
|
||||
// Dir string // directory holding files for this module, if any
|
||||
// GoMod string // go.mod file for this module, if any
|
||||
// GoMod string // path to go.mod file for this module, if any
|
||||
// Error *ModuleError // error loading module
|
||||
// }
|
||||
//
|
||||
@ -882,6 +882,8 @@
|
||||
// The -module flag changes (or, with -init, sets) the module's path
|
||||
// (the go.mod file's module line).
|
||||
//
|
||||
// The -go flag changes the minimum required version of Go listed in go.mod.
|
||||
//
|
||||
// The -require=path@version and -droprequire=path flags
|
||||
// add and drop a requirement on the given module path and version.
|
||||
// Note that -require overrides any existing requirements on path.
|
||||
|
@ -24,7 +24,6 @@ func loadTags() map[string]bool {
|
||||
if cfg.BuildContext.CgoEnabled {
|
||||
tags["cgo"] = true
|
||||
}
|
||||
// TODO: Should read these out of GOROOT source code?
|
||||
for _, tag := range cfg.BuildContext.BuildTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
|
@ -51,6 +51,8 @@ To override this guess, use the -module flag.
|
||||
The -module flag changes (or, with -init, sets) the module's path
|
||||
(the go.mod file's module line).
|
||||
|
||||
The -go flag changes the minimum required version of Go listed in go.mod.
|
||||
|
||||
The -require=path@version and -droprequire=path flags
|
||||
add and drop a requirement on the given module path and version.
|
||||
Note that -require overrides any existing requirements on path.
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -21,6 +22,7 @@ import (
|
||||
// A File is the parsed, interpreted form of a go.mod file.
|
||||
type File struct {
|
||||
Module *Module
|
||||
Go *Go
|
||||
Require []*Require
|
||||
Exclude []*Exclude
|
||||
Replace []*Replace
|
||||
@ -34,6 +36,12 @@ type Module struct {
|
||||
Syntax *Line
|
||||
}
|
||||
|
||||
// A Go is the go statement.
|
||||
type Go struct {
|
||||
Version string // "1.23"
|
||||
Syntax *Line
|
||||
}
|
||||
|
||||
// A Require is a single require statement.
|
||||
type Require struct {
|
||||
Mod module.Version
|
||||
@ -146,20 +154,39 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File
|
||||
return f, nil
|
||||
}
|
||||
|
||||
var goVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`)
|
||||
|
||||
func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
|
||||
// If strict is false, this module is a dependency.
|
||||
// We ignore all unknown directives and do not attempt to parse
|
||||
// replace and exclude either. They don't matter, and it will work better for
|
||||
// We ignore all unknown directives as well as main-module-only
|
||||
// directives like replace and exclude. It will work better for
|
||||
// forward compatibility if we can depend on modules that have unknown
|
||||
// statements (presumed relevant only when acting as the main module).
|
||||
if !strict && verb != "module" && verb != "require" {
|
||||
return
|
||||
// statements (presumed relevant only when acting as the main module)
|
||||
// and simply ignore those statements.
|
||||
if !strict {
|
||||
switch verb {
|
||||
case "module", "require", "go":
|
||||
// want these even for dependency go.mods
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch verb {
|
||||
default:
|
||||
fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb)
|
||||
|
||||
case "go":
|
||||
if f.Go != nil {
|
||||
fmt.Fprintf(errs, "%s:%d: repeated go statement\n", f.Syntax.Name, line.Start.Line)
|
||||
return
|
||||
}
|
||||
if len(args) != 1 || !goVersionRE.MatchString(args[0]) {
|
||||
fmt.Fprintf(errs, "%s:%d: usage: go 1.23\n", f.Syntax.Name, line.Start.Line)
|
||||
return
|
||||
}
|
||||
f.Go = &Go{Syntax: line}
|
||||
f.Go.Version = args[0]
|
||||
case "module":
|
||||
if f.Module != nil {
|
||||
fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)
|
||||
|
@ -344,7 +344,7 @@ func runGet(cmd *base.Command, args []string) {
|
||||
base.ExitIfErrors()
|
||||
|
||||
// Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks).
|
||||
// Resolve each one in parallell.
|
||||
// Resolve each one in parallel.
|
||||
reqs := modload.Reqs()
|
||||
var lookup par.Work
|
||||
for _, t := range tasks {
|
||||
|
@ -10,17 +10,18 @@ import "time"
|
||||
// and the fields are documented in the help text in ../list/list.go
|
||||
|
||||
type ModulePublic struct {
|
||||
Path string `json:",omitempty"` // module path
|
||||
Version string `json:",omitempty"` // module version
|
||||
Versions []string `json:",omitempty"` // available module versions
|
||||
Replace *ModulePublic `json:",omitempty"` // replaced by this module
|
||||
Time *time.Time `json:",omitempty"` // time version was created
|
||||
Update *ModulePublic `json:",omitempty"` // available update (with -u)
|
||||
Main bool `json:",omitempty"` // is this the main module?
|
||||
Indirect bool `json:",omitempty"` // module is only indirectly needed by main module
|
||||
Dir string `json:",omitempty"` // directory holding local copy of files, if any
|
||||
GoMod string `json:",omitempty"` // path to go.mod file describing module, if any
|
||||
Error *ModuleError `json:",omitempty"` // error loading module
|
||||
Path string `json:",omitempty"` // module path
|
||||
Version string `json:",omitempty"` // module version
|
||||
Versions []string `json:",omitempty"` // available module versions
|
||||
Replace *ModulePublic `json:",omitempty"` // replaced by this module
|
||||
Time *time.Time `json:",omitempty"` // time version was created
|
||||
Update *ModulePublic `json:",omitempty"` // available update (with -u)
|
||||
Main bool `json:",omitempty"` // is this the main module?
|
||||
Indirect bool `json:",omitempty"` // module is only indirectly needed by main module
|
||||
Dir string `json:",omitempty"` // directory holding local copy of files, if any
|
||||
GoMod string `json:",omitempty"` // path to go.mod file describing module, if any
|
||||
Error *ModuleError `json:",omitempty"` // error loading module
|
||||
GoVersion string `json:",omitempty"` // go version used in module
|
||||
}
|
||||
|
||||
type ModuleError struct {
|
||||
|
@ -86,13 +86,17 @@ func addVersions(m *modinfo.ModulePublic) {
|
||||
|
||||
func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
||||
if m == Target {
|
||||
return &modinfo.ModulePublic{
|
||||
info := &modinfo.ModulePublic{
|
||||
Path: m.Path,
|
||||
Version: m.Version,
|
||||
Main: true,
|
||||
Dir: ModRoot,
|
||||
GoMod: filepath.Join(ModRoot, "go.mod"),
|
||||
}
|
||||
if modFile.Go != nil {
|
||||
info.GoVersion = modFile.Go.Version
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
info := &modinfo.ModulePublic{
|
||||
@ -100,6 +104,9 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
||||
Version: m.Version,
|
||||
Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path],
|
||||
}
|
||||
if loaded != nil {
|
||||
info.GoVersion = loaded.goVersion[m.Path]
|
||||
}
|
||||
|
||||
if cfg.BuildGetmode == "vendor" {
|
||||
info.Dir = filepath.Join(ModRoot, "vendor", m.Path)
|
||||
@ -139,8 +146,9 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
||||
|
||||
if r := Replacement(m); r.Path != "" {
|
||||
info.Replace = &modinfo.ModulePublic{
|
||||
Path: r.Path,
|
||||
Version: r.Version,
|
||||
Path: r.Path,
|
||||
Version: r.Version,
|
||||
GoVersion: info.GoVersion,
|
||||
}
|
||||
if r.Version == "" {
|
||||
if filepath.IsAbs(r.Path) {
|
||||
|
@ -358,7 +358,8 @@ type loader struct {
|
||||
pkgCache *par.Cache // map from string to *loadPkg
|
||||
|
||||
// computed at end of iterations
|
||||
direct map[string]bool // imported directly by main module
|
||||
direct map[string]bool // imported directly by main module
|
||||
goVersion map[string]string // go version recorded in each module
|
||||
}
|
||||
|
||||
func newLoader() *loader {
|
||||
@ -399,7 +400,8 @@ var errMissing = errors.New("cannot find package")
|
||||
// which must call add(path) with the import path of each root package.
|
||||
func (ld *loader) load(roots func() []string) {
|
||||
var err error
|
||||
buildList, err = mvs.BuildList(Target, Reqs())
|
||||
reqs := Reqs()
|
||||
buildList, err = mvs.BuildList(Target, reqs)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
@ -445,7 +447,8 @@ func (ld *loader) load(roots func() []string) {
|
||||
}
|
||||
|
||||
// Recompute buildList with all our additions.
|
||||
buildList, err = mvs.BuildList(Target, Reqs())
|
||||
reqs = Reqs()
|
||||
buildList, err = mvs.BuildList(Target, reqs)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
@ -464,6 +467,13 @@ func (ld *loader) load(roots func() []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add Go versions, computed during walk.
|
||||
ld.goVersion = make(map[string]string)
|
||||
for _, m := range buildList {
|
||||
v, _ := reqs.(*mvsReqs).versions.Load(m)
|
||||
ld.goVersion[m.Path], _ = v.(string)
|
||||
}
|
||||
|
||||
// Mix in direct markings (really, lack of indirect markings)
|
||||
// from go.mod, unless we scanned the whole module
|
||||
// and can therefore be sure we know better than go.mod.
|
||||
@ -670,6 +680,7 @@ func Replacement(mod module.Version) module.Version {
|
||||
type mvsReqs struct {
|
||||
buildList []module.Version
|
||||
cache par.Cache
|
||||
versions sync.Map
|
||||
}
|
||||
|
||||
// Reqs returns the current module requirement graph.
|
||||
@ -745,11 +756,21 @@ func readVendorList() {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
|
||||
var list []module.Version
|
||||
for _, r := range f.Require {
|
||||
list = append(list, r.Mod)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
|
||||
if mod == Target {
|
||||
if modFile.Go != nil {
|
||||
r.versions.LoadOrStore(mod, modFile.Go.Version)
|
||||
}
|
||||
var list []module.Version
|
||||
list = append(list, r.buildList[1:]...)
|
||||
return list, nil
|
||||
return append(list, r.buildList[1:]...), nil
|
||||
}
|
||||
|
||||
if cfg.BuildGetmode == "vendor" {
|
||||
@ -778,11 +799,10 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
|
||||
base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err)
|
||||
return nil, ErrRequire
|
||||
}
|
||||
var list []module.Version
|
||||
for _, r := range f.Require {
|
||||
list = append(list, r.Mod)
|
||||
if f.Go != nil {
|
||||
r.versions.LoadOrStore(mod, f.Go.Version)
|
||||
}
|
||||
return list, nil
|
||||
return r.modFileToList(f), nil
|
||||
}
|
||||
mod = repl
|
||||
}
|
||||
@ -815,12 +835,11 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
|
||||
base.Errorf("go: %s@%s: parsing go.mod: unexpected module path %q", mod.Path, mod.Version, mpath)
|
||||
return nil, ErrRequire
|
||||
}
|
||||
|
||||
var list []module.Version
|
||||
for _, req := range f.Require {
|
||||
list = append(list, req.Mod)
|
||||
if f.Go != nil {
|
||||
r.versions.LoadOrStore(mod, f.Go.Version)
|
||||
}
|
||||
return list, nil
|
||||
|
||||
return r.modFileToList(f), nil
|
||||
}
|
||||
|
||||
// ErrRequire is the sentinel error returned when Require encounters problems.
|
||||
|
@ -320,6 +320,27 @@ func (b *Builder) needCgoHdr(a *Action) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// allowedVersion reports whether the version v is an allowed version of go
|
||||
// (one that we can compile).
|
||||
// v is known to be of the form "1.23".
|
||||
func allowedVersion(v string) bool {
|
||||
// Special case: no requirement.
|
||||
if v == "" {
|
||||
return true
|
||||
}
|
||||
// Special case "1.0" means "go1", which is OK.
|
||||
if v == "1.0" {
|
||||
return true
|
||||
}
|
||||
// Otherwise look through release tags of form "go1.23" for one that matches.
|
||||
for _, tag := range cfg.BuildContext.ReleaseTags {
|
||||
if strings.HasPrefix(tag, "go") && tag[2:] == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
needBuild uint32 = 1 << iota
|
||||
needCgoHdr
|
||||
@ -414,6 +435,10 @@ func (b *Builder) build(a *Action) (err error) {
|
||||
return fmt.Errorf("missing or invalid binary-only package; expected file %q", a.Package.Target)
|
||||
}
|
||||
|
||||
if p.Module != nil && !allowedVersion(p.Module.GoVersion) {
|
||||
return fmt.Errorf("module requires Go %s", p.Module.GoVersion)
|
||||
}
|
||||
|
||||
if err := b.Mkdir(a.Objdir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
61
src/cmd/go/testdata/script/mod_go_version.txt
vendored
Normal file
61
src/cmd/go/testdata/script/mod_go_version.txt
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# Test support for declaring needed Go version in module.
|
||||
|
||||
env GO111MODULE=on
|
||||
|
||||
go list
|
||||
! go build
|
||||
stderr 'module requires Go 1.999'
|
||||
go build sub.1
|
||||
! go build badsub.1
|
||||
stderr 'module requires Go 1.11111'
|
||||
|
||||
go build versioned.1
|
||||
go mod -require versioned.1@v1.1.0
|
||||
! go build versioned.1
|
||||
stderr 'module requires Go 1.99999'
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
go 1.999
|
||||
require (
|
||||
sub.1 v1.0.0
|
||||
badsub.1 v1.0.0
|
||||
versioned.1 v1.0.0
|
||||
)
|
||||
replace (
|
||||
sub.1 => ./sub
|
||||
badsub.1 => ./badsub
|
||||
versioned.1 v1.0.0 => ./versioned1
|
||||
versioned.1 v1.1.0 => ./versioned2
|
||||
)
|
||||
|
||||
-- x.go --
|
||||
package x
|
||||
|
||||
-- sub/go.mod --
|
||||
module m
|
||||
go 1.11
|
||||
|
||||
-- sub/x.go --
|
||||
package x
|
||||
|
||||
-- badsub/go.mod --
|
||||
module m
|
||||
go 1.11111
|
||||
|
||||
-- badsub/x.go --
|
||||
package x
|
||||
|
||||
-- versioned1/go.mod --
|
||||
module versioned
|
||||
go 1.0
|
||||
|
||||
-- versioned1/x.go --
|
||||
package x
|
||||
|
||||
-- versioned2/go.mod --
|
||||
module versioned
|
||||
go 1.99999
|
||||
|
||||
-- versioned2/x.go --
|
||||
package x
|
Loading…
Reference in New Issue
Block a user