Skip to content

Commit ccf04c6

Browse files
committed
cmd/go: display cached compiler output more often
CL 77110 arranged for caching and redisplaying compiler output when reusing a compile artifact from the build cache. It neglected to redisplay compiler and linker output when avoiding the compile and link steps by reusing the target output binary as a cached result. It also neglected to redisplay compiler and linker output when avoiding the compile and link (and test) steps by reusing cached test output. This CL brings back the compiler and linker output in those two cases, provided it can be found in the build cache. If it can't be found in the build cache, then the go command still reuses the binaries and avoids the compile/link/test steps. (It's not worth doing all that work again just to repeat diagnostic output.) Fixes #23877. Change-Id: I25bc054d93a88c039bcb8c5683fe4ac5cb1ee544 Reviewed-on: https://go-review.googlesource.com/128903 Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent dce644d commit ccf04c6

File tree

3 files changed

+104
-15
lines changed

3 files changed

+104
-15
lines changed

src/cmd/go/internal/work/buildid.go

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,14 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
469469
a.buildID = id[1] + buildIDSeparator + id[2]
470470
linkID := hashToString(b.linkActionID(a.triggers[0]))
471471
if id[0] == linkID {
472+
// Best effort attempt to display output from the compile and link steps.
473+
// If it doesn't work, it doesn't work: reusing the cached binary is more
474+
// important than reprinting diagnostic information.
475+
if c := cache.Default(); c != nil {
476+
showStdout(b, c, a.actionID, "stdout") // compile output
477+
showStdout(b, c, a.actionID, "link-stdout") // link output
478+
}
479+
472480
// Poison a.Target to catch uses later in the build.
473481
a.Target = "DO NOT USE - main build pseudo-cache Target"
474482
a.built = "DO NOT USE - main build pseudo-cache built"
@@ -486,6 +494,15 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
486494
// We avoid the nested build ID problem in the previous special case
487495
// by recording the test results in the cache under the action ID half.
488496
if !cfg.BuildA && len(a.triggers) == 1 && a.triggers[0].TryCache != nil && a.triggers[0].TryCache(b, a.triggers[0]) {
497+
// Best effort attempt to display output from the compile and link steps.
498+
// If it doesn't work, it doesn't work: reusing the test result is more
499+
// important than reprinting diagnostic information.
500+
if c := cache.Default(); c != nil {
501+
showStdout(b, c, a.Deps[0].actionID, "stdout") // compile output
502+
showStdout(b, c, a.Deps[0].actionID, "link-stdout") // link output
503+
}
504+
505+
// Poison a.Target to catch uses later in the build.
489506
a.Target = "DO NOT USE - pseudo-cache Target"
490507
a.built = "DO NOT USE - pseudo-cache built"
491508
return true
@@ -526,15 +543,7 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
526543
if !cfg.BuildA {
527544
if file, _, err := c.GetFile(actionHash); err == nil {
528545
if buildID, err := buildid.ReadFile(file); err == nil {
529-
if stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(a.actionID, "stdout")); err == nil {
530-
if len(stdout) > 0 {
531-
if cfg.BuildX || cfg.BuildN {
532-
b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID))))
533-
}
534-
if !cfg.BuildN {
535-
b.Print(string(stdout))
536-
}
537-
}
546+
if err := showStdout(b, c, a.actionID, "stdout"); err == nil {
538547
a.built = file
539548
a.Target = "DO NOT USE - using cache"
540549
a.buildID = buildID
@@ -555,6 +564,23 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
555564
return false
556565
}
557566

567+
func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) error {
568+
stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(actionID, key))
569+
if err != nil {
570+
return err
571+
}
572+
573+
if len(stdout) > 0 {
574+
if cfg.BuildX || cfg.BuildN {
575+
b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID))))
576+
}
577+
if !cfg.BuildN {
578+
b.Print(string(stdout))
579+
}
580+
}
581+
return nil
582+
}
583+
558584
// flushOutput flushes the output being queued in a.
559585
func (b *Builder) flushOutput(a *Action) {
560586
b.Print(string(a.output))
@@ -579,6 +605,26 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
579605
}
580606
}
581607

608+
// Cache output from compile/link, even if we don't do the rest.
609+
if c := cache.Default(); c != nil {
610+
switch a.Mode {
611+
case "build":
612+
c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output)
613+
case "link":
614+
// Even though we don't cache the binary, cache the linker text output.
615+
// We might notice that an installed binary is up-to-date but still
616+
// want to pretend to have run the linker.
617+
// Store it under the main package's action ID
618+
// to make it easier to find when that's all we have.
619+
for _, a1 := range a.Deps {
620+
if p1 := a1.Package; p1 != nil && p1.Name == "main" {
621+
c.PutBytes(cache.Subkey(a1.actionID, "link-stdout"), a.output)
622+
break
623+
}
624+
}
625+
}
626+
}
627+
582628
// Find occurrences of old ID and compute new content-based ID.
583629
r, err := os.Open(target)
584630
if err != nil {
@@ -646,7 +692,6 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
646692
}
647693
a.Package.Export = c.OutputFile(outputID)
648694
}
649-
c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output)
650695
}
651696
}
652697

src/cmd/go/internal/work/exec.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,11 +1155,11 @@ func (b *Builder) link(a *Action) (err error) {
11551155
// We still call updateBuildID to update a.buildID, which is important
11561156
// for test result caching, but passing rewrite=false (final arg)
11571157
// means we don't actually rewrite the binary, nor store the
1158-
// result into the cache.
1159-
// Not calling updateBuildID means we also don't insert these
1160-
// binaries into the build object cache. That's probably a net win:
1158+
// result into the cache. That's probably a net win:
11611159
// less cache space wasted on large binaries we are not likely to
11621160
// need again. (On the other hand it does make repeated go test slower.)
1161+
// It also makes repeated go run slower, which is a win in itself:
1162+
// we don't want people to treat go run like a scripting environment.
11631163
if err := b.updateBuildID(a, a.Target, !a.Package.Internal.OmitDebug); err != nil {
11641164
return err
11651165
}

src/cmd/go/testdata/script/build_cache_output.txt

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,58 @@ mkdir $GOCACHE
66

77
# Building a trivial non-main package should run compiler the first time.
88
go build -x -gcflags=-m lib.go
9-
stderr 'compile( |\.exe)'
9+
stderr 'compile( |\.exe"?)'
1010
stderr 'lib.go:2.* can inline f'
1111

1212
# ... but not the second, even though it still prints the compiler output.
1313
go build -x -gcflags=-m lib.go
14-
! stderr 'compile( |\.exe)'
14+
! stderr 'compile( |\.exe"?)'
1515
stderr 'lib.go:2.* can inline f'
1616

17+
# Building a trivial main package should run the compiler and linker the first time.
18+
go build -x -gcflags=-m -ldflags='-v -w' main.go
19+
stderr 'compile( |\.exe"?)'
20+
stderr 'main.go:2.* can inline main' # from compiler
21+
stderr 'link(\.exe"?)? -'
22+
stderr '\d+ symbols' # from linker
23+
24+
# ... but not the second, even though it still prints the compiler and linker output.
25+
go build -x -gcflags=-m -ldflags='-v -w' main.go
26+
! stderr 'compile( |\.exe"?)'
27+
stderr 'main.go:2.* can inline main' # from compiler
28+
! stderr 'link(\.exe"?)? -'
29+
stderr '\d+ symbols' # from linker
30+
31+
# Running a test should run the compiler, linker, and the test the first time.
32+
go test -v -x -gcflags=-m -ldflags=-v p_test.go
33+
stderr 'compile( |\.exe"?)'
34+
stderr 'p_test.go:.*can inline Test' # from compile of p_test
35+
stderr 'testmain\.go:.*inlin' # from compile of testmain
36+
stderr 'link(\.exe"?)? -'
37+
stderr '\d+ symbols' # from linker
38+
stderr 'p\.test( |\.exe"?)'
39+
stdout 'TEST' # from test
40+
41+
# ... but not the second, even though it still prints the compiler, linker, and test output.
42+
go test -v -x -gcflags=-m -ldflags=-v p_test.go
43+
! stderr 'compile( |\.exe"?)'
44+
stderr 'p_test.go:.*can inline Test' # from compile of p_test
45+
stderr 'testmain\.go:.*inlin' # from compile of testmain
46+
! stderr 'link(\.exe"?)? -'
47+
stderr '\d+ symbols' # from linker
48+
! stderr 'p\.test( |\.exe"?)'
49+
stdout 'TEST' # from test
50+
51+
1752
-- lib.go --
1853
package p
1954
func f(x *int) *int { return x }
55+
56+
-- main.go --
57+
package main
58+
func main() {}
59+
60+
-- p_test.go --
61+
package p
62+
import "testing"
63+
func Test(t *testing.T) {println("TEST")}

0 commit comments

Comments
 (0)