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 "bytes"
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) []byte {
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, _ := bytes.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