diff --git a/cmd/gorebuild/build.go b/cmd/gorebuild/build.go index 7b1a59d594..480aa9b030 100644 --- a/cmd/gorebuild/build.go +++ b/cmd/gorebuild/build.go @@ -6,6 +6,7 @@ package main import ( "fmt" + goversion "go/version" "os" "os/exec" "path/filepath" @@ -17,24 +18,27 @@ import ( // for the named version of Go. If the version needs no bootstrap // (that is, if it's before Go 1.5), BootstrapVersion returns an empty version. func BootstrapVersion(version string) (string, error) { - if Compare(version, Go(1, 5)) < 0 { + // go1 returns the version string for Go 1.N ("go1.N"). + go1 := func(N int) string { return fmt.Sprintf("go1.%d", N) } + + if goversion.Compare(version, go1(5)) < 0 { return "", nil } - if Compare(version, Go(1, 20)) < 0 { - return Go(1, 4), nil + if goversion.Compare(version, go1(20)) < 0 { + return go1(4), nil } - if Compare(version, Go(1, 22)) < 0 { - return Go(1, 17), nil + if goversion.Compare(version, go1(22)) < 0 { + return go1(17), nil } - if Compare(version, Go(1, 1000)) > 0 { + if goversion.Compare(version, go1(1000)) > 0 { return "", fmt.Errorf("invalid version %q", version) } for i := 24; ; i += 2 { - if Compare(version, Go(1, i)) < 0 { + if goversion.Compare(version, go1(i)) < 0 { // 1.24 will switch to 1.22; before that we used 1.20 // 1.26 will switch to 1.24; before that we used 1.22 // ... - return Go(1, i-4), nil + return go1(i - 4), nil } } } diff --git a/cmd/gorebuild/gover.go b/cmd/gorebuild/gover.go deleted file mode 100644 index 9f85f14fc0..0000000000 --- a/cmd/gorebuild/gover.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This code is copied from cmd/go/internal/gover. -// Perhaps some day a go/version package will provide this functionality. - -package main - -import "fmt" - -// Go returns the version string for Go N.M ("goN.M") -func Go(N, M int) string { - return fmt.Sprintf("go%d.%d", N, M) -} - -// Compare returns -1, 0, or +1 depending on whether -// x < y, x == y, or x > y, interpreted as toolchain versions. -// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". -// Malformed versions compare less than well-formed versions and equal to each other. -// The language version "go1.21" compares less than the release candidate and eventual releases "go1.21rc1" and "go1.21.0". -func Compare(x, y string) int { - vx := parse(x) - vy := parse(y) - - if c := cmpInt(vx.major, vy.major); c != 0 { - return c - } - if c := cmpInt(vx.minor, vy.minor); c != 0 { - return c - } - if c := cmpInt(vx.patch, vy.patch); c != 0 { - return c - } - if vx.kind != vy.kind { - if vx.kind < vy.kind { - return -1 - } - return +1 - } - if c := cmpInt(vx.pre, vy.pre); c != 0 { - return c - } - return 0 -} - -// IsValid reports whether the version x is valid. -func IsValid(x string) bool { - return parse(x) != version{} -} - -// parse parses the Go version string x into a version. -// It returns the zero version if x is malformed. -func parse(x string) version { - var v version - // Parse major version. - if len(x) < 2 || x[:2] != "go" { - return version{} - } - x = x[2:] - var ok bool - v.major, x, ok = cutInt(x) - if !ok { - return version{} - } - if x == "" { - // Interpret "1" as "1.0.0". - v.minor = "0" - v.patch = "0" - return v - } - - // Parse . before minor version. - if x[0] != '.' { - return version{} - } - - // Parse minor version. - v.minor, x, ok = cutInt(x[1:]) - if !ok { - return version{} - } - if x == "" { - // Patch missing is same as "0" for older versions. - // Starting in Go 1.21, patch missing is different from explicit .0. - if cmpInt(v.minor, "21") < 0 { - v.patch = "0" - } - return v - } - - // Parse patch if present. - if x[0] == '.' { - v.patch, x, ok = cutInt(x[1:]) - if !ok || x != "" { - // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). - // Allowing them would be a bit confusing because we already have: - // 1.21 < 1.21rc1 - // But a prerelease of a patch would have the opposite effect: - // 1.21.3rc1 < 1.21.3 - // We've never needed them before, so let's not start now. - return version{} - } - return v - } - - // Parse prerelease. - i := 0 - for i < len(x) && (x[i] < '0' || '9' < x[i]) { - if x[i] < 'a' || 'z' < x[i] { - return version{} - } - i++ - } - if i == 0 { - return version{} - } - v.kind, x = x[:i], x[i:] - if x == "" { - return v - } - v.pre, x, ok = cutInt(x) - if !ok || x != "" { - return version{} - } - - return v -} - -// cutInt scans the leading decimal number at the start of x to an integer -// and returns that value and the rest of the string. -func cutInt(x string) (n, rest string, ok bool) { - i := 0 - for i < len(x) && '0' <= x[i] && x[i] <= '9' { - i++ - } - if i == 0 || x[0] == '0' && i != 1 { - return "", "", false - } - return x[:i], x[i:], true -} - -// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. -// (Copied from golang.org/x/mod/semver's compareInt.) -func cmpInt(x, y string) int { - if x == y { - return 0 - } - if len(x) < len(y) { - return -1 - } - if len(x) > len(y) { - return +1 - } - if x < y { - return -1 - } else { - return +1 - } -} - -// A version is a parsed Go version: major[.minor[.patch]][kind[pre]] -// The numbers are the original decimal strings to avoid integer overflows -// and since there is very little actual math. (Probably overflow doesn't matter in practice, -// but at the time this code was written, there was an existing test that used -// go1.99999999999, which does not fit in an int on 32-bit platforms. -// The "big decimal" representation avoids the problem entirely.) -type version struct { - major string // decimal - minor string // decimal or "" - patch string // decimal or "" - kind string // "", "alpha", "beta", "rc" - pre string // decimal or "" -} diff --git a/cmd/gorebuild/report.go b/cmd/gorebuild/report.go index 4899a77c9a..ddd6a4c517 100644 --- a/cmd/gorebuild/report.go +++ b/cmd/gorebuild/report.go @@ -7,6 +7,7 @@ package main import ( "bytes" "fmt" + goversion "go/version" "log" "os" "path/filepath" @@ -243,7 +244,7 @@ func Run(args []string) *Report { if r.Log.Status != FAIL { r.Log.Status = PASS } - sort.Slice(r.Releases, func(i, j int) bool { return Compare(r.Releases[i].Version, r.Releases[j].Version) > 0 }) + sort.Slice(r.Releases, func(i, j int) bool { return goversion.Compare(r.Releases[i].Version, r.Releases[j].Version) > 0 }) for _, rel := range r.Releases { if rel.Log.Status != FAIL { rel.Log.Status = PASS diff --git a/internal/relui/workflows.go b/internal/relui/workflows.go index d59e07f837..d54733e5ba 100644 --- a/internal/relui/workflows.go +++ b/internal/relui/workflows.go @@ -15,6 +15,7 @@ import ( "encoding/json" "errors" "fmt" + goversion "go/version" "io" "io/fs" "net/http" @@ -1644,7 +1645,7 @@ func (tasks *BuildReleaseTasks) publishArtifacts(ctx *wf.TaskContext, version st if a.Target.GOOS == "linux" && a.Target.GOARCH == "arm" && slices.Contains(a.Target.ExtraEnv, "GOARM=6") { f.Arch = "armv6l" } - if task.CompareGoVersions(version, "go1.23") == -1 { // TODO: Delete this after Go 1.24.0 is out and this becomes dead code. + if goversion.Compare(version, "go1.23") == -1 { // TODO: Delete this after Go 1.24.0 is out and this becomes dead code. // Due to an oversight, we've been inadvertently setting the "arch" field // of published download metadata to "armv6l" for all arm ports, not just // linux/arm port as intended. Keep doing it for the rest of Go 1.22/1.21 diff --git a/internal/task/announce.go b/internal/task/announce.go index 873aa65fcd..b54c630704 100644 --- a/internal/task/announce.go +++ b/internal/task/announce.go @@ -9,6 +9,7 @@ import ( "embed" "errors" "fmt" + goversion "go/version" "io" "mime" "net/http" @@ -456,7 +457,7 @@ var announceTmpl = template.Must(template.New("").Funcs(template.FuncMap{ }, // atLeast reports whether v1 >= v2. "atLeast": func(v1, v2 string) bool { - return CompareGoVersions(v1, v2) >= 0 + return goversion.Compare(v1, v2) >= 0 }, // build extracts the pre-release build number of a valid Go version. // For example, build("go1.19beta2") == "2". diff --git a/internal/task/gover.go b/internal/task/gover.go deleted file mode 100644 index 70d946085b..0000000000 --- a/internal/task/gover.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This code is copied from cmd/go/internal/gover. -// TODO: remove this when updated to go1.22 which -// includes the go/version package. - -package task - -// CompareGoVersions returns -1, 0, or +1 depending on whether -// x < y, x == y, or x > y, interpreted as toolchain versions. -// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". -// Malformed versions compare less than well-formed versions and equal to each other. -// The language version "go1.21" compares less than the release candidate and eventual releases "go1.21rc1" and "go1.21.0". -func CompareGoVersions(x, y string) int { - vx := parse(x) - vy := parse(y) - - if c := cmpInt(vx.major, vy.major); c != 0 { - return c - } - if c := cmpInt(vx.minor, vy.minor); c != 0 { - return c - } - if c := cmpInt(vx.patch, vy.patch); c != 0 { - return c - } - if vx.kind != vy.kind { - if vx.kind < vy.kind { - return -1 - } - return +1 - } - if c := cmpInt(vx.pre, vy.pre); c != 0 { - return c - } - return 0 -} - -// IsValid reports whether the version x is valid. -func IsValid(x string) bool { - return parse(x) != goVersion{} -} - -// parse parses the Go version string x into a version. -// It returns the zero version if x is malformed. -func parse(x string) goVersion { - var v goVersion - // Parse major version. - if len(x) < 2 || x[:2] != "go" { - return goVersion{} - } - x = x[2:] - var ok bool - v.major, x, ok = cutInt(x) - if !ok { - return goVersion{} - } - if x == "" { - // Interpret "1" as "1.0.0". - v.minor = "0" - v.patch = "0" - return v - } - - // Parse . before minor version. - if x[0] != '.' { - return goVersion{} - } - - // Parse minor version. - v.minor, x, ok = cutInt(x[1:]) - if !ok { - return goVersion{} - } - if x == "" { - // Patch missing is same as "0" for older versions. - // Starting in Go 1.21, patch missing is different from explicit .0. - if cmpInt(v.minor, "21") < 0 { - v.patch = "0" - } - return v - } - - // Parse patch if present. - if x[0] == '.' { - v.patch, x, ok = cutInt(x[1:]) - if !ok || x != "" { - // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). - // Allowing them would be a bit confusing because we already have: - // 1.21 < 1.21rc1 - // But a prerelease of a patch would have the opposite effect: - // 1.21.3rc1 < 1.21.3 - // We've never needed them before, so let's not start now. - return goVersion{} - } - return v - } - - // Parse prerelease. - i := 0 - for i < len(x) && (x[i] < '0' || '9' < x[i]) { - if x[i] < 'a' || 'z' < x[i] { - return goVersion{} - } - i++ - } - if i == 0 { - return goVersion{} - } - v.kind, x = x[:i], x[i:] - if x == "" { - return v - } - v.pre, x, ok = cutInt(x) - if !ok || x != "" { - return goVersion{} - } - - return v -} - -// cutInt scans the leading decimal number at the start of x to an integer -// and returns that value and the rest of the string. -func cutInt(x string) (n, rest string, ok bool) { - i := 0 - for i < len(x) && '0' <= x[i] && x[i] <= '9' { - i++ - } - if i == 0 || x[0] == '0' && i != 1 { - return "", "", false - } - return x[:i], x[i:], true -} - -// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. -// (Copied from golang.org/x/mod/semver's compareInt.) -func cmpInt(x, y string) int { - if x == y { - return 0 - } - if len(x) < len(y) { - return -1 - } - if len(x) > len(y) { - return +1 - } - if x < y { - return -1 - } else { - return +1 - } -} - -// A goVersion is a parsed Go version: major[.minor[.patch]][kind[pre]] -// The numbers are the original decimal strings to avoid integer overflows -// and since there is very little actual math. (Probably overflow doesn't matter in practice, -// but at the time this code was written, there was an existing test that used -// go1.99999999999, which does not fit in an int on 32-bit platforms. -// The "big decimal" representation avoids the problem entirely.) -type goVersion struct { - major string // decimal - minor string // decimal or "" - patch string // decimal or "" - kind string // "", "alpha", "beta", "rc" - pre string // decimal or "" -} diff --git a/internal/task/tweet.go b/internal/task/tweet.go index ac9639d648..55975e7266 100644 --- a/internal/task/tweet.go +++ b/internal/task/tweet.go @@ -9,6 +9,7 @@ import ( "context" "encoding/json" "fmt" + goversion "go/version" "image" "image/color" "image/draw" @@ -352,7 +353,7 @@ func tweetImage(published Published, rnd *rand.Rand) (imagePNG []byte, imageText return nil, "", err } goarch := a.GOARCH() - if CompareGoVersions(a.Version, "go1.23") == -1 && a.OS != "linux" { // TODO: Delete this after Go 1.24.0 is out and this becomes dead code. + if goversion.Compare(a.Version, "go1.23") == -1 && a.OS != "linux" { // TODO: Delete this after Go 1.24.0 is out and this becomes dead code. goarch = strings.TrimSuffix(goarch, "v6l") } var buf bytes.Buffer diff --git a/internal/task/updateproxytestrepo.go b/internal/task/updateproxytestrepo.go index 67c7ccdd70..3bf2c4e185 100644 --- a/internal/task/updateproxytestrepo.go +++ b/internal/task/updateproxytestrepo.go @@ -6,6 +6,7 @@ package task import ( "fmt" + goversion "go/version" "os" "path/filepath" "strings" @@ -38,7 +39,7 @@ func (t *UpdateProxyTestRepoTasks) UpdateProxyTestRepo(ctx *wf.TaskContext, publ } // If the published version is lower than the current go.mod version, don't update. // If we could parse the go.mod file, assume we should update. - if f.Go != nil && CompareGoVersions(published.Version, "go"+f.Go.Version) < 0 { + if f.Go != nil && goversion.Compare(published.Version, "go"+f.Go.Version) < 0 { return "no update", nil }