1a2f01efa6
Update the Go library to the 1.10beta1 release. Requires a few changes to the compiler for modifications to the map runtime code, and to handle some nowritebarrier cases in the runtime. Reviewed-on: https://go-review.googlesource.com/86455 gotools/: * Makefile.am (go_cmd_vet_files): New variable. (go_cmd_buildid_files, go_cmd_test2json_files): New variables. (s-zdefaultcc): Change from constants to functions. (noinst_PROGRAMS): Add vet, buildid, and test2json. (cgo$(EXEEXT)): Link against $(LIBGOTOOL). (vet$(EXEEXT)): New target. (buildid$(EXEEXT)): New target. (test2json$(EXEEXT)): New target. (install-exec-local): Install all $(noinst_PROGRAMS). (uninstall-local): Uninstasll all $(noinst_PROGRAMS). (check-go-tool): Depend on $(noinst_PROGRAMS). Copy down objabi.go. (check-runtime): Depend on $(noinst_PROGRAMS). (check-cgo-test, check-carchive-test): Likewise. (check-vet): New target. (check): Depend on check-vet. Look at cmd_vet-testlog. (.PHONY): Add check-vet. * Makefile.in: Rebuild. From-SVN: r256365
319 lines
7.6 KiB
Go
319 lines
7.6 KiB
Go
// Copyright 2016 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.
|
|
|
|
// +build ignore
|
|
|
|
// The vet/all command runs go vet on the standard library and commands.
|
|
// It compares the output against a set of whitelists
|
|
// maintained in the whitelist directory.
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"go/build"
|
|
"go/types"
|
|
"internal/testenv"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync/atomic"
|
|
)
|
|
|
|
var (
|
|
flagPlatforms = flag.String("p", "", "platform(s) to use e.g. linux/amd64,darwin/386")
|
|
flagAll = flag.Bool("all", false, "run all platforms")
|
|
flagNoLines = flag.Bool("n", false, "don't print line numbers")
|
|
)
|
|
|
|
var cmdGoPath string
|
|
var failed uint32 // updated atomically
|
|
|
|
func main() {
|
|
log.SetPrefix("vet/all: ")
|
|
log.SetFlags(0)
|
|
|
|
var err error
|
|
cmdGoPath, err = testenv.GoTool()
|
|
if err != nil {
|
|
log.Print("could not find cmd/go; skipping")
|
|
// We're on a platform that can't run cmd/go.
|
|
// We want this script to be able to run as part of all.bash,
|
|
// so return cleanly rather than with exit code 1.
|
|
return
|
|
}
|
|
|
|
flag.Parse()
|
|
switch {
|
|
case *flagAll && *flagPlatforms != "":
|
|
log.Print("-all and -p flags are incompatible")
|
|
flag.Usage()
|
|
os.Exit(2)
|
|
case *flagPlatforms != "":
|
|
vetPlatforms(parseFlagPlatforms())
|
|
case *flagAll:
|
|
vetPlatforms(allPlatforms())
|
|
default:
|
|
hostPlatform.vet()
|
|
}
|
|
if atomic.LoadUint32(&failed) != 0 {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
var hostPlatform = platform{os: build.Default.GOOS, arch: build.Default.GOARCH}
|
|
|
|
func allPlatforms() []platform {
|
|
var pp []platform
|
|
cmd := exec.Command(cmdGoPath, "tool", "dist", "list")
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
lines := bytes.Split(out, []byte{'\n'})
|
|
for _, line := range lines {
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
pp = append(pp, parsePlatform(string(line)))
|
|
}
|
|
return pp
|
|
}
|
|
|
|
func parseFlagPlatforms() []platform {
|
|
var pp []platform
|
|
components := strings.Split(*flagPlatforms, ",")
|
|
for _, c := range components {
|
|
pp = append(pp, parsePlatform(c))
|
|
}
|
|
return pp
|
|
}
|
|
|
|
func parsePlatform(s string) platform {
|
|
vv := strings.Split(s, "/")
|
|
if len(vv) != 2 {
|
|
log.Fatalf("could not parse platform %s, must be of form goos/goarch", s)
|
|
}
|
|
return platform{os: vv[0], arch: vv[1]}
|
|
}
|
|
|
|
type whitelist map[string]int
|
|
|
|
// load adds entries from the whitelist file, if present, for os/arch to w.
|
|
func (w whitelist) load(goos string, goarch string) {
|
|
sz := types.SizesFor("gc", goarch)
|
|
if sz == nil {
|
|
log.Fatalf("unknown type sizes for arch %q", goarch)
|
|
}
|
|
archbits := 8 * sz.Sizeof(types.Typ[types.UnsafePointer])
|
|
|
|
// Look up whether goarch has a shared arch suffix,
|
|
// such as mips64x for mips64 and mips64le.
|
|
archsuff := goarch
|
|
if x, ok := archAsmX[goarch]; ok {
|
|
archsuff = x
|
|
}
|
|
|
|
// Load whitelists.
|
|
filenames := []string{
|
|
"all.txt",
|
|
goos + ".txt",
|
|
goarch + ".txt",
|
|
goos + "_" + goarch + ".txt",
|
|
fmt.Sprintf("%dbit.txt", archbits),
|
|
}
|
|
if goarch != archsuff {
|
|
filenames = append(filenames,
|
|
archsuff+".txt",
|
|
goos+"_"+archsuff+".txt",
|
|
)
|
|
}
|
|
|
|
// We allow error message templates using GOOS and GOARCH.
|
|
if goos == "android" {
|
|
goos = "linux" // so many special cases :(
|
|
}
|
|
|
|
// Read whitelists and do template substitution.
|
|
replace := strings.NewReplacer("GOOS", goos, "GOARCH", goarch, "ARCHSUFF", archsuff)
|
|
|
|
for _, filename := range filenames {
|
|
path := filepath.Join("whitelist", filename)
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
// Allow not-exist errors; not all combinations have whitelists.
|
|
if os.IsNotExist(err) {
|
|
continue
|
|
}
|
|
log.Fatal(err)
|
|
}
|
|
scan := bufio.NewScanner(f)
|
|
for scan.Scan() {
|
|
line := scan.Text()
|
|
if len(line) == 0 || strings.HasPrefix(line, "//") {
|
|
continue
|
|
}
|
|
w[replace.Replace(line)]++
|
|
}
|
|
if err := scan.Err(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
type platform struct {
|
|
os string
|
|
arch string
|
|
}
|
|
|
|
func (p platform) String() string {
|
|
return p.os + "/" + p.arch
|
|
}
|
|
|
|
// ignorePathPrefixes are file path prefixes that should be ignored wholesale.
|
|
var ignorePathPrefixes = [...]string{
|
|
// These testdata dirs have lots of intentionally broken/bad code for tests.
|
|
"cmd/go/testdata/",
|
|
"cmd/vet/testdata/",
|
|
"go/printer/testdata/",
|
|
}
|
|
|
|
func vetPlatforms(pp []platform) {
|
|
for _, p := range pp {
|
|
p.vet()
|
|
}
|
|
}
|
|
|
|
func (p platform) vet() {
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "go run main.go -p %s\n", p)
|
|
|
|
// Load whitelist(s).
|
|
w := make(whitelist)
|
|
w.load(p.os, p.arch)
|
|
|
|
// 'go tool vet .' is considerably faster than 'go vet ./...'
|
|
// TODO: The unsafeptr checks are disabled for now,
|
|
// because there are so many false positives,
|
|
// and no clear way to improve vet to eliminate large chunks of them.
|
|
// And having them in the whitelists will just cause annoyance
|
|
// and churn when working on the runtime.
|
|
cmd := exec.Command(cmdGoPath, "tool", "vet", "-unsafeptr=false", "-source", ".")
|
|
cmd.Dir = filepath.Join(runtime.GOROOT(), "src")
|
|
cmd.Env = append(os.Environ(), "GOOS="+p.os, "GOARCH="+p.arch, "CGO_ENABLED=0")
|
|
stderr, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := cmd.Start(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Process vet output.
|
|
scan := bufio.NewScanner(stderr)
|
|
var parseFailed bool
|
|
NextLine:
|
|
for scan.Scan() {
|
|
line := scan.Text()
|
|
if strings.HasPrefix(line, "vet: ") {
|
|
// Typecheck failure: Malformed syntax or multiple packages or the like.
|
|
// This will yield nicer error messages elsewhere, so ignore them here.
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(line, "panic: ") {
|
|
// Panic in vet. Don't filter anything, we want the complete output.
|
|
parseFailed = true
|
|
fmt.Fprintf(os.Stderr, "panic in vet (to reproduce: go run main.go -p %s):\n", p)
|
|
fmt.Fprintln(os.Stderr, line)
|
|
io.Copy(os.Stderr, stderr)
|
|
break
|
|
}
|
|
|
|
fields := strings.SplitN(line, ":", 3)
|
|
var file, lineno, msg string
|
|
switch len(fields) {
|
|
case 2:
|
|
// vet message with no line number
|
|
file, msg = fields[0], fields[1]
|
|
case 3:
|
|
file, lineno, msg = fields[0], fields[1], fields[2]
|
|
default:
|
|
if !parseFailed {
|
|
parseFailed = true
|
|
fmt.Fprintf(os.Stderr, "failed to parse %s vet output:\n", p)
|
|
}
|
|
fmt.Fprintln(os.Stderr, line)
|
|
}
|
|
msg = strings.TrimSpace(msg)
|
|
|
|
for _, ignore := range ignorePathPrefixes {
|
|
if strings.HasPrefix(file, filepath.FromSlash(ignore)) {
|
|
continue NextLine
|
|
}
|
|
}
|
|
|
|
key := file + ": " + msg
|
|
if w[key] == 0 {
|
|
// Vet error with no match in the whitelist. Print it.
|
|
if *flagNoLines {
|
|
fmt.Fprintf(&buf, "%s: %s\n", file, msg)
|
|
} else {
|
|
fmt.Fprintf(&buf, "%s:%s: %s\n", file, lineno, msg)
|
|
}
|
|
atomic.StoreUint32(&failed, 1)
|
|
continue
|
|
}
|
|
w[key]--
|
|
}
|
|
if parseFailed {
|
|
atomic.StoreUint32(&failed, 1)
|
|
return
|
|
}
|
|
if scan.Err() != nil {
|
|
log.Fatalf("failed to scan vet output: %v", scan.Err())
|
|
}
|
|
err = cmd.Wait()
|
|
// We expect vet to fail.
|
|
// Make sure it has failed appropriately, though (for example, not a PathError).
|
|
if _, ok := err.(*exec.ExitError); !ok {
|
|
log.Fatalf("unexpected go vet execution failure: %v", err)
|
|
}
|
|
printedHeader := false
|
|
if len(w) > 0 {
|
|
for k, v := range w {
|
|
if v != 0 {
|
|
if !printedHeader {
|
|
fmt.Fprintln(&buf, "unmatched whitelist entries:")
|
|
printedHeader = true
|
|
}
|
|
for i := 0; i < v; i++ {
|
|
fmt.Fprintln(&buf, k)
|
|
}
|
|
atomic.StoreUint32(&failed, 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
os.Stdout.Write(buf.Bytes())
|
|
}
|
|
|
|
// archAsmX maps architectures to the suffix usually used for their assembly files,
|
|
// if different than the arch name itself.
|
|
var archAsmX = map[string]string{
|
|
"android": "linux",
|
|
"mips64": "mips64x",
|
|
"mips64le": "mips64x",
|
|
"mips": "mipsx",
|
|
"mipsle": "mipsx",
|
|
"ppc64": "ppc64x",
|
|
"ppc64le": "ppc64x",
|
|
}
|