1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package imports
20
21 import (
22 "bytes"
23 "cmd/go/internal/cfg"
24 "errors"
25 "fmt"
26 "go/build/constraint"
27 "internal/syslist"
28 "strings"
29 "unicode"
30 )
31
32 var (
33 bSlashSlash = []byte("//")
34 bStarSlash = []byte("*/")
35 bSlashStar = []byte("/*")
36 bPlusBuild = []byte("+build")
37
38 goBuildComment = []byte("//go:build")
39
40 errMultipleGoBuild = errors.New("multiple //go:build comments")
41 )
42
43 func isGoBuildComment(line []byte) bool {
44 if !bytes.HasPrefix(line, goBuildComment) {
45 return false
46 }
47 line = bytes.TrimSpace(line)
48 rest := line[len(goBuildComment):]
49 return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
50 }
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 func ShouldBuild(content []byte, tags map[string]bool) bool {
71
72
73
74 content, goBuild, _, err := parseFileHeader(content)
75 if err != nil {
76 return false
77 }
78
79
80
81 var shouldBuild bool
82 switch {
83 case goBuild != nil:
84 x, err := constraint.Parse(string(goBuild))
85 if err != nil {
86 return false
87 }
88 shouldBuild = eval(x, tags, true)
89
90 default:
91 shouldBuild = true
92 p := content
93 for len(p) > 0 {
94 line := p
95 if i := bytes.IndexByte(line, '\n'); i >= 0 {
96 line, p = line[:i], p[i+1:]
97 } else {
98 p = p[len(p):]
99 }
100 line = bytes.TrimSpace(line)
101 if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
102 continue
103 }
104 text := string(line)
105 if !constraint.IsPlusBuild(text) {
106 continue
107 }
108 if x, err := constraint.Parse(text); err == nil {
109 if !eval(x, tags, true) {
110 shouldBuild = false
111 }
112 }
113 }
114 }
115
116 return shouldBuild
117 }
118
119 func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
120 end := 0
121 p := content
122 ended := false
123 inSlashStar := false
124
125 Lines:
126 for len(p) > 0 {
127 line := p
128 if i := bytes.IndexByte(line, '\n'); i >= 0 {
129 line, p = line[:i], p[i+1:]
130 } else {
131 p = p[len(p):]
132 }
133 line = bytes.TrimSpace(line)
134 if len(line) == 0 && !ended {
135
136
137
138
139
140
141
142
143 end = len(content) - len(p)
144 continue Lines
145 }
146 if !bytes.HasPrefix(line, bSlashSlash) {
147 ended = true
148 }
149
150 if !inSlashStar && isGoBuildComment(line) {
151 if goBuild != nil {
152 return nil, nil, false, errMultipleGoBuild
153 }
154 goBuild = line
155 }
156
157 Comments:
158 for len(line) > 0 {
159 if inSlashStar {
160 if i := bytes.Index(line, bStarSlash); i >= 0 {
161 inSlashStar = false
162 line = bytes.TrimSpace(line[i+len(bStarSlash):])
163 continue Comments
164 }
165 continue Lines
166 }
167 if bytes.HasPrefix(line, bSlashSlash) {
168 continue Lines
169 }
170 if bytes.HasPrefix(line, bSlashStar) {
171 inSlashStar = true
172 line = bytes.TrimSpace(line[len(bSlashStar):])
173 continue Comments
174 }
175
176 break Lines
177 }
178 }
179
180 return content[:end], goBuild, sawBinaryOnly, nil
181 }
182
183
184
185
186
187
188 func matchTag(name string, tags map[string]bool, prefer bool) bool {
189
190
191 for _, c := range name {
192 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
193 return false
194 }
195 }
196
197 if tags["*"] && name != "" && name != "ignore" {
198
199
200
201
202 return prefer
203 }
204
205 if tags[name] {
206 return true
207 }
208
209 switch name {
210 case "linux":
211 return tags["android"]
212 case "solaris":
213 return tags["illumos"]
214 case "darwin":
215 return tags["ios"]
216 case "unix":
217 return syslist.UnixOS[cfg.BuildContext.GOOS]
218 default:
219 return false
220 }
221 }
222
223
224
225
226
227
228
229 func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
230 switch x := x.(type) {
231 case *constraint.TagExpr:
232 return matchTag(x.Tag, tags, prefer)
233 case *constraint.NotExpr:
234 return !eval(x.X, tags, !prefer)
235 case *constraint.AndExpr:
236 return eval(x.X, tags, prefer) && eval(x.Y, tags, prefer)
237 case *constraint.OrExpr:
238 return eval(x.X, tags, prefer) || eval(x.Y, tags, prefer)
239 }
240 panic(fmt.Sprintf("unexpected constraint expression %T", x))
241 }
242
243
244
245
246
247
248
249 func Eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
250 return eval(x, tags, prefer)
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 func MatchFile(name string, tags map[string]bool) bool {
274 if tags["*"] {
275 return true
276 }
277 if dot := strings.Index(name, "."); dot != -1 {
278 name = name[:dot]
279 }
280
281
282
283
284
285
286
287
288 i := strings.Index(name, "_")
289 if i < 0 {
290 return true
291 }
292 name = name[i:]
293
294 l := strings.Split(name, "_")
295 if n := len(l); n > 0 && l[n-1] == "test" {
296 l = l[:n-1]
297 }
298 n := len(l)
299 if n >= 2 && syslist.KnownOS[l[n-2]] && syslist.KnownArch[l[n-1]] {
300 return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true)
301 }
302 if n >= 1 && syslist.KnownOS[l[n-1]] {
303 return matchTag(l[n-1], tags, true)
304 }
305 if n >= 1 && syslist.KnownArch[l[n-1]] {
306 return matchTag(l[n-1], tags, true)
307 }
308 return true
309 }
310
View as plain text