]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go/internal/work: copy vet tool's stdout to our stdout
authorAlan Donovan <adonovan@google.com>
Thu, 11 Sep 2025 15:02:03 +0000 (11:02 -0400)
committerGopher Robot <gobot@golang.org>
Fri, 12 Sep 2025 21:16:27 +0000 (14:16 -0700)
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 <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>

src/cmd/go/internal/work/exec.go
src/cmd/go/testdata/script/vet_asm.txt

index b68795b9171de38553c154c5e53b3a25e3b26240..51cb2e5a0411df14fa763ba7417c3edb9e06c2ef 100644 (file)
@@ -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
index ae2db97794f040b348be0f522b1e626836b7a02a..8aa69ce1a3c99965a417a5b444f0233007fc75eb 100644 (file)
@@ -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