Commit 7a567a63 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

cmd/dist: support using cross-compiled std test binaries for slow builders

We want the builders to be able to cross-compile test binaries for a
few of the super slow builders that require either slow hardware or
slow full CPU emulation.

Updates golang/go#31217

Change-Id: I8d33b18efaf788f6f131354b2917ac9738ca975e
Reviewed-on: https://go-review.googlesource.com/c/go/+/178399
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent d307bd4e
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp" "regexp"
...@@ -277,8 +278,17 @@ func (t *tester) tags() string { ...@@ -277,8 +278,17 @@ func (t *tester) tags() string {
return "-tags=" return "-tags="
} }
// timeoutDuration converts the provided number of seconds into a
// time.Duration, scaled by the t.timeoutScale factor.
func (t *tester) timeoutDuration(sec int) time.Duration {
return time.Duration(sec) * time.Second * time.Duration(t.timeoutScale)
}
// timeout returns the "-timeout=" string argument to "go test" given
// the number of seconds of timeout. It scales it by the
// t.timeoutScale factor.
func (t *tester) timeout(sec int) string { func (t *tester) timeout(sec int) string {
return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale)) return "-timeout=" + t.timeoutDuration(sec).String()
} }
// ranGoTest and stdMatches are state closed over by the stdlib // ranGoTest and stdMatches are state closed over by the stdlib
...@@ -319,6 +329,11 @@ func (t *tester) registerStdTest(pkg string) { ...@@ -319,6 +329,11 @@ func (t *tester) registerStdTest(pkg string) {
break break
} }
} }
// Special case for our slow cross-compiled
// qemu builders:
if t.shouldUsePrecompiledStdTest() {
return t.runPrecompiledStdTest(t.timeoutDuration(timeoutSec))
}
args := []string{ args := []string{
"test", "test",
short(), short(),
...@@ -1416,6 +1431,60 @@ func (t *tester) makeGOROOTUnwritable() { ...@@ -1416,6 +1431,60 @@ func (t *tester) makeGOROOTUnwritable() {
} }
} }
// shouldUsePrecompiledStdTest reports whether "dist test" should use
// a pre-compiled go test binary on disk rather than running "go test"
// and compiling it again. This is used by our slow qemu-based builder
// that do full processor emulation where we cross-compile the
// make.bash step as well as pre-compile each std test binary.
//
// This only reports true if dist is run with an single go_test:foo
// argument (as the build coordinator does with our slow qemu-based
// builders), we're in a builder environment ("GO_BUILDER_NAME" is set),
// and the pre-built test binary exists.
func (t *tester) shouldUsePrecompiledStdTest() bool {
bin := t.prebuiltGoPackageTestBinary()
if bin == "" {
return false
}
_, err := os.Stat(bin)
return err == nil
}
// prebuiltGoPackageTestBinary returns the path where we'd expect
// the pre-built go test binary to be on disk when dist test is run with
// a single argument.
// It returns an empty string if a pre-built binary should not be used.
func (t *tester) prebuiltGoPackageTestBinary() string {
if len(stdMatches) != 1 || t.race || t.compileOnly || os.Getenv("GO_BUILDER_NAME") == "" {
return ""
}
pkg := stdMatches[0]
return filepath.Join(os.Getenv("GOROOT"), "src", pkg, path.Base(pkg)+".test")
}
// runPrecompiledStdTest runs the pre-compiled standard library package test binary.
// See shouldUsePrecompiledStdTest above; it must return true for this to be called.
func (t *tester) runPrecompiledStdTest(timeout time.Duration) error {
bin := t.prebuiltGoPackageTestBinary()
fmt.Fprintf(os.Stderr, "# %s: using pre-built %s...\n", stdMatches[0], bin)
cmd := exec.Command(bin, "-test.short", "-test.timeout="+timeout.String())
cmd.Dir = filepath.Dir(bin)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return err
}
// And start a timer to kill the process if it doesn't kill
// itself in the prescribed timeout.
const backupKillFactor = 1.05 // add 5%
timer := time.AfterFunc(time.Duration(float64(timeout)*backupKillFactor), func() {
fmt.Fprintf(os.Stderr, "# %s: timeout running %s; killing...\n", stdMatches[0], bin)
cmd.Process.Kill()
})
defer timer.Stop()
return cmd.Wait()
}
// raceDetectorSupported is a copy of the function // raceDetectorSupported is a copy of the function
// cmd/internal/sys.RaceDetectorSupported, which can't be used here // cmd/internal/sys.RaceDetectorSupported, which can't be used here
// because cmd/dist has to be buildable by Go 1.4. // because cmd/dist has to be buildable by Go 1.4.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment