sweet: rebuild cmd/compile and cmd/link for go-build benchmark

go-build is a subtly strange benchmark. For every other benchmark, sweet
builds the benchmarked program in set up, using Config.BuildEnv.
go-build does not do that because the benchmarked programs (cmd/compile,
cmd/link, and (to a lesser degree) cmd/go) are conveniently prebuilt in
GOROOT.

But this breaks the intuition that BuildEnv can control build flags for
the benchmarked program, particularly for sweet run -pgo, where sweet
itself expects a PGO flag in BuildEnv to impact the benchmarked program.

Adjust go-build to follow these standards by copying GOROOT for each
config and running `go install cmd/compile cmd/link` in the copied
GOROOT, allowing BuildEnv to take effect.

This fixes the general case for PGO, though it could use more work as
really cmd/compile and cmd/link should receive _different_ PGO profiles.

We also run the dummy build with the ExecEnv (matching how the actual
benchmark will run) to avoid passing -pgo to those builds.

For golang/go#55022.

Change-Id: I36e4486c79ee4200c2e10ccba913d559187bdad2
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/444895
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/sweet/harnesses/go-build.go b/sweet/harnesses/go-build.go
index a55113e..3b3e278 100644
--- a/sweet/harnesses/go-build.go
+++ b/sweet/harnesses/go-build.go
@@ -10,6 +10,7 @@
 	"path/filepath"
 
 	"golang.org/x/benchmarks/sweet/common"
+	"golang.org/x/benchmarks/sweet/common/fileutil"
 	"golang.org/x/benchmarks/sweet/common/log"
 )
 
@@ -72,12 +73,30 @@
 	return nil
 }
 
-func (h GoBuild) Build(cfg *common.Config, bcfg *common.BuildConfig) error {
+func (h GoBuild) Build(pcfg *common.Config, bcfg *common.BuildConfig) error {
+	// Local copy of config for updating GOROOT.
+	cfg := pcfg.Copy()
+
 	benchmarks := buildBenchmarks
 	if bcfg.Short {
 		// Do only the pkgsite benchmark.
 		benchmarks = []*buildBenchmark{buildBenchmarks[2]}
 	}
+
+	// cfg.GoRoot is our source toolchain. We need to rebuild cmd/compile
+	// and cmd/link with cfg.BuildEnv to apply any configured build options
+	// (e.g., PGO).
+	//
+	// Do so by `go install`ing them into a copied GOROOT.
+	goroot := filepath.Join(bcfg.BinDir, "goroot")
+	if err := fileutil.CopyDir(goroot, cfg.GoRoot, nil); err != nil {
+		return fmt.Errorf("error copying GOROOT: %v", err)
+	}
+	cfg.GoRoot = goroot
+	if err := cfg.GoTool().Do("install", "cmd/compile", "cmd/link"); err != nil {
+		return fmt.Errorf("error building cmd/compile and cmd/link: %v", err)
+	}
+
 	for _, bench := range benchmarks {
 		// Generate a symlink to the repository and put it in bin.
 		// It's not a binary, but it's the only place we can put it
@@ -89,9 +108,14 @@
 		}
 
 		// Build the benchmark once, pulling in any requisite packages.
+		//
+		// Run the go tool with ExecEnv, as that is what we will use
+		// when benchmarking.
 		pkgPath := filepath.Join(bcfg.BinDir, bench.name, bench.pkg)
 		dummyBin := filepath.Join(bcfg.BinDir, "dummy")
-		if err := cfg.GoTool().BuildPath(pkgPath, dummyBin); err != nil {
+		goTool := cfg.GoTool()
+		goTool.Env = cfg.ExecEnv.MustSet("GOROOT=" + cfg.GoRoot)
+		if err := goTool.BuildPath(pkgPath, dummyBin); err != nil {
 			return fmt.Errorf("error building %s %s: %w", bench.name, bench.pkg, err)
 		}
 	}
@@ -102,7 +126,11 @@
 	return nil
 }
 
-func (h GoBuild) Run(cfg *common.Config, rcfg *common.RunConfig) error {
+func (h GoBuild) Run(pcfg *common.Config, rcfg *common.RunConfig) error {
+	// Local copy of config for updating GOROOT.
+	cfg := pcfg.Copy()
+	cfg.GoRoot = filepath.Join(rcfg.BinDir, "goroot") // see Build, above.
+
 	benchmarks := buildBenchmarks
 	if rcfg.Short {
 		// Do only the pkgsite benchmark.