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