]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/build/constraint/vers.go
go/build/constraint: add GoVersion
[gostls13.git] / src / go / build / constraint / vers.go
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, _ := stringsCut(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 // TODO: Delete, replace calls with strings.Cut once Go bootstrap toolchain is bumped.
80 func stringsCut(s, sep string) (before, after string, found bool) {
81         if i := strings.Index(s, sep); i >= 0 {
82                 return s[:i], s[i+len(sep):], true
83         }
84         return s, "", false
85 }
86
87 // andVersion returns the minimum Go version
88 // implied by the AND of two minimum Go versions,
89 // which is the max of the versions.
90 func andVersion(x, y int) int {
91         if x > y {
92                 return x
93         }
94         return y
95 }
96
97 // orVersion returns the minimum Go version
98 // implied by the OR of two minimum Go versions,
99 // which is the min of the versions.
100 func orVersion(x, y int) int {
101         if x < y {
102                 return x
103         }
104         return y
105 }