From: Alan Donovan Date: Thu, 11 Sep 2025 15:02:03 +0000 (-0400) Subject: cmd/go/internal/work: copy vet tool's stdout to our stdout X-Git-Tag: go1.26rc1~882 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ac803b5949f6dbc5bfa559afe506d35f9e1b3195;p=gostls13.git cmd/go/internal/work: copy vet tool's stdout to our stdout The go command connects both the stdout and stderr files of its child commands (cmd/compile, cmd/vet, etc) to the go command's own stderr. If the child command is supposed to produce structure output on stderr, as is the case for go vet -json or go fix -diff, it will be merged with the error stream, making it useless. This change to the go vet <-> unitchecker protocol specifies the name of a file into which the vet tool should write its stdout. On success, the go command will then copy the entire content of that file to its own stdout, under a lock. This ensures that partial writes to stdout in case of failure, concurrent writes to stdout by parallel vet tasks, or other junk on stderr, cannot interfere with the integrity of the go command's structure output on stdout. CL 702835 is the corresponding change on the x/tools side. For #75432 Change-Id: Ib4db25b6b0095d359152d7543bd9bf692551bbfa Reviewed-on: https://go-review.googlesource.com/c/go/+/702815 LUCI-TryBot-Result: Go LUCI Reviewed-by: Michael Matloob Auto-Submit: Alan Donovan --- diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index b68795b917..51cb2e5a04 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -1184,6 +1184,7 @@ type vetConfig struct { 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 + Stdout string // write stdout (JSON, unified diff) to this output file GoVersion string // Go version for package SucceedOnTypecheckFailure bool // awful hack; see #18395 and below @@ -1297,6 +1298,7 @@ func (b *Builder) vet(ctx context.Context, a *Action) error { vcfg.VetxOnly = a.VetxOnly vcfg.VetxOutput = a.Objdir + "vet.out" + vcfg.Stdout = a.Objdir + "vet.stdout" vcfg.PackageVetx = make(map[string]string) h := cache.NewHash("vet " + a.Package.ImportPath) @@ -1392,13 +1394,25 @@ func (b *Builder) vet(ctx context.Context, a *Action) error { // 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 - cache.Default().Put(key, f) - f.Close() + cache.Default().Put(key, f) // ignore error + f.Close() // ignore error + } + + // If vet wrote to stdout, copy it to go's stdout, atomically. + if f, err := os.Open(vcfg.Stdout); err == nil { + stdoutMu.Lock() + if _, err := io.Copy(os.Stdout, f); err != nil && runErr == nil { + runErr = fmt.Errorf("copying vet tool stdout: %w", err) + } + f.Close() // ignore error + stdoutMu.Unlock() } return runErr } +var stdoutMu sync.Mutex // serializes concurrent writes (e.g. JSON values) to stdout + // linkActionID computes the action ID for a link action. func (b *Builder) linkActionID(a *Action) cache.ActionID { p := a.Package diff --git a/src/cmd/go/testdata/script/vet_asm.txt b/src/cmd/go/testdata/script/vet_asm.txt index ae2db97794..8aa69ce1a3 100644 --- a/src/cmd/go/testdata/script/vet_asm.txt +++ b/src/cmd/go/testdata/script/vet_asm.txt @@ -14,12 +14,13 @@ stderr '2 MOVW' stderr '3 RET' stderr '4' -# -json causes success, even with diagnostics and errors. +# -json causes success, even with diagnostics and errors, +# and writes to stdout. go vet -json -asmdecl a -stderr '"a": {' -stderr '"asmdecl":' -stderr '"posn": ".*asm.s:2:1",' -stderr '"message": ".*invalid MOVW.*"' +stdout '"a": {' +stdout '"asmdecl":' +stdout '"posn": ".*asm.s:2:1",' +stdout '"message": ".*invalid MOVW.*"' -- a/a.go -- package a