| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Copied from Go distribution src/go/build/build.go, syslist.go |
| |
| package imports |
| |
| import ( |
| "bytes" |
| "strings" |
| "unicode" |
| ) |
| |
| var slashslash = []byte("//") |
| |
| // ShouldBuild reports whether it is okay to use this file, |
| // The rule is that in the file's leading run of // comments |
| // and blank lines, which must be followed by a blank line |
| // (to avoid including a Go package clause doc comment), |
| // lines beginning with '// +build' are taken as build directives. |
| // |
| // The file is accepted only if each such line lists something |
| // matching the file. For example: |
| // |
| // // +build windows linux |
| // |
| // marks the file as applicable only on Windows and Linux. |
| // |
| func ShouldBuild(content []byte, tags map[string]bool) bool { |
| // Pass 1. Identify leading run of // comments and blank lines, |
| // which must be followed by a blank line. |
| end := 0 |
| p := content |
| for len(p) > 0 { |
| line := p |
| if i := bytes.IndexByte(line, '\n'); i >= 0 { |
| line, p = line[:i], p[i+1:] |
| } else { |
| p = p[len(p):] |
| } |
| line = bytes.TrimSpace(line) |
| if len(line) == 0 { // Blank line |
| end = len(content) - len(p) |
| continue |
| } |
| if !bytes.HasPrefix(line, slashslash) { // Not comment line |
| break |
| } |
| } |
| content = content[:end] |
| |
| // Pass 2. Process each line in the run. |
| p = content |
| allok := true |
| for len(p) > 0 { |
| line := p |
| if i := bytes.IndexByte(line, '\n'); i >= 0 { |
| line, p = line[:i], p[i+1:] |
| } else { |
| p = p[len(p):] |
| } |
| line = bytes.TrimSpace(line) |
| if !bytes.HasPrefix(line, slashslash) { |
| continue |
| } |
| line = bytes.TrimSpace(line[len(slashslash):]) |
| if len(line) > 0 && line[0] == '+' { |
| // Looks like a comment +line. |
| f := strings.Fields(string(line)) |
| if f[0] == "+build" { |
| ok := false |
| for _, tok := range f[1:] { |
| if matchTags(tok, tags) { |
| ok = true |
| } |
| } |
| if !ok { |
| allok = false |
| } |
| } |
| } |
| } |
| |
| return allok |
| } |
| |
| // matchTags reports whether the name is one of: |
| // |
| // tag (if haveTags[tag] is true) |
| // !tag (if haveTags[tag] is false) |
| // a comma-separated list of any of these |
| // |
| func matchTags(name string, tags map[string]bool) bool { |
| if name == "" { |
| return false |
| } |
| if i := strings.Index(name, ","); i >= 0 { |
| // comma-separated list |
| ok1 := matchTags(name[:i], tags) |
| ok2 := matchTags(name[i+1:], tags) |
| return ok1 && ok2 |
| } |
| if strings.HasPrefix(name, "!!") { // bad syntax, reject always |
| return false |
| } |
| if strings.HasPrefix(name, "!") { // negation |
| return len(name) > 1 && !matchTags(name[1:], tags) |
| } |
| |
| // Tags must be letters, digits, underscores or dots. |
| // Unlike in Go identifiers, all digits are fine (e.g., "386"). |
| for _, c := range name { |
| if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { |
| return false |
| } |
| } |
| |
| if name == "linux" && tags["android"] { |
| return true |
| } |
| return tags[name] |
| } |
| |
| // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH |
| // suffix which does not match the current system. |
| // The recognized name formats are: |
| // |
| // name_$(GOOS).* |
| // name_$(GOARCH).* |
| // name_$(GOOS)_$(GOARCH).* |
| // name_$(GOOS)_test.* |
| // name_$(GOARCH)_test.* |
| // name_$(GOOS)_$(GOARCH)_test.* |
| // |
| // An exception: if GOOS=android, then files with GOOS=linux are also matched. |
| func MatchFile(name string, tags map[string]bool) bool { |
| if dot := strings.Index(name, "."); dot != -1 { |
| name = name[:dot] |
| } |
| |
| // Before Go 1.4, a file called "linux.go" would be equivalent to having a |
| // build tag "linux" in that file. For Go 1.4 and beyond, we require this |
| // auto-tagging to apply only to files with a non-empty prefix, so |
| // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating |
| // systems, such as android, to arrive without breaking existing code with |
| // innocuous source code in "android.go". The easiest fix: cut everything |
| // in the name before the initial _. |
| i := strings.Index(name, "_") |
| if i < 0 { |
| return true |
| } |
| name = name[i:] // ignore everything before first _ |
| |
| l := strings.Split(name, "_") |
| if n := len(l); n > 0 && l[n-1] == "test" { |
| l = l[:n-1] |
| } |
| n := len(l) |
| if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { |
| return tags[l[n-2]] && tags[l[n-1]] |
| } |
| if n >= 1 && knownOS[l[n-1]] { |
| return tags[l[n-1]] |
| } |
| if n >= 1 && knownArch[l[n-1]] { |
| return tags[l[n-1]] |
| } |
| return true |
| } |
| |
| var knownOS = make(map[string]bool) |
| var knownArch = make(map[string]bool) |
| |
| func init() { |
| for _, v := range strings.Fields(goosList) { |
| knownOS[v] = true |
| } |
| for _, v := range strings.Fields(goarchList) { |
| knownArch[v] = true |
| } |
| } |
| |
| const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows zos " |
| const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 " |