sweet: apply flags to cockroachdb 'go build' based on 'go version'

Currently, the cockroachdb benchmark is built by passing a build tag
(untested_go_version) unconditionally, and we also try to build with and
without '-checklinkname=0' passed to the linker. These are required
because some CockroachDB packages reach into the runtime internals.

The current approach doesn't work well across Go versions because
setting "untested_go_version" for older toolchains causes a symbol
conflict (it ends up enabling multiple files with the same definition).
We can fix this upstream, but we also need a fix in the short term.
Plus, it'll help to clean up this logic and make it more explicit.

So, this CL parses 'go version' in a dead-simple way to determine what
to do. If we're using an older version of Go and the version is
well-defined, then we never use 'untested_go_version'. But if the
version of Go is explicitly a development version or newer than Go
1.24, then we do.

Simultaneously, we only pass -checklinkname=0 to the linker if the
toolchain version is >= 1.23. We always pass it for development Go
versions.

Fixes golang/go#68555.

Change-Id: Ieaad89cb6a92bdeee2636dc62b5b3a26ac317b5f
Cq-Include-Trybots: luci.golang.try:x_benchmarks-gotip-linux-amd64-longtest,x_benchmarks-go1.22-linux-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/600475
Reviewed-by: Austin Clements <austin@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>
diff --git a/sweet/common/gotool.go b/sweet/common/gotool.go
index 08dd1a7..3fc2b6d 100644
--- a/sweet/common/gotool.go
+++ b/sweet/common/gotool.go
@@ -72,6 +72,17 @@
 	return g.Do("", "build", "-o", out, pkg)
 }
 
+func (g *Go) Version() (string, error) {
+	cmd := exec.Command(g.Tool, "version")
+	cmd.Env = g.Env.Collapse()
+	log.TraceCommand(cmd, false)
+	out, err := cmd.Output()
+	if err != nil {
+		return "", fmt.Errorf("error running 'go version': %w", err)
+	}
+	return string(out), nil
+}
+
 func (g *Go) BuildPath(path, out string, args ...string) error {
 	if path[0] != '/' && path[0] != '.' {
 		path = "./" + path
diff --git a/sweet/harnesses/cockroachdb.go b/sweet/harnesses/cockroachdb.go
index 75a4b86..04cecb7 100644
--- a/sweet/harnesses/cockroachdb.go
+++ b/sweet/harnesses/cockroachdb.go
@@ -5,12 +5,12 @@
 package harnesses
 
 import (
-	"errors"
 	"fmt"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
+	"strings"
 	"time"
 
 	"golang.org/x/benchmarks/sweet/common"
@@ -98,22 +98,32 @@
 		return err
 	}
 
-	// Finally build the cockroach binary with `go build`. Build the
-	// cockroach-short binary as it is functionally the same, but
-	// without the UI, making it much quicker to build.
+	// Get the Go version. Then, finall build the cockroach binary with `go build`.
 	//
-	// As of go1.23, we need to pass the `-ldflags=-checklinkname=0` flag
-	// to build cockroach. However, benchmark release branches are on older
-	// versions that don't recognize the flag. Try first with the flag and
-	// again without if there is an error.
+	// Build the cockroach-short binary as it is functionally the same, but without
+	// the UI, making it much quicker to build.
 	//
-	// Cockroachdb reaches into runtime internals, so it has a negative build
-	// tag to exclude unreleased Go versions. Set untested_go_version so we can
-	// run at tip (and cross our fingers).
-	if buildWithFlagErr := cfg.GoTool().BuildPath(filepath.Join(bcfg.SrcDir, "pkg/cmd/cockroach-short"), bcfg.BinDir, "-tags=untested_go_version", "-ldflags=-checklinkname=0"); buildWithFlagErr != nil {
-		if buildWithoutFlagErr := cfg.GoTool().BuildPath(filepath.Join(bcfg.SrcDir, "pkg/cmd/cockroach-short"), bcfg.BinDir, "-tags=untested_go_version"); buildWithoutFlagErr != nil {
-			return errors.Join(buildWithFlagErr, buildWithoutFlagErr)
-		}
+	// CockroachDB reaches into runtime internals, so we need to pass -checklinkname=0
+	// to the linker to get it to build with Go 1.23+. Also, CockroachDB has a negative
+	// build tag to exclude unreleased Go versions, so we need to explicitly set it for
+	// new-enough Go versions.
+	//
+	// Note that since the version of CockroachDB is pinned, we can generally rely on
+	// the checking below. However as CockroachDB and Go evolve, the logic below may need
+	// to change.
+	ver, err := cfg.GoTool().Version()
+	if err != nil {
+		return fmt.Errorf("getting go version for toolchain: %v", err)
+	}
+	var goBuildArgs []string
+	if v := strings.TrimPrefix(ver, "go version "); strings.HasPrefix(v, "devel ") || v >= "go1.23" {
+		goBuildArgs = append(goBuildArgs, "-ldflags=-checklinkname=0")
+	}
+	if v := strings.TrimPrefix(ver, "go version "); strings.HasPrefix(v, "devel ") || v >= "go1.24" {
+		goBuildArgs = append(goBuildArgs, "-tags=untested_go_version")
+	}
+	if err := cfg.GoTool().BuildPath(filepath.Join(bcfg.SrcDir, "pkg/cmd/cockroach-short"), bcfg.BinDir, goBuildArgs...); err != nil {
+		return err
 	}
 
 	// Rename the binary from cockroach-short to cockroach for
@@ -126,7 +136,6 @@
 	if err := cfg.GoTool().BuildPath(bcfg.BenchDir, filepath.Join(bcfg.BinDir, "cockroachdb-bench")); err != nil {
 		return err
 	}
-
 	return nil
 }