github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/vet/all/main.go (about) 1 // Copyright 2016 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 // +build ignore 6 7 // The vet/all command runs go vet on the standard library and commands. 8 // It compares the output against a set of whitelists 9 // maintained in the whitelist directory. 10 package main 11 12 import ( 13 "bufio" 14 "bytes" 15 "flag" 16 "fmt" 17 "go/build" 18 "go/types" 19 "internal/testenv" 20 "log" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "sync/atomic" 27 ) 28 29 var ( 30 flagPlatforms = flag.String("p", "", "platform(s) to use e.g. linux/amd64,darwin/386") 31 flagAll = flag.Bool("all", false, "run all platforms") 32 flagNoLines = flag.Bool("n", false, "don't print line numbers") 33 ) 34 35 var cmdGoPath string 36 var failed uint32 // updated atomically 37 38 func main() { 39 log.SetPrefix("vet/all: ") 40 log.SetFlags(0) 41 42 var err error 43 cmdGoPath, err = testenv.GoTool() 44 if err != nil { 45 log.Print("could not find cmd/go; skipping") 46 // We're on a platform that can't run cmd/go. 47 // We want this script to be able to run as part of all.bash, 48 // so return cleanly rather than with exit code 1. 49 return 50 } 51 52 flag.Parse() 53 switch { 54 case *flagAll && *flagPlatforms != "": 55 log.Print("-all and -p flags are incompatible") 56 flag.Usage() 57 os.Exit(2) 58 case *flagPlatforms != "": 59 vetPlatforms(parseFlagPlatforms()) 60 case *flagAll: 61 vetPlatforms(allPlatforms()) 62 default: 63 hostPlatform.vet() 64 } 65 if atomic.LoadUint32(&failed) != 0 { 66 os.Exit(1) 67 } 68 } 69 70 var hostPlatform = platform{os: build.Default.GOOS, arch: build.Default.GOARCH} 71 72 func allPlatforms() []platform { 73 var pp []platform 74 cmd := exec.Command(cmdGoPath, "tool", "dist", "list") 75 out, err := cmd.Output() 76 if err != nil { 77 log.Fatal(err) 78 } 79 lines := bytes.Split(out, []byte{'\n'}) 80 for _, line := range lines { 81 if len(line) == 0 { 82 continue 83 } 84 pp = append(pp, parsePlatform(string(line))) 85 } 86 return pp 87 } 88 89 func parseFlagPlatforms() []platform { 90 var pp []platform 91 components := strings.Split(*flagPlatforms, ",") 92 for _, c := range components { 93 pp = append(pp, parsePlatform(c)) 94 } 95 return pp 96 } 97 98 func parsePlatform(s string) platform { 99 vv := strings.Split(s, "/") 100 if len(vv) != 2 { 101 log.Fatalf("could not parse platform %s, must be of form goos/goarch", s) 102 } 103 return platform{os: vv[0], arch: vv[1]} 104 } 105 106 type whitelist map[string]int 107 108 // load adds entries from the whitelist file, if present, for os/arch to w. 109 func (w whitelist) load(goos string, goarch string) { 110 sz := types.SizesFor("gc", goarch) 111 if sz == nil { 112 log.Fatalf("unknown type sizes for arch %q", goarch) 113 } 114 archbits := 8 * sz.Sizeof(types.Typ[types.UnsafePointer]) 115 116 // Look up whether goarch has a shared arch suffix, 117 // such as mips64x for mips64 and mips64le. 118 archsuff := goarch 119 if x, ok := archAsmX[goarch]; ok { 120 archsuff = x 121 } 122 123 // Load whitelists. 124 filenames := []string{ 125 "all.txt", 126 goos + ".txt", 127 goarch + ".txt", 128 goos + "_" + goarch + ".txt", 129 fmt.Sprintf("%dbit.txt", archbits), 130 } 131 if goarch != archsuff { 132 filenames = append(filenames, 133 archsuff+".txt", 134 goos+"_"+archsuff+".txt", 135 ) 136 } 137 138 // We allow error message templates using GOOS and GOARCH. 139 if goos == "android" { 140 goos = "linux" // so many special cases :( 141 } 142 143 // Read whitelists and do template substitution. 144 replace := strings.NewReplacer("GOOS", goos, "GOARCH", goarch, "ARCHSUFF", archsuff) 145 146 for _, filename := range filenames { 147 path := filepath.Join("whitelist", filename) 148 f, err := os.Open(path) 149 if err != nil { 150 // Allow not-exist errors; not all combinations have whitelists. 151 if os.IsNotExist(err) { 152 continue 153 } 154 log.Fatal(err) 155 } 156 scan := bufio.NewScanner(f) 157 for scan.Scan() { 158 line := scan.Text() 159 if len(line) == 0 || strings.HasPrefix(line, "//") { 160 continue 161 } 162 w[replace.Replace(line)]++ 163 } 164 if err := scan.Err(); err != nil { 165 log.Fatal(err) 166 } 167 } 168 } 169 170 type platform struct { 171 os string 172 arch string 173 } 174 175 func (p platform) String() string { 176 return p.os + "/" + p.arch 177 } 178 179 // ignorePathPrefixes are file path prefixes that should be ignored wholesale. 180 var ignorePathPrefixes = [...]string{ 181 // These testdata dirs have lots of intentionally broken/bad code for tests. 182 "cmd/go/testdata/", 183 "cmd/vet/testdata/", 184 "go/printer/testdata/", 185 } 186 187 func vetPlatforms(pp []platform) { 188 for _, p := range pp { 189 p.vet() 190 } 191 } 192 193 func (p platform) vet() { 194 var buf bytes.Buffer 195 fmt.Fprintf(&buf, "go run main.go -p %s\n", p) 196 197 // Load whitelist(s). 198 w := make(whitelist) 199 w.load(p.os, p.arch) 200 201 // 'go tool vet .' is considerably faster than 'go vet ./...' 202 // TODO: The unsafeptr checks are disabled for now, 203 // because there are so many false positives, 204 // and no clear way to improve vet to eliminate large chunks of them. 205 // And having them in the whitelists will just cause annoyance 206 // and churn when working on the runtime. 207 cmd := exec.Command(cmdGoPath, "tool", "vet", "-unsafeptr=false", "-source", ".") 208 cmd.Dir = filepath.Join(runtime.GOROOT(), "src") 209 cmd.Env = append(os.Environ(), "GOOS="+p.os, "GOARCH="+p.arch, "CGO_ENABLED=0") 210 stderr, err := cmd.StderrPipe() 211 if err != nil { 212 log.Fatal(err) 213 } 214 if err := cmd.Start(); err != nil { 215 log.Fatal(err) 216 } 217 218 // Process vet output. 219 scan := bufio.NewScanner(stderr) 220 var parseFailed bool 221 NextLine: 222 for scan.Scan() { 223 line := scan.Text() 224 if strings.HasPrefix(line, "vet: ") { 225 // Typecheck failure: Malformed syntax or multiple packages or the like. 226 // This will yield nicer error messages elsewhere, so ignore them here. 227 continue 228 } 229 230 fields := strings.SplitN(line, ":", 3) 231 var file, lineno, msg string 232 switch len(fields) { 233 case 2: 234 // vet message with no line number 235 file, msg = fields[0], fields[1] 236 case 3: 237 file, lineno, msg = fields[0], fields[1], fields[2] 238 default: 239 if !parseFailed { 240 parseFailed = true 241 fmt.Fprintln(os.Stderr, "failed to parse vet output:") 242 } 243 fmt.Println(os.Stderr, line) 244 } 245 msg = strings.TrimSpace(msg) 246 247 for _, ignore := range ignorePathPrefixes { 248 if strings.HasPrefix(file, filepath.FromSlash(ignore)) { 249 continue NextLine 250 } 251 } 252 253 key := file + ": " + msg 254 if w[key] == 0 { 255 // Vet error with no match in the whitelist. Print it. 256 if *flagNoLines { 257 fmt.Fprintf(&buf, "%s: %s\n", file, msg) 258 } else { 259 fmt.Fprintf(&buf, "%s:%s: %s\n", file, lineno, msg) 260 } 261 atomic.StoreUint32(&failed, 1) 262 continue 263 } 264 w[key]-- 265 } 266 if parseFailed { 267 atomic.StoreUint32(&failed, 1) 268 return 269 } 270 if scan.Err() != nil { 271 log.Fatalf("failed to scan vet output: %v", scan.Err()) 272 } 273 err = cmd.Wait() 274 // We expect vet to fail. 275 // Make sure it has failed appropriately, though (for example, not a PathError). 276 if _, ok := err.(*exec.ExitError); !ok { 277 log.Fatalf("unexpected go vet execution failure: %v", err) 278 } 279 printedHeader := false 280 if len(w) > 0 { 281 for k, v := range w { 282 if v != 0 { 283 if !printedHeader { 284 fmt.Fprintln(&buf, "unmatched whitelist entries:") 285 printedHeader = true 286 } 287 for i := 0; i < v; i++ { 288 fmt.Fprintln(&buf, k) 289 } 290 atomic.StoreUint32(&failed, 1) 291 } 292 } 293 } 294 295 os.Stdout.Write(buf.Bytes()) 296 } 297 298 // archAsmX maps architectures to the suffix usually used for their assembly files, 299 // if different than the arch name itself. 300 var archAsmX = map[string]string{ 301 "android": "linux", 302 "mips64": "mips64x", 303 "mips64le": "mips64x", 304 "mips": "mipsx", 305 "mipsle": "mipsx", 306 "ppc64": "ppc64x", 307 "ppc64le": "ppc64x", 308 }