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