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 "strings"
28 "unicode"
29 )
30
31 var (
32 bSlashSlash = []byte("//")
33 bStarSlash = []byte("*/")
34 bSlashStar = []byte("/*")
35 bPlusBuild = []byte("+build")
36
37 goBuildComment = []byte("//go:build")
38
39 errMultipleGoBuild = errors.New("multiple //go:build comments")
40 )
41
42 func isGoBuildComment(line []byte) bool {
43 if !bytes.HasPrefix(line, goBuildComment) {
44 return false
45 }
46 line = bytes.TrimSpace(line)
47 rest := line[len(goBuildComment):]
48 return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
49 }
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 func ShouldBuild(content []byte, tags map[string]bool) bool {
70
71
72
73 content, goBuild, _, err := parseFileHeader(content)
74 if err != nil {
75 return false
76 }
77
78
79
80 var shouldBuild bool
81 switch {
82 case goBuild != nil:
83 x, err := constraint.Parse(string(goBuild))
84 if err != nil {
85 return false
86 }
87 shouldBuild = eval(x, tags, true)
88
89 default:
90 shouldBuild = true
91 p := content
92 for len(p) > 0 {
93 line := p
94 if i := bytes.IndexByte(line, '\n'); i >= 0 {
95 line, p = line[:i], p[i+1:]
96 } else {
97 p = p[len(p):]
98 }
99 line = bytes.TrimSpace(line)
100 if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
101 continue
102 }
103 text := string(line)
104 if !constraint.IsPlusBuild(text) {
105 continue
106 }
107 if x, err := constraint.Parse(text); err == nil {
108 if !eval(x, tags, true) {
109 shouldBuild = false
110 }
111 }
112 }
113 }
114
115 return shouldBuild
116 }
117
118 func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
119 end := 0
120 p := content
121 ended := false
122 inSlashStar := false
123
124 Lines:
125 for len(p) > 0 {
126 line := p
127 if i := bytes.IndexByte(line, '\n'); i >= 0 {
128 line, p = line[:i], p[i+1:]
129 } else {
130 p = p[len(p):]
131 }
132 line = bytes.TrimSpace(line)
133 if len(line) == 0 && !ended {
134
135
136
137
138
139
140
141
142 end = len(content) - len(p)
143 continue Lines
144 }
145 if !bytes.HasPrefix(line, bSlashSlash) {
146 ended = true
147 }
148
149 if !inSlashStar && isGoBuildComment(line) {
150 if goBuild != nil {
151 return nil, nil, false, errMultipleGoBuild
152 }
153 goBuild = line
154 }
155
156 Comments:
157 for len(line) > 0 {
158 if inSlashStar {
159 if i := bytes.Index(line, bStarSlash); i >= 0 {
160 inSlashStar = false
161 line = bytes.TrimSpace(line[i+len(bStarSlash):])
162 continue Comments
163 }
164 continue Lines
165 }
166 if bytes.HasPrefix(line, bSlashSlash) {
167 continue Lines
168 }
169 if bytes.HasPrefix(line, bSlashStar) {
170 inSlashStar = true
171 line = bytes.TrimSpace(line[len(bSlashStar):])
172 continue Comments
173 }
174
175 break Lines
176 }
177 }
178
179 return content[:end], goBuild, sawBinaryOnly, nil
180 }
181
182
183
184
185
186
187 func matchTag(name string, tags map[string]bool, prefer bool) bool {
188
189
190 for _, c := range name {
191 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
192 return false
193 }
194 }
195
196 if tags["*"] && name != "" && name != "ignore" {
197
198
199
200
201 return prefer
202 }
203
204 if tags[name] {
205 return true
206 }
207
208 switch name {
209 case "linux":
210 return tags["android"]
211 case "solaris":
212 return tags["illumos"]
213 case "darwin":
214 return tags["ios"]
215 case "unix":
216 return unixOS[cfg.BuildContext.GOOS]
217 default:
218 return false
219 }
220 }
221
222
223
224
225
226
227
228 func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
229 switch x := x.(type) {
230 case *constraint.TagExpr:
231 return matchTag(x.Tag, tags, prefer)
232 case *constraint.NotExpr:
233 return !eval(x.X, tags, !prefer)
234 case *constraint.AndExpr:
235 return eval(x.X, tags, prefer) && eval(x.Y, tags, prefer)
236 case *constraint.OrExpr:
237 return eval(x.X, tags, prefer) || eval(x.Y, tags, prefer)
238 }
239 panic(fmt.Sprintf("unexpected constraint expression %T", x))
240 }
241
242
243
244
245
246
247
248 func Eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
249 return eval(x, tags, prefer)
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 func MatchFile(name string, tags map[string]bool) bool {
273 if tags["*"] {
274 return true
275 }
276 if dot := strings.Index(name, "."); dot != -1 {
277 name = name[:dot]
278 }
279
280
281
282
283
284
285
286
287 i := strings.Index(name, "_")
288 if i < 0 {
289 return true
290 }
291 name = name[i:]
292
293 l := strings.Split(name, "_")
294 if n := len(l); n > 0 && l[n-1] == "test" {
295 l = l[:n-1]
296 }
297 n := len(l)
298 if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
299 return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true)
300 }
301 if n >= 1 && KnownOS[l[n-1]] {
302 return matchTag(l[n-1], tags, true)
303 }
304 if n >= 1 && KnownArch[l[n-1]] {
305 return matchTag(l[n-1], tags, true)
306 }
307 return true
308 }
309
310 var KnownOS = map[string]bool{
311 "aix": true,
312 "android": true,
313 "darwin": true,
314 "dragonfly": true,
315 "freebsd": true,
316 "hurd": true,
317 "illumos": true,
318 "ios": true,
319 "js": true,
320 "linux": true,
321 "nacl": true,
322 "netbsd": true,
323 "openbsd": true,
324 "plan9": true,
325 "solaris": true,
326 "wasip1": true,
327 "windows": true,
328 "zos": true,
329 }
330
331
332
333
334 var unixOS = map[string]bool{
335 "aix": true,
336 "android": true,
337 "darwin": true,
338 "dragonfly": true,
339 "freebsd": true,
340 "hurd": true,
341 "illumos": true,
342 "ios": true,
343 "linux": true,
344 "netbsd": true,
345 "openbsd": true,
346 "solaris": true,
347 }
348
349 var KnownArch = map[string]bool{
350 "386": true,
351 "amd64": true,
352 "amd64p32": true,
353 "arm": true,
354 "armbe": true,
355 "arm64": true,
356 "arm64be": true,
357 "ppc64": true,
358 "ppc64le": true,
359 "mips": true,
360 "mipsle": true,
361 "mips64": true,
362 "mips64le": true,
363 "mips64p32": true,
364 "mips64p32le": true,
365 "loong64": true,
366 "ppc": true,
367 "riscv": true,
368 "riscv64": true,
369 "s390": true,
370 "s390x": true,
371 "sparc": true,
372 "sparc64": true,
373 "wasm": true,
374 }
375
View as plain text