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