goversion: parse new version format and simplify version representation (#3470)
Add parsing for the new version format with toolchain (X.Y.Z-something) and simplify internal representation of versions so that revision, beta version and rc version are all represented on a single field with rc and beta versions being negative numbers (this limits rc versions to a maximum of 1000 which will not be a problem in practice).
This commit is contained in:
parent
b07ef66fe4
commit
53688a102f
@ -24,20 +24,19 @@ func Compatible(producer string, warnonly bool) error {
|
||||
if ver.IsDevel() {
|
||||
return nil
|
||||
}
|
||||
verstr := fmt.Sprintf("%d.%d.%d", ver.Major, ver.Minor, ver.Rev)
|
||||
if !ver.AfterOrEqual(GoVersion{MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor, -1, 0, 0, ""}) {
|
||||
if !ver.AfterOrEqual(GoVersion{MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor, betaRev(0), "", ""}) {
|
||||
if warnonly {
|
||||
logflags.WriteError(fmt.Sprintf(goTooOldWarn, verstr))
|
||||
logflags.WriteError(fmt.Sprintf(goTooOldWarn, ver.String()))
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(goTooOldErr, verstr)
|
||||
return fmt.Errorf(goTooOldErr, ver.String())
|
||||
}
|
||||
if ver.AfterOrEqual(GoVersion{MaxSupportedVersionOfGoMajor, MaxSupportedVersionOfGoMinor + 1, -1, 0, 0, ""}) {
|
||||
if ver.AfterOrEqual(GoVersion{MaxSupportedVersionOfGoMajor, MaxSupportedVersionOfGoMinor + 1, betaRev(0), "", ""}) {
|
||||
if warnonly {
|
||||
logflags.WriteError(fmt.Sprintf(dlvTooOldWarn, verstr))
|
||||
logflags.WriteError(fmt.Sprintf(dlvTooOldWarn, ver.String()))
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(dlvTooOldErr, verstr)
|
||||
return fmt.Errorf(dlvTooOldErr, ver.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package goversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -10,16 +11,28 @@ import (
|
||||
// the Go compiler version used to compile
|
||||
// the target binary.
|
||||
type GoVersion struct {
|
||||
Major int
|
||||
Minor int
|
||||
Rev int
|
||||
Beta int
|
||||
RC int
|
||||
Proposal string
|
||||
Major int
|
||||
Minor int
|
||||
Rev int // revision number or negative number for beta and rc releases
|
||||
Proposal string
|
||||
Toolchain string
|
||||
}
|
||||
|
||||
const (
|
||||
betaStart = -1000
|
||||
betaEnd = -2000
|
||||
)
|
||||
|
||||
func betaRev(beta int) int {
|
||||
return beta + betaEnd
|
||||
}
|
||||
|
||||
func rcRev(rc int) int {
|
||||
return rc + betaStart
|
||||
}
|
||||
|
||||
var (
|
||||
GoVer18Beta = GoVersion{1, 8, -1, 0, 0, ""}
|
||||
GoVer18Beta = GoVersion{1, 8, betaRev(0), "", ""}
|
||||
)
|
||||
|
||||
// Parse parses a go version string
|
||||
@ -28,7 +41,7 @@ func Parse(ver string) (GoVersion, bool) {
|
||||
var err1, err2, err3 error
|
||||
|
||||
if strings.HasPrefix(ver, "devel") {
|
||||
return GoVersion{-1, 0, 0, 0, 0, ""}, true
|
||||
return GoVersion{-1, 0, 0, "", ""}, true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ver, "go") {
|
||||
@ -40,15 +53,22 @@ func Parse(ver string) (GoVersion, bool) {
|
||||
var vr []string
|
||||
|
||||
if vr = strings.SplitN(v[1], "beta", 2); len(vr) == 2 {
|
||||
r.Beta, err3 = strconv.Atoi(vr[1])
|
||||
// old beta releases goX.YbetaZ
|
||||
var beta int
|
||||
beta, err3 = strconv.Atoi(vr[1])
|
||||
r.Rev = betaRev(beta)
|
||||
} else if vr = strings.SplitN(v[1], "b", 2); len(vr) == 2 {
|
||||
// old boringcrypto version goX.YbZ
|
||||
if _, err := strconv.Atoi(vr[1]); err != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
} else {
|
||||
vr = strings.SplitN(v[1], "rc", 2)
|
||||
if len(vr) == 2 {
|
||||
r.RC, err3 = strconv.Atoi(vr[1])
|
||||
// rc release goX.YrcZ
|
||||
var rc int
|
||||
rc, err3 = strconv.Atoi(vr[1])
|
||||
r.Rev = rcRev(rc)
|
||||
} else {
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
if err2 != nil {
|
||||
@ -58,8 +78,9 @@ func Parse(ver string) (GoVersion, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// old major release (if none of the options above apply) goX.Y
|
||||
|
||||
r.Minor, err2 = strconv.Atoi(vr[0])
|
||||
r.Rev = -1
|
||||
r.Proposal = ""
|
||||
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
@ -73,10 +94,15 @@ func Parse(ver string) (GoVersion, bool) {
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
|
||||
vr := strings.SplitN(v[2], "b", 2)
|
||||
if len(vr) == 2 {
|
||||
if vr := strings.SplitN(v[2], "-", 2); len(vr) == 2 {
|
||||
// minor version with toolchain modifier goX.Y.Z-anything
|
||||
r.Rev, err3 = strconv.Atoi(vr[0])
|
||||
r.Toolchain = vr[1]
|
||||
} else if vr := strings.SplitN(v[2], "b", 2); len(vr) == 2 {
|
||||
// old boringcrypto version goX.Y.ZbW
|
||||
r.Rev, err3 = strconv.Atoi(vr[0])
|
||||
} else {
|
||||
// minor version goX.Y.Z
|
||||
r.Rev, err3 = strconv.Atoi(v[2])
|
||||
}
|
||||
|
||||
@ -89,6 +115,8 @@ func Parse(ver string) (GoVersion, bool) {
|
||||
|
||||
case 4:
|
||||
|
||||
// old proposal release goX.Y.Z.anything
|
||||
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
r.Rev, err3 = strconv.Atoi(v[2])
|
||||
@ -128,14 +156,6 @@ func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Beta < b.Beta {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.RC < b.RC {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -145,6 +165,28 @@ func (v *GoVersion) IsDevel() bool {
|
||||
return v.Major < 0
|
||||
}
|
||||
|
||||
func (v *GoVersion) String() string {
|
||||
switch {
|
||||
case v.Rev < betaStart:
|
||||
// beta version
|
||||
return fmt.Sprintf("go%d.%dbeta%d", v.Major, v.Minor, v.Rev-betaEnd)
|
||||
case v.Rev < 0:
|
||||
// rc version
|
||||
return fmt.Sprintf("go%d.%drc%d", v.Major, v.Minor, v.Rev-betaStart)
|
||||
case v.Proposal != "":
|
||||
// with proposal
|
||||
return fmt.Sprintf("go%d.%d.%d.%s", v.Major, v.Minor, v.Rev, v.Proposal)
|
||||
case v.Rev == 0 && v.Minor < 21:
|
||||
// old major version
|
||||
return fmt.Sprintf("go%d.%d", v.Major, v.Minor)
|
||||
case v.Toolchain != "":
|
||||
return fmt.Sprintf("go%d.%d.%d-%s", v.Major, v.Minor, v.Rev, v.Toolchain)
|
||||
default:
|
||||
// post go1.21 major version or minor version
|
||||
return fmt.Sprintf("go%d.%d.%d", v.Major, v.Minor, v.Rev)
|
||||
}
|
||||
}
|
||||
|
||||
const goVersionPrefix = "go version "
|
||||
|
||||
// Installed runs "go version" and parses the output
|
||||
@ -178,7 +220,7 @@ func VersionAfterOrEqualRev(version string, major, minor, rev int) bool {
|
||||
if ver.IsDevel() {
|
||||
return true
|
||||
}
|
||||
return ver.AfterOrEqual(GoVersion{major, minor, rev, 0, 0, ""})
|
||||
return ver.AfterOrEqual(GoVersion{major, minor, rev, "", ""})
|
||||
}
|
||||
|
||||
const producerVersionPrefix = "Go cmd/compile "
|
||||
@ -190,7 +232,7 @@ func ProducerAfterOrEqual(producer string, major, minor int) bool {
|
||||
if ver.IsDevel() {
|
||||
return true
|
||||
}
|
||||
return ver.AfterOrEqual(GoVersion{major, minor, -1, 0, 0, ""})
|
||||
return ver.AfterOrEqual(GoVersion{major, minor, 0, "", ""})
|
||||
}
|
||||
|
||||
func ParseProducer(producer string) GoVersion {
|
||||
|
||||
@ -5,27 +5,51 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func versionAfterOrEqual(t *testing.T, verStr string, ver GoVersion) {
|
||||
func parseVer(t *testing.T, verStr string) GoVersion {
|
||||
pver, ok := Parse(verStr)
|
||||
if !ok {
|
||||
t.Fatalf("Could not parse version string <%s>", verStr)
|
||||
}
|
||||
return pver
|
||||
}
|
||||
|
||||
func versionAfterOrEqual(t *testing.T, verStr string, ver GoVersion) {
|
||||
t.Helper()
|
||||
pver := parseVer(t, verStr)
|
||||
if !pver.AfterOrEqual(ver) {
|
||||
t.Fatalf("Version <%s> parsed as %v not after %v", verStr, pver, ver)
|
||||
}
|
||||
t.Logf("version string <%s> → %v", verStr, ver)
|
||||
}
|
||||
|
||||
func TestParseVersionString(t *testing.T) {
|
||||
versionAfterOrEqual(t, "go1.4", GoVersion{1, 4, 0, 0, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.5.0", GoVersion{1, 5, 0, 0, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.4.2", GoVersion{1, 4, 2, 0, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.5beta2", GoVersion{1, 5, -1, 2, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.5rc2", GoVersion{1, 5, -1, 0, 2, ""})
|
||||
versionAfterOrEqual(t, "go1.6.1 (appengine-1.9.37)", GoVersion{1, 6, 1, 0, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.8.1.typealias", GoVersion{1, 6, 1, 0, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.8b1", GoVersion{1, 8, -1, 0, 0, ""})
|
||||
versionAfterOrEqual(t, "go1.16.4b7", GoVersion{1, 16, 4, 0, 0, ""})
|
||||
func versionAfterOrEqual2(t *testing.T, verStr1, verStr2 string) {
|
||||
t.Helper()
|
||||
pver1 := parseVer(t, verStr1)
|
||||
pver2 := parseVer(t, verStr2)
|
||||
if !pver1.AfterOrEqual(pver2) {
|
||||
t.Fatalf("Version <%s> %#v not after or equal to <%s> %#v", verStr1, pver1, verStr2, pver2)
|
||||
}
|
||||
}
|
||||
|
||||
func versionEqual(t *testing.T, verStr string, ver GoVersion) {
|
||||
t.Helper()
|
||||
pver := parseVer(t, verStr)
|
||||
if pver != ver {
|
||||
t.Fatalf("Version <%s> parsed as %v not equal to %v", verStr, pver, ver)
|
||||
}
|
||||
t.Logf("version string <%s> → %v", verStr, ver)
|
||||
}
|
||||
|
||||
func TestParseVersionStringAfterOrEqual(t *testing.T) {
|
||||
versionAfterOrEqual(t, "go1.4", GoVersion{1, 4, 0, "", ""})
|
||||
versionAfterOrEqual(t, "go1.5.0", GoVersion{1, 5, 0, "", ""})
|
||||
versionAfterOrEqual(t, "go1.4.2", GoVersion{1, 4, 2, "", ""})
|
||||
versionAfterOrEqual(t, "go1.5beta2", GoVersion{1, 5, betaRev(2), "", ""})
|
||||
versionAfterOrEqual(t, "go1.5rc2", GoVersion{1, 5, rcRev(2), "", ""})
|
||||
versionAfterOrEqual(t, "go1.6.1 (appengine-1.9.37)", GoVersion{1, 6, 1, "", ""})
|
||||
versionAfterOrEqual(t, "go1.8.1.typealias", GoVersion{1, 6, 1, "", ""})
|
||||
versionAfterOrEqual(t, "go1.8b1", GoVersion{1, 8, 0, "", ""})
|
||||
versionAfterOrEqual(t, "go1.16.4b7", GoVersion{1, 16, 4, "", ""})
|
||||
ver, ok := Parse("devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64")
|
||||
if !ok {
|
||||
t.Fatalf("Could not parse devel version string")
|
||||
@ -33,6 +57,43 @@ func TestParseVersionString(t *testing.T) {
|
||||
if !ver.IsDevel() {
|
||||
t.Fatalf("Devel version string not correctly recognized")
|
||||
}
|
||||
|
||||
versionAfterOrEqual2(t, "go1.16", "go1.16b1")
|
||||
versionAfterOrEqual2(t, "go1.16", "go1.16rc1")
|
||||
versionAfterOrEqual2(t, "go1.16rc1", "go1.16beta1")
|
||||
versionAfterOrEqual2(t, "go1.16beta2", "go1.16beta1")
|
||||
versionAfterOrEqual2(t, "go1.16rc10", "go1.16rc8")
|
||||
}
|
||||
|
||||
func TestParseVersionStringEqual(t *testing.T) {
|
||||
versionEqual(t, "go1.4", GoVersion{1, 4, 0, "", ""})
|
||||
versionEqual(t, "go1.5.0", GoVersion{1, 5, 0, "", ""})
|
||||
versionEqual(t, "go1.4.2", GoVersion{1, 4, 2, "", ""})
|
||||
versionEqual(t, "go1.5beta2", GoVersion{1, 5, betaRev(2), "", ""})
|
||||
versionEqual(t, "go1.5rc2", GoVersion{1, 5, rcRev(2), "", ""})
|
||||
versionEqual(t, "go1.6.1 (appengine-1.9.37)", GoVersion{1, 6, 1, "", ""})
|
||||
versionEqual(t, "go1.8.1.typealias", GoVersion{1, 8, 1, "typealias", ""})
|
||||
versionEqual(t, "go1.8b1", GoVersion{1, 8, 0, "", ""})
|
||||
versionEqual(t, "go1.16.4b7", GoVersion{1, 16, 4, "", ""})
|
||||
versionEqual(t, "go1.21.1-something", GoVersion{1, 21, 1, "", "something"})
|
||||
versionEqual(t, "devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64", GoVersion{Major: -1})
|
||||
}
|
||||
|
||||
func TestRoundtrip(t *testing.T) {
|
||||
for _, verStr := range []string{
|
||||
"go1.4",
|
||||
"go1.4.2",
|
||||
"go1.5beta2",
|
||||
"go1.5rc2",
|
||||
"go1.8.1.typealias",
|
||||
"go1.21.1-something",
|
||||
"go1.21.0",
|
||||
} {
|
||||
pver := parseVer(t, verStr)
|
||||
if pver.String() != verStr {
|
||||
t.Fatalf("roundtrip mismatch <%s> -> %#v -> <%s>", verStr, pver, pver.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstalled(t *testing.T) {
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
func TestScopeWithEscapedVariable(t *testing.T) {
|
||||
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1, Beta: 3}) {
|
||||
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9}) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user