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 package constraint 6 7 import ( 8 "strconv" 9 "strings" 10 ) 11 12 // GoVersion returns the minimum Go version implied by a given build expression. 13 // If the expression can be satisfied without any Go version tags, GoVersion returns an empty string. 14 // 15 // For example: 16 // 17 // GoVersion(linux && go1.22) = "go1.22" 18 // GoVersion((linux && go1.22) || (windows && go1.20)) = "go1.20" => go1.20 19 // GoVersion(linux) = "" 20 // GoVersion(linux || (windows && go1.22)) = "" 21 // GoVersion(!go1.22) = "" 22 // 23 // GoVersion assumes that any tag or negated tag may independently be true, 24 // so that its analysis can be purely structural, without SAT solving. 25 // “Impossible” subexpressions may therefore affect the result. 26 // 27 // For example: 28 // 29 // GoVersion((linux && !linux && go1.20) || go1.21) = "go1.20" 30 func GoVersion(x Expr) string { 31 v := minVersion(x, +1) 32 if v < 0 { 33 return "" 34 } 35 if v == 0 { 36 return "go1" 37 } 38 return "go1." + strconv.Itoa(v) 39 } 40 41 // minVersion returns the minimum Go major version (9 for go1.9) 42 // implied by expression z, or if sign < 0, by expression !z. 43 func minVersion(z Expr, sign int) int { 44 switch z := z.(type) { 45 default: 46 return -1 47 case *AndExpr: 48 op := andVersion 49 if sign < 0 { 50 op = orVersion 51 } 52 return op(minVersion(z.X, sign), minVersion(z.Y, sign)) 53 case *OrExpr: 54 op := orVersion 55 if sign < 0 { 56 op = andVersion 57 } 58 return op(minVersion(z.X, sign), minVersion(z.Y, sign)) 59 case *NotExpr: 60 return minVersion(z.X, -sign) 61 case *TagExpr: 62 if sign < 0 { 63 // !foo implies nothing 64 return -1 65 } 66 if z.Tag == "go1" { 67 return 0 68 } 69 _, v, _ := strings.Cut(z.Tag, "go1.") 70 n, err := strconv.Atoi(v) 71 if err != nil { 72 // not a go1.N tag 73 return -1 74 } 75 return n 76 } 77 } 78 79 // andVersion returns the minimum Go version 80 // implied by the AND of two minimum Go versions, 81 // which is the max of the versions. 82 func andVersion(x, y int) int { 83 if x > y { 84 return x 85 } 86 return y 87 } 88 89 // orVersion returns the minimum Go version 90 // implied by the OR of two minimum Go versions, 91 // which is the min of the versions. 92 func orVersion(x, y int) int { 93 if x < y { 94 return x 95 } 96 return y 97 } 98