github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/goversion/go_version.go (about) 1 package goversion 2 3 import ( 4 "fmt" 5 "os/exec" 6 "strconv" 7 "strings" 8 ) 9 10 // GoVersion represents the Go version of 11 // the Go compiler version used to compile 12 // the target binary. 13 type GoVersion struct { 14 Major int 15 Minor int 16 Rev int // revision number or negative number for beta and rc releases 17 Proposal string 18 Toolchain string 19 } 20 21 const ( 22 betaStart = -1000 23 betaEnd = -2000 24 ) 25 26 func betaRev(beta int) int { 27 return beta + betaEnd 28 } 29 30 func rcRev(rc int) int { 31 return rc + betaStart 32 } 33 34 var ( 35 GoVer18Beta = GoVersion{1, 8, betaRev(0), "", ""} 36 ) 37 38 // Parse parses a go version string 39 func Parse(ver string) (GoVersion, bool) { 40 var r GoVersion 41 var err1, err2, err3 error 42 43 if strings.HasPrefix(ver, "devel") { 44 return GoVersion{-1, 0, 0, "", ""}, true 45 } 46 47 if strings.HasPrefix(ver, "go") { 48 ver := strings.Split(ver, " ")[0] 49 v := strings.SplitN(ver[2:], ".", 4) 50 switch len(v) { 51 case 2: 52 r.Major, err1 = strconv.Atoi(v[0]) 53 var vr []string 54 55 if vr = strings.SplitN(v[1], "beta", 2); len(vr) == 2 { 56 // old beta releases goX.YbetaZ 57 var beta int 58 beta, err3 = strconv.Atoi(vr[1]) 59 r.Rev = betaRev(beta) 60 } else if vr = strings.SplitN(v[1], "b", 2); len(vr) == 2 { 61 // old boringcrypto version goX.YbZ 62 if _, err := strconv.Atoi(vr[1]); err != nil { 63 return GoVersion{}, false 64 } 65 } else { 66 vr = strings.SplitN(v[1], "rc", 2) 67 if len(vr) == 2 { 68 // rc release goX.YrcZ 69 var rc int 70 rc, err3 = strconv.Atoi(vr[1]) 71 r.Rev = rcRev(rc) 72 } else { 73 r.Minor, err2 = strconv.Atoi(v[1]) 74 if err2 != nil { 75 return GoVersion{}, false 76 } 77 return r, true 78 } 79 } 80 81 // old major release (if none of the options above apply) goX.Y 82 83 r.Minor, err2 = strconv.Atoi(vr[0]) 84 r.Proposal = "" 85 86 if err1 != nil || err2 != nil || err3 != nil { 87 return GoVersion{}, false 88 } 89 90 return r, true 91 92 case 3: 93 94 r.Major, err1 = strconv.Atoi(v[0]) 95 r.Minor, err2 = strconv.Atoi(v[1]) 96 97 if vr := strings.SplitN(v[2], "-", 2); len(vr) == 2 { 98 // minor version with toolchain modifier goX.Y.Z-anything 99 r.Rev, err3 = strconv.Atoi(vr[0]) 100 r.Toolchain = vr[1] 101 } else if vr := strings.SplitN(v[2], "b", 2); len(vr) == 2 { 102 // old boringcrypto version goX.Y.ZbW 103 r.Rev, err3 = strconv.Atoi(vr[0]) 104 } else { 105 // minor version goX.Y.Z 106 r.Rev, err3 = strconv.Atoi(v[2]) 107 } 108 109 r.Proposal = "" 110 if err1 != nil || err2 != nil || err3 != nil { 111 return GoVersion{}, false 112 } 113 114 return r, true 115 116 case 4: 117 118 // old proposal release goX.Y.Z.anything 119 120 r.Major, err1 = strconv.Atoi(v[0]) 121 r.Minor, err2 = strconv.Atoi(v[1]) 122 r.Rev, err3 = strconv.Atoi(v[2]) 123 r.Proposal = v[3] 124 if err1 != nil || err2 != nil || err3 != nil || r.Proposal == "" { 125 return GoVersion{}, false 126 } 127 128 return r, true 129 130 default: 131 return GoVersion{}, false 132 } 133 } 134 135 return GoVersion{}, false 136 } 137 138 // AfterOrEqual returns whether one GoVersion is after or 139 // equal to the other. 140 func (v *GoVersion) AfterOrEqual(b GoVersion) bool { 141 if v.Major < b.Major { 142 return false 143 } else if v.Major > b.Major { 144 return true 145 } 146 147 if v.Minor < b.Minor { 148 return false 149 } else if v.Minor > b.Minor { 150 return true 151 } 152 153 if v.Rev < b.Rev { 154 return false 155 } else if v.Rev > b.Rev { 156 return true 157 } 158 159 return true 160 } 161 162 // IsDevel returns whether the GoVersion 163 // is a development version. 164 func (v *GoVersion) IsDevel() bool { 165 return v.Major < 0 166 } 167 168 func (v *GoVersion) String() string { 169 switch { 170 case v.Rev < betaStart: 171 // beta version 172 return fmt.Sprintf("go%d.%dbeta%d", v.Major, v.Minor, v.Rev-betaEnd) 173 case v.Rev < 0: 174 // rc version 175 return fmt.Sprintf("go%d.%drc%d", v.Major, v.Minor, v.Rev-betaStart) 176 case v.Proposal != "": 177 // with proposal 178 return fmt.Sprintf("go%d.%d.%d.%s", v.Major, v.Minor, v.Rev, v.Proposal) 179 case v.Rev == 0 && v.Minor < 21: 180 // old major version 181 return fmt.Sprintf("go%d.%d", v.Major, v.Minor) 182 case v.Toolchain != "": 183 return fmt.Sprintf("go%d.%d.%d-%s", v.Major, v.Minor, v.Rev, v.Toolchain) 184 default: 185 // post go1.21 major version or minor version 186 return fmt.Sprintf("go%d.%d.%d", v.Major, v.Minor, v.Rev) 187 } 188 } 189 190 const goVersionPrefix = "go version " 191 192 // Installed runs "go version" and parses the output 193 func Installed() (GoVersion, bool) { 194 out, err := exec.Command("go", "version").CombinedOutput() 195 if err != nil { 196 return GoVersion{}, false 197 } 198 199 s := string(out) 200 201 if !strings.HasPrefix(s, goVersionPrefix) { 202 return GoVersion{}, false 203 } 204 205 return Parse(s[len(goVersionPrefix):]) 206 } 207 208 // VersionAfterOrEqual checks that version (as returned by runtime.Version() 209 // or go version) is major.minor or a later version, or a development 210 // version. 211 func VersionAfterOrEqual(version string, major, minor int) bool { 212 return VersionAfterOrEqualRev(version, major, minor, -1) 213 } 214 215 // VersionAfterOrEqualRev checks that version (as returned by runtime.Version() 216 // or go version) is major.minor or a later version, or a development 217 // version. 218 func VersionAfterOrEqualRev(version string, major, minor, rev int) bool { 219 ver, _ := Parse(version) 220 if ver.IsDevel() { 221 return true 222 } 223 return ver.AfterOrEqual(GoVersion{major, minor, rev, "", ""}) 224 } 225 226 const producerVersionPrefix = "Go cmd/compile " 227 228 // ProducerAfterOrEqual checks that the DW_AT_producer version is 229 // major.minor or a later version, or a development version. 230 func ProducerAfterOrEqual(producer string, major, minor int) bool { 231 ver := ParseProducer(producer) 232 if ver.IsDevel() { 233 return true 234 } 235 return ver.AfterOrEqual(GoVersion{major, minor, 0, "", ""}) 236 } 237 238 func ParseProducer(producer string) GoVersion { 239 producer = strings.TrimPrefix(producer, producerVersionPrefix) 240 ver, _ := Parse(producer) 241 return ver 242 }