]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/dist: support using cross-compiled std test binaries for slow builders
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 21 May 2019 19:02:00 +0000 (19:02 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 23 May 2019 01:38:26 +0000 (01:38 +0000)
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: Ian Lance Taylor <iant@golang.org>
src/cmd/dist/test.go

index 833baf8f915ac1ebcde8b6cf1924f4bf578b3d9f..f63c94697c85c8b12fa7a1aeed382adc0ff8e11d 100644 (file)
@@ -12,6 +12,7 @@ import (
        "log"
        "os"
        "os/exec"
+       "path"
        "path/filepath"
        "reflect"
        "regexp"
@@ -277,8 +278,17 @@ func (t *tester) tags() string {
        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 {
-       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
@@ -319,6 +329,11 @@ func (t *tester) registerStdTest(pkg string) {
                                        break
                                }
                        }
+                       // Special case for our slow cross-compiled
+                       // qemu builders:
+                       if t.shouldUsePrecompiledStdTest() {
+                               return t.runPrecompiledStdTest(t.timeoutDuration(timeoutSec))
+                       }
                        args := []string{
                                "test",
                                short(),
@@ -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
 // cmd/internal/sys.RaceDetectorSupported, which can't be used here
 // because cmd/dist has to be buildable by Go 1.4.