github.com/gernest/nezuko@v0.1.2/internal/imports/build.go (about) 1 // Copyright 2018 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 // Copied from Go distribution src/go/build/build.go, syslist.go 6 7 package imports 8 9 import ( 10 "bytes" 11 "strings" 12 "unicode" 13 ) 14 15 var slashslash = []byte("//") 16 17 // ShouldBuild reports whether it is okay to use this file, 18 // The rule is that in the file's leading run of // comments 19 // and blank lines, which must be followed by a blank line 20 // (to avoid including a Go package clause doc comment), 21 // lines beginning with '// +build' are taken as build directives. 22 // 23 // The file is accepted only if each such line lists something 24 // matching the file. For example: 25 // 26 // // +build windows linux 27 // 28 // marks the file as applicable only on Windows and Linux. 29 // 30 // If tags["*"] is true, then ShouldBuild will consider every 31 // build tag except "ignore" to be both true and false for 32 // the purpose of satisfying build tags, in order to estimate 33 // (conservatively) whether a file could ever possibly be used 34 // in any build. 35 // 36 func ShouldBuild(content []byte, tags map[string]bool) bool { 37 // Pass 1. Identify leading run of // comments and blank lines, 38 // which must be followed by a blank line. 39 end := 0 40 p := content 41 for len(p) > 0 { 42 line := p 43 if i := bytes.IndexByte(line, '\n'); i >= 0 { 44 line, p = line[:i], p[i+1:] 45 } else { 46 p = p[len(p):] 47 } 48 line = bytes.TrimSpace(line) 49 if len(line) == 0 { // Blank line 50 end = len(content) - len(p) 51 continue 52 } 53 if !bytes.HasPrefix(line, slashslash) { // Not comment line 54 break 55 } 56 } 57 content = content[:end] 58 59 // Pass 2. Process each line in the run. 60 p = content 61 allok := true 62 for len(p) > 0 { 63 line := p 64 if i := bytes.IndexByte(line, '\n'); i >= 0 { 65 line, p = line[:i], p[i+1:] 66 } else { 67 p = p[len(p):] 68 } 69 line = bytes.TrimSpace(line) 70 if !bytes.HasPrefix(line, slashslash) { 71 continue 72 } 73 line = bytes.TrimSpace(line[len(slashslash):]) 74 if len(line) > 0 && line[0] == '+' { 75 // Looks like a comment +line. 76 f := strings.Fields(string(line)) 77 if f[0] == "+build" { 78 ok := false 79 for _, tok := range f[1:] { 80 if matchTags(tok, tags) { 81 ok = true 82 } 83 } 84 if !ok { 85 allok = false 86 } 87 } 88 } 89 } 90 91 return allok 92 } 93 94 // matchTags reports whether the name is one of: 95 // 96 // tag (if tags[tag] is true) 97 // !tag (if tags[tag] is false) 98 // a comma-separated list of any of these 99 // 100 func matchTags(name string, tags map[string]bool) bool { 101 if name == "" { 102 return false 103 } 104 if i := strings.Index(name, ","); i >= 0 { 105 // comma-separated list 106 ok1 := matchTags(name[:i], tags) 107 ok2 := matchTags(name[i+1:], tags) 108 return ok1 && ok2 109 } 110 if strings.HasPrefix(name, "!!") { // bad syntax, reject always 111 return false 112 } 113 if strings.HasPrefix(name, "!") { // negation 114 return len(name) > 1 && matchTag(name[1:], tags, false) 115 } 116 return matchTag(name, tags, true) 117 } 118 119 // matchTag reports whether the tag name is valid and satisfied by tags[name]==want. 120 func matchTag(name string, tags map[string]bool, want bool) bool { 121 // Tags must be letters, digits, underscores or dots. 122 // Unlike in Go identifiers, all digits are fine (e.g., "386"). 123 for _, c := range name { 124 if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' { 125 return false 126 } 127 } 128 129 if tags["*"] && name != "" && name != "ignore" { 130 // Special case for gathering all possible imports: 131 // if we put * in the tags map then all tags 132 // except "ignore" are considered both present and not 133 // (so we return true no matter how 'want' is set). 134 return true 135 } 136 137 have := tags[name] 138 if name == "linux" { 139 have = have || tags["android"] 140 } 141 return have == want 142 } 143 144 // MatchFile returns false if the name contains a $GOOS or $GOARCH 145 // suffix which does not match the current system. 146 // The recognized name formats are: 147 // 148 // name_$(GOOS).* 149 // name_$(GOARCH).* 150 // name_$(GOOS)_$(GOARCH).* 151 // name_$(GOOS)_test.* 152 // name_$(GOARCH)_test.* 153 // name_$(GOOS)_$(GOARCH)_test.* 154 // 155 // An exception: if GOOS=android, then files with GOOS=linux are also matched. 156 // 157 // If tags["*"] is true, then MatchFile will consider all possible 158 // GOOS and GOARCH to be available and will consequently 159 // always return true. 160 func MatchFile(name string, tags map[string]bool) bool { 161 if tags["*"] { 162 return true 163 } 164 if dot := strings.Index(name, "."); dot != -1 { 165 name = name[:dot] 166 } 167 168 // Before Go 1.4, a file called "linux.go" would be equivalent to having a 169 // build tag "linux" in that file. For Go 1.4 and beyond, we require this 170 // auto-tagging to apply only to files with a non-empty prefix, so 171 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 172 // systems, such as android, to arrive without breaking existing code with 173 // innocuous source code in "android.go". The easiest fix: cut everything 174 // in the name before the initial _. 175 i := strings.Index(name, "_") 176 if i < 0 { 177 return true 178 } 179 name = name[i:] // ignore everything before first _ 180 181 l := strings.Split(name, "_") 182 if n := len(l); n > 0 && l[n-1] == "test" { 183 l = l[:n-1] 184 } 185 n := len(l) 186 if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] { 187 return tags[l[n-2]] && tags[l[n-1]] 188 } 189 if n >= 1 && KnownOS[l[n-1]] { 190 return tags[l[n-1]] 191 } 192 if n >= 1 && KnownArch[l[n-1]] { 193 return tags[l[n-1]] 194 } 195 return true 196 } 197 198 var KnownOS = make(map[string]bool) 199 var KnownArch = make(map[string]bool) 200 201 func init() { 202 for _, v := range strings.Fields(goosList) { 203 KnownOS[v] = true 204 } 205 for _, v := range strings.Fields(goarchList) { 206 KnownArch[v] = true 207 } 208 } 209 210 const goosList = "aix android darwin dragonfly freebsd hurd js linux nacl netbsd openbsd plan9 solaris windows zos " 211 const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "