Commit 38431f10 authored by Russ Cox's avatar Russ Cox

cmd/go: do not build test packages unnecessarily during go vet

Vet needs export data for the imports of the package it is analyzing.
Vet does not need export data for the package itself, since vet will
do its own type checking. Assuming that vet is just as good as the compiler
at detecting invalid programs, don't run the compiler unnecessarily.

This especially matters for tests without external test files or for
which the external test files do not import the test-augmented original
package. In that case, the test-augmented original package need not
be compiled at all.

Cuts time for 'go clean -cache && go vet -x cmd/compile/internal/ssa'
from 7.6r 24.3u 2.8s to 3.5r 8.5u 1.9s, by not running the compiler
on the augmented test package.

There is still more to be done here - if we do need to build a
test-augmented package, we rerun cgo unnecessarily.
But this is a big help.

Cuts time for 'go vet std cmd' by about 30%.

For #31916.

Change-Id: If6136b4d384f1da77aed90b43f1a6b95f09b5d86
Reviewed-on: https://go-review.googlesource.com/c/go/+/176438
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent ba23fa48
...@@ -5263,8 +5263,14 @@ func TestCacheVet(t *testing.T) { ...@@ -5263,8 +5263,14 @@ func TestCacheVet(t *testing.T) {
if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") { if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
t.Skip("GODEBUG gocacheverify") t.Skip("GODEBUG gocacheverify")
} }
if testing.Short() {
// In short mode, reuse cache.
// Test failures may be masked if the cache has just the right entries already
// (not a concern during all.bash, which runs in a clean cache).
if cfg.Getenv("GOCACHE") == "off" { if cfg.Getenv("GOCACHE") == "off" {
tooSlow(t) tooSlow(t)
}
} else {
tg.makeTempdir() tg.makeTempdir()
tg.setenv("GOCACHE", tg.path("cache")) tg.setenv("GOCACHE", tg.path("cache"))
} }
......
...@@ -86,6 +86,7 @@ type Action struct { ...@@ -86,6 +86,7 @@ type Action struct {
VetxOnly bool // Mode=="vet": only being called to supply info about dependencies VetxOnly bool // Mode=="vet": only being called to supply info about dependencies
needVet bool // Mode=="build": need to fill in vet config needVet bool // Mode=="build": need to fill in vet config
needBuild bool // Mode=="build": need to do actual build (can be false if needVet is true)
vetCfg *vetConfig // vet config vetCfg *vetConfig // vet config
output []byte // output redirect buffer (nil means use b.Print) output []byte // output redirect buffer (nil means use b.Print)
...@@ -212,6 +213,8 @@ const ( ...@@ -212,6 +213,8 @@ const (
ModeBuild BuildMode = iota ModeBuild BuildMode = iota
ModeInstall ModeInstall
ModeBuggyInstall ModeBuggyInstall
ModeVetOnly = 1 << 8
) )
func (b *Builder) Init() { func (b *Builder) Init() {
...@@ -354,6 +357,9 @@ func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action { ...@@ -354,6 +357,9 @@ func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action {
// depMode is the action (build or install) to use when building dependencies. // depMode is the action (build or install) to use when building dependencies.
// To turn package main into an executable, call b.Link instead. // To turn package main into an executable, call b.Link instead.
func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action { func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {
vetOnly := mode&ModeVetOnly != 0
mode &^= ModeVetOnly
if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" { if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" {
// Imported via local path or using modules. No permanent target. // Imported via local path or using modules. No permanent target.
mode = ModeBuild mode = ModeBuild
...@@ -400,6 +406,19 @@ func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Actio ...@@ -400,6 +406,19 @@ func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Actio
return a return a
}) })
// Find the build action; the cache entry may have been replaced
// by the install action during (*Builder).installAction.
buildAction := a
switch buildAction.Mode {
case "build", "built-in package":
// ok
case "build-install":
buildAction = a.Deps[0]
default:
panic("lost build action: " + buildAction.Mode)
}
buildAction.needBuild = buildAction.needBuild || !vetOnly
// Construct install action. // Construct install action.
if mode == ModeInstall || mode == ModeBuggyInstall { if mode == ModeInstall || mode == ModeBuggyInstall {
a = b.installAction(a, mode) a = b.installAction(a, mode)
...@@ -421,7 +440,7 @@ func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action { ...@@ -421,7 +440,7 @@ func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action {
func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action { func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
// Construct vet action. // Construct vet action.
a := b.cacheAction("vet", p, func() *Action { a := b.cacheAction("vet", p, func() *Action {
a1 := b.CompileAction(mode, depMode, p) a1 := b.CompileAction(mode|ModeVetOnly, depMode, p)
// vet expects to be able to import "fmt". // vet expects to be able to import "fmt".
var stk load.ImportStack var stk load.ImportStack
......
...@@ -367,8 +367,8 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -367,8 +367,8 @@ func (b *Builder) build(a *Action) (err error) {
return 0 return 0
} }
cached := false cachedBuild := false
need := bit(needBuild, !b.IsCmdList || b.NeedExport) | need := bit(needBuild, !b.IsCmdList && a.needBuild || b.NeedExport) |
bit(needCgoHdr, b.needCgoHdr(a)) | bit(needCgoHdr, b.needCgoHdr(a)) |
bit(needVet, a.needVet) | bit(needVet, a.needVet) |
bit(needCompiledGoFiles, b.NeedCompiledGoFiles) bit(needCompiledGoFiles, b.NeedCompiledGoFiles)
...@@ -377,6 +377,11 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -377,6 +377,11 @@ func (b *Builder) build(a *Action) (err error) {
if b.useCache(a, p, b.buildActionID(a), p.Target) { if b.useCache(a, p, b.buildActionID(a), p.Target) {
// We found the main output in the cache. // We found the main output in the cache.
// If we don't need any other outputs, we can stop. // If we don't need any other outputs, we can stop.
// Otherwise, we need to write files to a.Objdir (needVet, needCgoHdr).
// Remember that we might have them in cache
// and check again after we create a.Objdir.
cachedBuild = true
a.output = []byte{} // start saving output in case we miss any cache results
need &^= needBuild need &^= needBuild
if b.NeedExport { if b.NeedExport {
p.Export = a.built p.Export = a.built
...@@ -384,16 +389,11 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -384,16 +389,11 @@ func (b *Builder) build(a *Action) (err error) {
if need&needCompiledGoFiles != 0 && b.loadCachedSrcFiles(a) { if need&needCompiledGoFiles != 0 && b.loadCachedSrcFiles(a) {
need &^= needCompiledGoFiles need &^= needCompiledGoFiles
} }
// Otherwise, we need to write files to a.Objdir (needVet, needCgoHdr).
// Remember that we might have them in cache
// and check again after we create a.Objdir.
cached = true
a.output = []byte{} // start saving output in case we miss any cache results
} }
// Source files might be cached, even if the full action is not // Source files might be cached, even if the full action is not
// (e.g., go list -compiled -find). // (e.g., go list -compiled -find).
if !cached && need&needCompiledGoFiles != 0 && b.loadCachedSrcFiles(a) { if !cachedBuild && need&needCompiledGoFiles != 0 && b.loadCachedSrcFiles(a) {
need &^= needCompiledGoFiles need &^= needCompiledGoFiles
} }
...@@ -438,8 +438,8 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -438,8 +438,8 @@ func (b *Builder) build(a *Action) (err error) {
} }
objdir := a.Objdir objdir := a.Objdir
if cached { // Load cached cgo header, but only if we're skipping the main build (cachedBuild==true).
if need&needCgoHdr != 0 && b.loadCachedCgoHdr(a) { if cachedBuild && need&needCgoHdr != 0 && b.loadCachedCgoHdr(a) {
need &^= needCgoHdr need &^= needCgoHdr
} }
...@@ -453,7 +453,6 @@ func (b *Builder) build(a *Action) (err error) { ...@@ -453,7 +453,6 @@ func (b *Builder) build(a *Action) (err error) {
if need == 0 { if need == 0 {
return nil return nil
} }
}
// make target directory // make target directory
dir, _ := filepath.Split(a.Target) dir, _ := filepath.Split(a.Target)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment