github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-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 if name == "solaris" { 142 have = have || tags["illumos"] 143 } 144 return have == want 145 } 146 147 // MatchFile returns false if the name contains a $GOOS or $GOARCH 148 // suffix which does not match the current system. 149 // The recognized name formats are: 150 // 151 // name_$(GOOS).* 152 // name_$(GOARCH).* 153 // name_$(GOOS)_$(GOARCH).* 154 // name_$(GOOS)_test.* 155 // name_$(GOARCH)_test.* 156 // name_$(GOOS)_$(GOARCH)_test.* 157 // 158 // Exceptions: 159 // if GOOS=android, then files with GOOS=linux are also matched. 160 // if GOOS=illumos, then files with GOOS=solaris are also matched. 161 // 162 // If tags["*"] is true, then MatchFile will consider all possible 163 // GOOS and GOARCH to be available and will consequently 164 // always return true. 165 func MatchFile(name string, tags map[string]bool) bool { 166 if tags["*"] { 167 return true 168 } 169 if dot := strings.Index(name, "."); dot != -1 { 170 name = name[:dot] 171 } 172 173 // Before Go 1.4, a file called "linux.go" would be equivalent to having a 174 // build tag "linux" in that file. For Go 1.4 and beyond, we require this 175 // auto-tagging to apply only to files with a non-empty prefix, so 176 // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating 177 // systems, such as android, to arrive without breaking existing code with 178 // innocuous source code in "android.go". The easiest fix: cut everything 179 // in the name before the initial _. 180 i := strings.Index(name, "_") 181 if i < 0 { 182 return true 183 } 184 name = name[i:] // ignore everything before first _ 185 186 l := strings.Split(name, "_") 187 if n := len(l); n > 0 && l[n-1] == "test" { 188 l = l[:n-1] 189 } 190 n := len(l) 191 if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] { 192 return matchTag(l[n-2], tags, true) && matchTag(l[n-1], tags, true) 193 } 194 if n >= 1 && KnownOS[l[n-1]] { 195 return matchTag(l[n-1], tags, true) 196 } 197 if n >= 1 && KnownArch[l[n-1]] { 198 return matchTag(l[n-1], tags, true) 199 } 200 return true 201 } 202 203 var KnownOS = map[string]bool{ 204 "aix": true, 205 "android": true, 206 "darwin": true, 207 "dragonfly": true, 208 "freebsd": true, 209 "hurd": true, 210 "illumos": true, 211 "js": true, 212 "linux": true, 213 "nacl": true, // legacy; don't remove 214 "netbsd": true, 215 "openbsd": true, 216 "plan9": true, 217 "solaris": true, 218 "windows": true, 219 "zos": true, 220 } 221 222 var KnownArch = map[string]bool{ 223 "386": true, 224 "amd64": true, 225 "amd64p32": true, // legacy; don't remove 226 "arm": true, 227 "armbe": true, 228 "arm64": true, 229 "arm64be": true, 230 "ppc64": true, 231 "ppc64le": true, 232 "mips": true, 233 "mipsle": true, 234 "mips64": true, 235 "mips64le": true, 236 "mips64p32": true, 237 "mips64p32le": true, 238 "ppc": true, 239 "riscv": true, 240 "riscv64": true, 241 "s390": true, 242 "s390x": true, 243 "sparc": true, 244 "sparc64": true, 245 "wasm": true, 246 }