return Entry{buf, size, time.Unix(0, tm)}, nil
}
+// GetFile looks up the action ID in the cache and returns
+// the name of the corresponding data file.
+func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {
+ entry, err = c.Get(id)
+ if err != nil {
+ return "", Entry{}, err
+ }
+ file = c.OutputFile(entry.OutputID)
+ info, err := os.Stat(file)
+ if err != nil || info.Size() != entry.Size {
+ return "", Entry{}, errMissing
+ }
+ return file, entry, nil
+}
+
// GetBytes looks up the action ID in the cache and returns
// the corresponding output bytes.
// GetBytes should only be used for data that can be expected to fit in memory.
}
switch f.Name {
// Flags known to the build but not to vet, so must be dropped.
- case "x", "n", "vettool", "compiler":
+ case "a", "x", "n", "vettool", "compiler":
if extraWord {
args = append(args[:i], args[i+2:]...)
extraWord = false
actionID cache.ActionID // cache ID of action input
buildID string // build ID of action output
- needVet bool // Mode=="build": need to fill in vet config
- vetCfg *vetConfig // vet config
- output []byte // output redirect buffer (nil means use b.Print)
+ VetxOnly bool // Mode=="vet": only being called to supply info about dependencies
+ needVet bool // Mode=="build": need to fill in vet config
+ vetCfg *vetConfig // vet config
+ output []byte // output redirect buffer (nil means use b.Print)
// Execution state.
pending int // number of deps yet to complete
Priority int `json:",omitempty"`
Failed bool `json:",omitempty"`
Built string `json:",omitempty"`
+ VetxOnly bool `json:",omitempty"`
}
// cacheKey is the key for the action cache.
Failed: a.Failed,
Priority: a.priority,
Built: a.built,
+ VetxOnly: a.VetxOnly,
}
if a.Package != nil {
// TODO(rsc): Make this a unique key for a.Package somehow.
// If the caller may be causing p to be installed, it is up to the caller
// to make sure that the install depends on (runs after) vet.
func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action {
+ a := b.vetAction(mode, depMode, p)
+ a.VetxOnly = false
+ return a
+}
+
+func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
// Construct vet action.
a := b.cacheAction("vet", p, func() *Action {
a1 := b.CompileAction(mode, depMode, p)
stk.Pop()
aFmt := b.CompileAction(ModeBuild, depMode, p1)
+ deps := []*Action{a1, aFmt}
+ for _, p1 := range load.PackageList(p.Internal.Imports) {
+ deps = append(deps, b.vetAction(mode, depMode, p1))
+ }
+
a := &Action{
- Mode: "vet",
- Package: p,
- Deps: []*Action{a1, aFmt},
- Objdir: a1.Objdir,
+ Mode: "vet",
+ Package: p,
+ Deps: deps,
+ Objdir: a1.Objdir,
+ VetxOnly: true,
+ IgnoreFail: true, // it's OK if vet of dependencies "fails" (reports problems)
}
if a1.Func == nil {
// Built-in packages like unsafe.
}
a1.needVet = true
a.Func = (*Builder).vet
-
return a
})
return a
return id
}
- cmdline := str.StringList(cfg.BuildToolexec, base.Tool(name), "-V=full")
+ path := base.Tool(name)
+ desc := "go tool " + name
+
+ // Special case: undocumented -vettool overrides usual vet, for testing vet.
+ if name == "vet" && VetTool != "" {
+ path = VetTool
+ desc = VetTool
+ }
+
+ cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
- base.Fatalf("go tool %s: %v\n%s%s", name, err, stdout.Bytes(), stderr.Bytes())
+ base.Fatalf("%s: %v\n%s%s", desc, err, stdout.Bytes(), stderr.Bytes())
}
line := stdout.String()
f := strings.Fields(line)
- if len(f) < 3 || f[0] != name || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") {
- base.Fatalf("go tool %s -V=full: unexpected output:\n\t%s", name, line)
+ if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") {
+ base.Fatalf("%s -V=full: unexpected output:\n\t%s", desc, line)
}
if f[2] == "devel" {
// On the development branch, use the content ID part of the build ID.
// but we're still happy to use results from the build artifact cache.
if c := cache.Default(); c != nil {
if !cfg.BuildA {
- entry, err := c.Get(actionHash)
- if err == nil {
- file := c.OutputFile(entry.OutputID)
- info, err1 := os.Stat(file)
- buildID, err2 := buildid.ReadFile(file)
- if err1 == nil && err2 == nil && info.Size() == entry.Size {
- stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(a.actionID, "stdout"))
- if err == nil {
+ if file, _, err := c.GetFile(actionHash); err == nil {
+ if buildID, err := buildid.ReadFile(file); err == nil {
+ if stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(a.actionID, "stdout")); err == nil {
if len(stdout) > 0 {
if cfg.BuildX || cfg.BuildN {
b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID))))
}
func (b *Builder) findCachedObjdirFile(a *Action, c *cache.Cache, name string) (string, error) {
- entry, err := c.Get(cache.Subkey(a.actionID, name))
+ file, _, err := c.GetFile(cache.Subkey(a.actionID, name))
if err != nil {
return "", err
}
- out := c.OutputFile(entry.OutputID)
- info, err := os.Stat(out)
- if err != nil || info.Size() != entry.Size {
- return "", fmt.Errorf("not in cache")
- }
- return out, nil
+ return file, nil
}
func (b *Builder) loadCachedObjdirFile(a *Action, c *cache.Cache, name string) error {
return true
}
+// vetConfig is the configuration passed to vet describing a single package.
type vetConfig struct {
- Compiler string
- Dir string
- GoFiles []string
- ImportMap map[string]string
- PackageFile map[string]string
- Standard map[string]bool
- ImportPath string
+ Compiler string // compiler name (gc, gccgo)
+ Dir string // directory containing package
+ ImportPath string // canonical import path ("package path")
+ GoFiles []string // absolute paths to package source files
+
+ ImportMap map[string]string // map import path in source code to package path
+ PackageFile map[string]string // map package path to .a file with export data
+ Standard map[string]bool // map package path to whether it's in the standard library
+ PackageVetx map[string]string // map package path to vetx data from earlier vet run
+ VetxOnly bool // only compute vetx data; don't report detected problems
+ VetxOutput string // write vetx data to this output file
- SucceedOnTypecheckFailure bool
+ SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
}
func buildVetConfig(a *Action, gofiles []string) {
// a.Deps[0] is the build of the package being vetted.
// a.Deps[1] is the build of the "fmt" package.
+ a.Failed = false // vet of dependency may have failed but we can still succeed
+
vcfg := a.Deps[0].vetCfg
if vcfg == nil {
// Vet config should only be missing if the build failed.
return nil
}
+ vcfg.VetxOnly = a.VetxOnly
+ vcfg.VetxOutput = a.Objdir + "vet.out"
+ vcfg.PackageVetx = make(map[string]string)
+
+ h := cache.NewHash("vet " + a.Package.ImportPath)
+ fmt.Fprintf(h, "vet %q\n", b.toolID("vet"))
+
+ // Note: We could decide that vet should compute export data for
+ // all analyses, in which case we don't need to include the flags here.
+ // But that would mean that if an analysis causes problems like
+ // unexpected crashes there would be no way to turn it off.
+ // It seems better to let the flags disable export analysis too.
+ fmt.Fprintf(h, "vetflags %q\n", VetFlags)
+
+ fmt.Fprintf(h, "pkg %q\n", a.Deps[0].actionID)
+ for _, a1 := range a.Deps {
+ if a1.Mode == "vet" && a1.built != "" {
+ fmt.Fprintf(h, "vetout %q %s\n", a1.Package.ImportPath, b.fileHash(a1.built))
+ vcfg.PackageVetx[a1.Package.ImportPath] = a1.built
+ }
+ }
+ key := cache.ActionID(h.Sum())
+
+ if vcfg.VetxOnly {
+ if c := cache.Default(); c != nil && !cfg.BuildA {
+ if file, _, err := c.GetFile(key); err == nil {
+ a.built = file
+ return nil
+ }
+ }
+ }
+
if vcfg.ImportMap["fmt"] == "" {
a1 := a.Deps[1]
vcfg.ImportMap["fmt"] = "fmt"
if tool == "" {
tool = base.Tool("vet")
}
- return b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg")
+ runErr := b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg")
+
+ // If vet wrote export data, save it for input to future vets.
+ if f, err := os.Open(vcfg.VetxOutput); err == nil {
+ a.built = vcfg.VetxOutput
+ if c := cache.Default(); c != nil {
+ c.Put(key, f)
+ }
+ f.Close()
+ }
+
+ return runErr
}
// linkActionID computes the action ID for a link action.