golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/gorebuild/gover.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This code is copied from cmd/go/internal/gover. 6 // Perhaps some day a go/version package will provide this functionality. 7 8 package main 9 10 import "fmt" 11 12 // Go returns the version string for Go N.M ("goN.M") 13 func Go(N, M int) string { 14 return fmt.Sprintf("go%d.%d", N, M) 15 } 16 17 // Compare returns -1, 0, or +1 depending on whether 18 // x < y, x == y, or x > y, interpreted as toolchain versions. 19 // The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". 20 // Malformed versions compare less than well-formed versions and equal to each other. 21 // The language version "go1.21" compares less than the release candidate and eventual releases "go1.21rc1" and "go1.21.0". 22 func Compare(x, y string) int { 23 vx := parse(x) 24 vy := parse(y) 25 26 if c := cmpInt(vx.major, vy.major); c != 0 { 27 return c 28 } 29 if c := cmpInt(vx.minor, vy.minor); c != 0 { 30 return c 31 } 32 if c := cmpInt(vx.patch, vy.patch); c != 0 { 33 return c 34 } 35 if vx.kind != vy.kind { 36 if vx.kind < vy.kind { 37 return -1 38 } 39 return +1 40 } 41 if c := cmpInt(vx.pre, vy.pre); c != 0 { 42 return c 43 } 44 return 0 45 } 46 47 // IsValid reports whether the version x is valid. 48 func IsValid(x string) bool { 49 return parse(x) != version{} 50 } 51 52 // parse parses the Go version string x into a version. 53 // It returns the zero version if x is malformed. 54 func parse(x string) version { 55 var v version 56 // Parse major version. 57 if len(x) < 2 || x[:2] != "go" { 58 return version{} 59 } 60 x = x[2:] 61 var ok bool 62 v.major, x, ok = cutInt(x) 63 if !ok { 64 return version{} 65 } 66 if x == "" { 67 // Interpret "1" as "1.0.0". 68 v.minor = "0" 69 v.patch = "0" 70 return v 71 } 72 73 // Parse . before minor version. 74 if x[0] != '.' { 75 return version{} 76 } 77 78 // Parse minor version. 79 v.minor, x, ok = cutInt(x[1:]) 80 if !ok { 81 return version{} 82 } 83 if x == "" { 84 // Patch missing is same as "0" for older versions. 85 // Starting in Go 1.21, patch missing is different from explicit .0. 86 if cmpInt(v.minor, "21") < 0 { 87 v.patch = "0" 88 } 89 return v 90 } 91 92 // Parse patch if present. 93 if x[0] == '.' { 94 v.patch, x, ok = cutInt(x[1:]) 95 if !ok || x != "" { 96 // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). 97 // Allowing them would be a bit confusing because we already have: 98 // 1.21 < 1.21rc1 99 // But a prerelease of a patch would have the opposite effect: 100 // 1.21.3rc1 < 1.21.3 101 // We've never needed them before, so let's not start now. 102 return version{} 103 } 104 return v 105 } 106 107 // Parse prerelease. 108 i := 0 109 for i < len(x) && (x[i] < '0' || '9' < x[i]) { 110 if x[i] < 'a' || 'z' < x[i] { 111 return version{} 112 } 113 i++ 114 } 115 if i == 0 { 116 return version{} 117 } 118 v.kind, x = x[:i], x[i:] 119 if x == "" { 120 return v 121 } 122 v.pre, x, ok = cutInt(x) 123 if !ok || x != "" { 124 return version{} 125 } 126 127 return v 128 } 129 130 // cutInt scans the leading decimal number at the start of x to an integer 131 // and returns that value and the rest of the string. 132 func cutInt(x string) (n, rest string, ok bool) { 133 i := 0 134 for i < len(x) && '0' <= x[i] && x[i] <= '9' { 135 i++ 136 } 137 if i == 0 || x[0] == '0' && i != 1 { 138 return "", "", false 139 } 140 return x[:i], x[i:], true 141 } 142 143 // cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. 144 // (Copied from golang.org/x/mod/semver's compareInt.) 145 func cmpInt(x, y string) int { 146 if x == y { 147 return 0 148 } 149 if len(x) < len(y) { 150 return -1 151 } 152 if len(x) > len(y) { 153 return +1 154 } 155 if x < y { 156 return -1 157 } else { 158 return +1 159 } 160 } 161 162 // A version is a parsed Go version: major[.minor[.patch]][kind[pre]] 163 // The numbers are the original decimal strings to avoid integer overflows 164 // and since there is very little actual math. (Probably overflow doesn't matter in practice, 165 // but at the time this code was written, there was an existing test that used 166 // go1.99999999999, which does not fit in an int on 32-bit platforms. 167 // The "big decimal" representation avoids the problem entirely.) 168 type version struct { 169 major string // decimal 170 minor string // decimal or "" 171 patch string // decimal or "" 172 kind string // "", "alpha", "beta", "rc" 173 pre string // decimal or "" 174 }