1
0
mirror of https://github.com/golang/go synced 2024-11-08 06:26:19 -07:00
go/src/cmd/dist/test.go

1453 lines
40 KiB
Go
Raw Normal View History

// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"time"
)
func cmdtest() {
cmd/go: switch to entirely content-based staleness determination This CL changes the go command to base all its rebuilding decisions on the content of the files being processed and not their file system modification times. It also eliminates the special handling of release toolchains, which were previously considered always up-to-date because modification time order could not be trusted when unpacking a pre-built release. The go command previously tracked "build IDs" as a backup to modification times, to catch changes not reflected in modification times. For example, if you remove one .go file in a package with multiple .go files, there is no modification time remaining in the system that indicates that the installed package is out of date. The old build ID was the hash of a list of file names and a few other factors, expected to change if those factors changed. This CL moves to using this kind of build ID as the only way to detect staleness, making sure that the build ID hash includes all possible factors that need to influence the rebuild decision. One such factor is the compiler flags. As of this CL, if you run go build -gcflags -N cmd/gofmt you will get a gofmt where every package is built with -N, regardless of what may or may not be installed already. Another such factor is the linker flags. As of this CL, if you run go install myprog go install -ldflags=-s myprog the second go install will now correctly build a new myprog with the updated linker flags. (Previously the installed myprog appeared up-to-date, because the ldflags were not included in the build ID.) Because we have more precise information we can also validate whether the target of a "go test -c" operation is already the right binary and therefore can avoid a rebuild. This CL sets us up for having a more general build artifact cache, maybe even a step toward not having a pkg directory with .a files, but this CL does not take that step. For now the result of go install is the same as it ever was; we just do a better job of what needs to be installed. This CL does slow down builds a small amount by reading all the dependent source files in full. (The go command already read the beginning of every dependent source file to discover build tags and imports.) On my MacBook Pro, before this CL all.bash takes 3m58s, while after this CL and a few optimizations stacked above it all.bash takes 4m28s. Given that CL 73850 cut 1m43s off the all.bash time earlier today, we can afford adding 30s back for now. More optimizations are planned that should make the go command more efficient than it was even before this CL. Fixes #15799. Fixes #18369. Fixes #19340. Fixes #21477. Change-Id: I10d7ca0e31ca3f58aabb9b1f11e2e3d9d18f0bc9 Reviewed-on: https://go-review.googlesource.com/73212 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
2017-10-11 18:39:28 -06:00
gogcflags = os.Getenv("GO_GCFLAGS")
var t tester
var noRebuild bool
flag.BoolVar(&t.listMode, "list", false, "list available tests")
flag.BoolVar(&t.rebuild, "rebuild", false, "rebuild everything first")
flag.BoolVar(&noRebuild, "no-rebuild", false, "overrides -rebuild (historical dreg)")
flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
flag.BoolVar(&t.race, "race", false, "run in race builder mode (different set of tests)")
flag.BoolVar(&t.compileOnly, "compile-only", false, "compile tests, but don't run them. This is for some builders. Not all dist tests respect this flag, but most do.")
flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
"run only those tests matching the regular expression; empty means to run all. "+
"Special exception: if the string begins with '!', the match is inverted.")
xflagparse(-1) // any number of args
if noRebuild {
t.rebuild = false
}
t.run()
}
// tester executes cmdtest.
type tester struct {
race bool
listMode bool
rebuild bool
failed bool
keepGoing bool
compileOnly bool // just try to compile all tests, but no need to run
runRxStr string
runRx *regexp.Regexp
runRxWant bool // want runRx to match (true) or not match (false)
runNames []string // tests to run, exclusive with runRx; empty means all
banner string // prefix, or "" for none
lastHeading string // last dir heading printed
cgoEnabled bool
partial bool
haveTime bool // the 'time' binary is available
tests []distTest
timeoutScale int
worklist []*work
}
type work struct {
dt *distTest
cmd *exec.Cmd
start chan bool
out []byte
err error
end chan bool
}
// A distTest is a test run by dist test.
// Each test has a unique name and belongs to a group (heading)
type distTest struct {
name string // unique test name; may be filtered with -run flag
heading string // group section; this header is printed before the test is run.
fn func(*distTest) error
}
func (t *tester) run() {
timelog("start", "dist test")
var exeSuffix string
if goos == "windows" {
exeSuffix = ".exe"
}
if _, err := os.Stat(filepath.Join(gobin, "go"+exeSuffix)); err == nil {
os.Setenv("PATH", fmt.Sprintf("%s%c%s", gobin, os.PathListSeparator, os.Getenv("PATH")))
}
slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output()
if err != nil {
log.Fatalf("Error running go env CGO_ENABLED: %v", err)
}
t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp)))
if flag.NArg() > 0 && t.runRxStr != "" {
log.Fatalf("the -run regular expression flag is mutually exclusive with test name arguments")
}
t.runNames = flag.Args()
if t.hasBash() {
if _, err := exec.LookPath("time"); err == nil {
t.haveTime = true
}
}
if t.rebuild {
t.out("Building packages and commands.")
cmd/go: do not install dependencies during "go install" This CL makes "go install" behave the way many users expect: install only the things named on the command line. Future builds still run as fast, thanks to the new build cache (CL 75473). To install dependencies as well (the old behavior), use "go install -i". Actual definitions aside, what most users know and expect of "go install" is that (1) it installs what you asked, and (2) it's fast, unlike "go build". It was fast because it installed dependencies, but installing dependencies confused users repeatedly (see for example #5065, #6424, #10998, #12329, "go build" and "go test" so that they could be "fast" too, but that only created new opportunities for confusion. We also had to add -installsuffix and then -pkgdir, to allow "fast" even when dependencies could not be installed in the usual place. The recent introduction of precise content-based staleness logic means that the go command detects the need for rebuilding packages more often than it used to, with the consequence that "go install" rebuilds and reinstalls dependencies more than it used to. This will create more new opportunities for confusion and will certainly lead to more issues filed like the ones listed above. CL 75743 introduced a build cache, separate from the install locations. That cache makes all operations equally incremental and fast, whether or not the operation is "install" or "build", and whether or not "-i" is used. Installing dependencies is no longer necessary for speed, it has confused users in the past, and the more accurate rebuilds mean that it will confuse users even more often in the future. This CL aims to end all that confusion by not installing dependencies by default. By analogy with "go build -i" and "go test -i", which still install dependencies, this CL introduces "go install -i", which installs dependencies in addition to the things named on the command line. Fixes #5065. Fixes #6424. Fixes #10998. Fixes #12329. Fixes #18981. Fixes #22469. Another step toward #4719. Change-Id: I3d7bc145c3a680e2f26416e182fa0dcf1e2a15e5 Reviewed-on: https://go-review.googlesource.com/75850 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
2017-11-02 23:24:19 -06:00
// Force rebuild the whole toolchain.
goInstall("go", append([]string{"-a", "-i"}, toolchain...)...)
}
// Complete rebuild bootstrap, even with -no-rebuild.
// If everything is up-to-date, this is a no-op.
// If everything is not up-to-date, the first checkNotStale
// during the test process will kill the tests, so we might
// as well install the world.
// Now that for example "go install cmd/compile" does not
// also install runtime (you need "go install -i cmd/compile"
// for that), it's easy for previous workflows like
// "rebuild the compiler and then run run.bash"
// to break if we don't automatically refresh things here.
// Rebuilding is a shortened bootstrap.
// See cmdbootstrap for a description of the overall process.
//
// But don't do this if we're running in the Go build system,
// where cmd/dist is invoked many times. This just slows that
// down (Issue 24300).
if !t.listMode && os.Getenv("GO_BUILDER_NAME") == "" {
cmd/go: do not install dependencies during "go install" This CL makes "go install" behave the way many users expect: install only the things named on the command line. Future builds still run as fast, thanks to the new build cache (CL 75473). To install dependencies as well (the old behavior), use "go install -i". Actual definitions aside, what most users know and expect of "go install" is that (1) it installs what you asked, and (2) it's fast, unlike "go build". It was fast because it installed dependencies, but installing dependencies confused users repeatedly (see for example #5065, #6424, #10998, #12329, "go build" and "go test" so that they could be "fast" too, but that only created new opportunities for confusion. We also had to add -installsuffix and then -pkgdir, to allow "fast" even when dependencies could not be installed in the usual place. The recent introduction of precise content-based staleness logic means that the go command detects the need for rebuilding packages more often than it used to, with the consequence that "go install" rebuilds and reinstalls dependencies more than it used to. This will create more new opportunities for confusion and will certainly lead to more issues filed like the ones listed above. CL 75743 introduced a build cache, separate from the install locations. That cache makes all operations equally incremental and fast, whether or not the operation is "install" or "build", and whether or not "-i" is used. Installing dependencies is no longer necessary for speed, it has confused users in the past, and the more accurate rebuilds mean that it will confuse users even more often in the future. This CL aims to end all that confusion by not installing dependencies by default. By analogy with "go build -i" and "go test -i", which still install dependencies, this CL introduces "go install -i", which installs dependencies in addition to the things named on the command line. Fixes #5065. Fixes #6424. Fixes #10998. Fixes #12329. Fixes #18981. Fixes #22469. Another step toward #4719. Change-Id: I3d7bc145c3a680e2f26416e182fa0dcf1e2a15e5 Reviewed-on: https://go-review.googlesource.com/75850 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
2017-11-02 23:24:19 -06:00
goInstall("go", append([]string{"-i"}, toolchain...)...)
goInstall("go", append([]string{"-i"}, toolchain...)...)
goInstall("go", "std", "cmd")
checkNotStale("go", "std", "cmd")
}
t.timeoutScale = 1
switch goarch {
case "arm":
t.timeoutScale = 2
case "mips", "mipsle", "mips64", "mips64le":
t.timeoutScale = 4
}
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
t.timeoutScale, err = strconv.Atoi(s)
if err != nil {
log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err)
}
}
if t.runRxStr != "" {
if t.runRxStr[0] == '!' {
t.runRxWant = false
t.runRxStr = t.runRxStr[1:]
} else {
t.runRxWant = true
}
t.runRx = regexp.MustCompile(t.runRxStr)
}
t.registerTests()
if t.listMode {
for _, tt := range t.tests {
fmt.Println(tt.name)
}
return
}
cmd/link: set runtime.GOROOT default during link Suppose you build the Go toolchain in directory A, move the whole thing to directory B, and then use it from B to build a new program hello.exe, and then run hello.exe, and hello.exe crashes with a stack trace into the standard library. Long ago, you'd have seen hello.exe print file names in the A directory tree, even though the files had moved to the B directory tree. About two years ago we changed the compiler to write down these files with the name "$GOROOT" (that literal string) instead of A, so that the final link from B could replace "$GOROOT" with B, so that hello.exe's crash would show the correct source file paths in the stack trace. (golang.org/cl/18200) Now suppose that you do the same thing but hello.exe doesn't crash: it prints fmt.Println(runtime.GOROOT()). And you run hello.exe after clearing $GOROOT from the environment. Long ago, you'd have seen hello.exe print A instead of B. Before this CL, you'd still see hello.exe print A instead of B. This case is the one instance where a moved toolchain still divulges its origin. Not anymore. After this CL, hello.exe will print B, because the linker sets runtime/internal/sys.DefaultGoroot with the effective GOROOT from link time. This makes the default result of runtime.GOROOT once again match the file names recorded in the binary, after two years of divergence. With that cleared up, we can reintroduce GOROOT into the link action ID and also reenable TestExecutableGOROOT/RelocatedExe. When $GOROOT_FINAL is set during link, it is used in preference to $GOROOT, as always, but it was easier to explain the behavior above without introducing that complication. Fixes #22155. Fixes #20284. Fixes #22475. Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016 Reviewed-on: https://go-review.googlesource.com/86835 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 09:59:29 -07:00
// We must unset GOROOT_FINAL before tests, because runtime/debug requires
// correct access to source code, so if we have GOROOT_FINAL in effect,
// at least runtime/debug test will fail.
cmd/link: set runtime.GOROOT default during link Suppose you build the Go toolchain in directory A, move the whole thing to directory B, and then use it from B to build a new program hello.exe, and then run hello.exe, and hello.exe crashes with a stack trace into the standard library. Long ago, you'd have seen hello.exe print file names in the A directory tree, even though the files had moved to the B directory tree. About two years ago we changed the compiler to write down these files with the name "$GOROOT" (that literal string) instead of A, so that the final link from B could replace "$GOROOT" with B, so that hello.exe's crash would show the correct source file paths in the stack trace. (golang.org/cl/18200) Now suppose that you do the same thing but hello.exe doesn't crash: it prints fmt.Println(runtime.GOROOT()). And you run hello.exe after clearing $GOROOT from the environment. Long ago, you'd have seen hello.exe print A instead of B. Before this CL, you'd still see hello.exe print A instead of B. This case is the one instance where a moved toolchain still divulges its origin. Not anymore. After this CL, hello.exe will print B, because the linker sets runtime/internal/sys.DefaultGoroot with the effective GOROOT from link time. This makes the default result of runtime.GOROOT once again match the file names recorded in the binary, after two years of divergence. With that cleared up, we can reintroduce GOROOT into the link action ID and also reenable TestExecutableGOROOT/RelocatedExe. When $GOROOT_FINAL is set during link, it is used in preference to $GOROOT, as always, but it was easier to explain the behavior above without introducing that complication. Fixes #22155. Fixes #20284. Fixes #22475. Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016 Reviewed-on: https://go-review.googlesource.com/86835 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 09:59:29 -07:00
// If GOROOT_FINAL was set before, then now all the commands will appear stale.
// Nothing we can do about that other than not checking them below.
// (We call checkNotStale but only with "std" not "cmd".)
os.Setenv("GOROOT_FINAL_OLD", os.Getenv("GOROOT_FINAL")) // for cmd/link test
os.Unsetenv("GOROOT_FINAL")
for _, name := range t.runNames {
if !t.isRegisteredTestName(name) {
log.Fatalf("unknown test %q", name)
}
}
for _, dt := range t.tests {
if !t.shouldRunTest(dt.name) {
t.partial = true
continue
}
dt := dt // dt used in background after this iteration
if err := dt.fn(&dt); err != nil {
t.runPending(&dt) // in case that hasn't been done yet
t.failed = true
if t.keepGoing {
log.Printf("Failed: %v", err)
} else {
log.Fatalf("Failed: %v", err)
}
}
}
t.runPending(nil)
timelog("end", "dist test")
if t.failed {
fmt.Println("\nFAILED")
os.Exit(1)
} else if t.partial {
fmt.Println("\nALL TESTS PASSED (some were excluded)")
} else {
fmt.Println("\nALL TESTS PASSED")
}
}
func (t *tester) shouldRunTest(name string) bool {
if t.runRx != nil {
return t.runRx.MatchString(name) == t.runRxWant
}
if len(t.runNames) == 0 {
return true
}
for _, runName := range t.runNames {
if runName == name {
return true
}
}
return false
}
// short returns a -short flag to pass to 'go test'.
// It returns "-short", unless the environment variable
// GO_TEST_SHORT is set to a non-empty, false-ish string.
//
// This environment variable is meant to be an internal
// detail between the Go build system and cmd/dist
// and is not intended for use by users.
func short() string {
if v := os.Getenv("GO_TEST_SHORT"); v != "" {
short, err := strconv.ParseBool(v)
if err != nil {
log.Fatalf("invalid GO_TEST_SHORT %q: %v", v, err)
}
if !short {
return "-short=false"
}
}
return "-short"
}
// goTest returns the beginning of the go test command line.
// Callers should use goTest and then pass flags overriding these
// defaults as later arguments in the command line.
func (t *tester) goTest() []string {
return []string{
"go", "test", short(), "-count=1", t.tags(), t.runFlag(""),
}
}
func (t *tester) tags() string {
if t.iOS() {
return "-tags=lldb"
}
return "-tags="
}
func (t *tester) timeout(sec int) string {
return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale))
}
// ranGoTest and stdMatches are state closed over by the stdlib
// testing func in registerStdTest below. The tests are run
// sequentially, so there's no need for locks.
//
// ranGoBench and benchMatches are the same, but are only used
// in -race mode.
var (
ranGoTest bool
stdMatches []string
ranGoBench bool
benchMatches []string
)
func (t *tester) registerStdTest(pkg string) {
testName := "go_test:" + pkg
if t.runRx == nil || t.runRx.MatchString(testName) == t.runRxWant {
stdMatches = append(stdMatches, pkg)
}
t.tests = append(t.tests, distTest{
name: testName,
heading: "Testing packages.",
fn: func(dt *distTest) error {
if ranGoTest {
return nil
}
t.runPending(dt)
timelog("start", dt.name)
defer timelog("end", dt.name)
ranGoTest = true
timeoutSec := 180
for _, pkg := range stdMatches {
if pkg == "cmd/go" {
timeoutSec *= 3
break
}
}
args := []string{
"test",
short(),
t.tags(),
t.timeout(timeoutSec),
"-gcflags=all=" + gogcflags,
}
if t.race {
args = append(args, "-race")
}
if t.compileOnly {
args = append(args, "-run=^$")
} else if goos == "js" && goarch == "wasm" {
args = append(args, "-run=^Test") // exclude examples; Issue 25913
}
args = append(args, stdMatches...)
cmd := exec.Command("go", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
},
})
}
func (t *tester) registerRaceBenchTest(pkg string) {
testName := "go_test_bench:" + pkg
if t.runRx == nil || t.runRx.MatchString(testName) == t.runRxWant {
benchMatches = append(benchMatches, pkg)
}
t.tests = append(t.tests, distTest{
name: testName,
heading: "Running benchmarks briefly.",
fn: func(dt *distTest) error {
if ranGoBench {
return nil
}
t.runPending(dt)
timelog("start", dt.name)
defer timelog("end", dt.name)
ranGoBench = true
args := []string{
"test",
short(),
"-race",
t.timeout(1200), // longer timeout for race with benchmarks
"-run=^$", // nothing. only benchmarks.
"-benchtime=.1s",
"-cpu=4",
}
if !t.compileOnly {
args = append(args, "-bench=.*")
}
args = append(args, benchMatches...)
cmd := exec.Command("go", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
},
})
}
// stdOutErrAreTerminals is defined in test_linux.go, to report
// whether stdout & stderr are terminals.
var stdOutErrAreTerminals func() bool
func (t *tester) registerTests() {
if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-vetall") {
// Run vet over std and cmd and call it quits.
for k := range cgoEnabled {
osarch := k
t.tests = append(t.tests, distTest{
name: "vet/" + osarch,
cmd/vet: tighten printf format error messages Every time I see an error that begins `missing argument for Fprintf("%s")` my mental type-checker goes off, since obviously "%s" is not a valid first argument to Fprintf. Writing Printf("%s") to report an error in Printf("hello %s") is almost as confusing. This CL rewords the errors reported by vet's printf check to be more consistent with each other, avoid placing context like "in printf call" in the middle of the message, and to avoid the imprecisions above by not quoting the format string at all. Before: bad.go:9: no formatting directive in Printf call bad.go:10: missing argument for Printf("%s"): format reads arg 1, have only 0 args bad.go:11: wrong number of args for format in Printf call: 1 needed but 2 args bad.go:12: bad syntax for printf argument index: [1] bad.go:13: index value [0] for Printf("%[0]s"); indexes start at 1 bad.go:14: missing argument for Printf("%[2]s"): format reads arg 2, have only 1 args bad.go:15: bad syntax for printf argument index: [abc] bad.go:16: unrecognized printf verb 'z' bad.go:17: arg "hello" for * in printf format not of type int bad.go:18: arg fmt.Sprint in printf call is a function value, not a function call bad.go:19: arg fmt.Sprint in Print call is a function value, not a function call bad.go:20: arg "world" for printf verb %d of wrong type: string bad.go:21: missing argument for Printf("%q"): format reads arg 2, have only 1 args bad.go:22: first argument to Print is os.Stderr bad.go:23: Println call ends with newline bad.go:32: arg r in Sprint call causes recursive call to String method bad.go:34: arg r for printf causes recursive call to String method After: bad.go:9: Printf call has arguments but no formatting directives bad.go:10: Printf format %s reads arg #1, but have only 0 args bad.go:11: Printf call needs 1 args but has 2 args bad.go:12: Printf format %[1 is missing closing ] bad.go:13: Printf format has invalid argument index [0] bad.go:14: Printf format has invalid argument index [2] bad.go:15: Printf format has invalid argument index [abc] bad.go:16: Printf format %.234z has unknown verb z bad.go:17: Printf format %.*s uses non-int "hello" as argument of * bad.go:18: Printf format %s arg fmt.Sprint is a func value, not called bad.go:19: Print arg fmt.Sprint is a func value, not called bad.go:20: Printf format %d has arg "world" of wrong type string bad.go:21: Printf format %q reads arg #2, but have only 1 args bad.go:22: Print does not take io.Writer but has first arg os.Stderr bad.go:23: Println args end with redundant newline bad.go:32: Sprint arg r causes recursive call to String method bad.go:34: Sprintf format %s with arg r causes recursive String method call Change-Id: I5719f0fb9f2cd84df8ad4c7754ab9b79c691b060 Reviewed-on: https://go-review.googlesource.com/74352 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rob Pike <r@golang.org>
2017-10-29 13:18:52 -06:00
heading: "cmd/vet/all",
fn: func(dt *distTest) error {
t.addCmd(dt, "src/cmd/vet/all", "go", "run", "main.go", "-p="+osarch)
return nil
},
})
}
return
}
// Fast path to avoid the ~1 second of `go list std cmd` when
// the caller lists specific tests to run. (as the continuous
// build coordinator does).
if len(t.runNames) > 0 {
for _, name := range t.runNames {
if strings.HasPrefix(name, "go_test:") {
t.registerStdTest(strings.TrimPrefix(name, "go_test:"))
}
if strings.HasPrefix(name, "go_test_bench:") {
t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:"))
}
}
} else {
// Use a format string to only list packages and commands that have tests.
const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
cmd := exec.Command("go", "list", "-f", format)
if t.race {
cmd.Args = append(cmd.Args, "-tags=race")
}
cmd.Args = append(cmd.Args, "std")
if !t.race {
cmd.Args = append(cmd.Args, "cmd")
}
all, err := cmd.Output()
if err != nil {
log.Fatalf("Error running go list std cmd: %v, %s", err, all)
}
pkgs := strings.Fields(string(all))
for _, pkg := range pkgs {
t.registerStdTest(pkg)
}
if t.race {
for _, pkg := range pkgs {
if t.packageHasBenchmarks(pkg) {
t.registerRaceBenchTest(pkg)
}
}
}
}
// Test the os/user package in the pure-Go mode too.
if !t.compileOnly {
t.tests = append(t.tests, distTest{
name: "osusergo",
heading: "os/user with tag osusergo",
fn: func(dt *distTest) error {
t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=osusergo", "os/user")
return nil
},
})
}
if t.race {
return
}
// Runtime CPU tests.
if !t.compileOnly && goos != "js" { // js can't handle -cpu != 1
testName := "runtime:cpu124"
t.tests = append(t.tests, distTest{
name: testName,
heading: "GOMAXPROCS=2 runtime -cpu=1,2,4 -quick",
fn: func(dt *distTest) error {
cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "runtime", "-cpu=1,2,4", "-quick")
// We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code,
// creation of first goroutines and first garbage collections in the parallel setting.
cmd.Env = append(os.Environ(), "GOMAXPROCS=2")
return nil
},
})
}
// This test needs its stdout/stderr to be terminals, so we don't run it from cmd/go's tests.
// See issue 18153.
if goos == "linux" {
t.tests = append(t.tests, distTest{
name: "cmd_go_test_terminal",
heading: "cmd/go terminal test",
fn: func(dt *distTest) error {
t.runPending(dt)
timelog("start", dt.name)
defer timelog("end", dt.name)
if !stdOutErrAreTerminals() {
fmt.Println("skipping terminal test; stdout/stderr not terminals")
return nil
}
cmd := exec.Command("go", "test")
cmd.Dir = filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
},
})
}
// On the builders only, test that a moved GOROOT still works.
// Fails on iOS because CC_FOR_TARGET refers to clangwrap.sh
// in the unmoved GOROOT.
// Fails on Android and js/wasm with an exec format error.
// Fails on plan9 with "cannot find GOROOT" (issue #21016).
if os.Getenv("GO_BUILDER_NAME") != "" && goos != "android" && !t.iOS() && goos != "plan9" && goos != "js" {
t.tests = append(t.tests, distTest{
name: "moved_goroot",
heading: "moved GOROOT",
fn: func(dt *distTest) error {
t.runPending(dt)
timelog("start", dt.name)
defer timelog("end", dt.name)
moved := goroot + "-moved"
if err := os.Rename(goroot, moved); err != nil {
if goos == "windows" {
// Fails on Windows (with "Access is denied") if a process
// or binary is in this directory. For instance, using all.bat
// when run from c:\workdir\go\src fails here
// if GO_BUILDER_NAME is set. Our builders invoke tests
// a different way which happens to work when sharding
// tests, but we should be tolerant of the non-sharded
// all.bat case.
log.Printf("skipping test on Windows")
return nil
}
return err
}
// Run `go test fmt` in the moved GOROOT.
cmd/go: cache built packages This CL adds caching of built package files in $GOCACHE, so that a second build with a particular configuration will be able to reuse the work done in the first build of that configuration, even if the first build was only "go build" and not "go install", or even if there was an intervening "go install" that wiped out the installed copy of the first build. The benchjuju benchmark runs go build on a specific revision of jujud 10 times. Before this CL: 102.72u 15.29s 21.98r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 105.99u 15.55s 22.71r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 106.49u 15.70s 22.82r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 107.09u 15.72s 22.94r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 108.19u 15.85s 22.78r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 108.92u 16.00s 23.02r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.25u 15.82s 23.05r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.57u 15.96s 23.11r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.86u 15.97s 23.17r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 110.50u 16.05s 23.37r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... After this CL: 113.66u 17.00s 24.17r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.85u 0.68s 3.49r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.98u 0.72s 3.63r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 4.07u 0.72s 3.57r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.98u 0.70s 3.43r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 4.58u 0.70s 3.58r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.90u 0.70s 3.46r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.85u 0.71s 3.52r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.70u 0.69s 3.64r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.79u 0.68s 3.41r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... This CL reduces the overall all.bash time from 4m22s to 4m17s on my laptop. Not much faster, but also not slower. See also #4719, #20137, #20372. Change-Id: I101d5363f8c55bf4825167a5f6954862739bf000 Reviewed-on: https://go-review.googlesource.com/75473 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
2017-10-31 19:50:48 -06:00
// Disable GOCACHE because it points back at the old GOROOT.
cmd := exec.Command(filepath.Join(moved, "bin", "go"), "test", "fmt")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Don't set GOROOT in the environment.
for _, e := range os.Environ() {
cmd/go: cache built packages This CL adds caching of built package files in $GOCACHE, so that a second build with a particular configuration will be able to reuse the work done in the first build of that configuration, even if the first build was only "go build" and not "go install", or even if there was an intervening "go install" that wiped out the installed copy of the first build. The benchjuju benchmark runs go build on a specific revision of jujud 10 times. Before this CL: 102.72u 15.29s 21.98r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 105.99u 15.55s 22.71r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 106.49u 15.70s 22.82r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 107.09u 15.72s 22.94r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 108.19u 15.85s 22.78r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 108.92u 16.00s 23.02r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.25u 15.82s 23.05r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.57u 15.96s 23.11r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.86u 15.97s 23.17r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 110.50u 16.05s 23.37r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... After this CL: 113.66u 17.00s 24.17r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.85u 0.68s 3.49r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.98u 0.72s 3.63r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 4.07u 0.72s 3.57r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.98u 0.70s 3.43r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 4.58u 0.70s 3.58r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.90u 0.70s 3.46r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.85u 0.71s 3.52r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.70u 0.69s 3.64r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.79u 0.68s 3.41r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... This CL reduces the overall all.bash time from 4m22s to 4m17s on my laptop. Not much faster, but also not slower. See also #4719, #20137, #20372. Change-Id: I101d5363f8c55bf4825167a5f6954862739bf000 Reviewed-on: https://go-review.googlesource.com/75473 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
2017-10-31 19:50:48 -06:00
if !strings.HasPrefix(e, "GOROOT=") && !strings.HasPrefix(e, "GOCACHE=") {
cmd.Env = append(cmd.Env, e)
}
}
cmd/go: cache built packages This CL adds caching of built package files in $GOCACHE, so that a second build with a particular configuration will be able to reuse the work done in the first build of that configuration, even if the first build was only "go build" and not "go install", or even if there was an intervening "go install" that wiped out the installed copy of the first build. The benchjuju benchmark runs go build on a specific revision of jujud 10 times. Before this CL: 102.72u 15.29s 21.98r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 105.99u 15.55s 22.71r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 106.49u 15.70s 22.82r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 107.09u 15.72s 22.94r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 108.19u 15.85s 22.78r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 108.92u 16.00s 23.02r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.25u 15.82s 23.05r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.57u 15.96s 23.11r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 109.86u 15.97s 23.17r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 110.50u 16.05s 23.37r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... After this CL: 113.66u 17.00s 24.17r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.85u 0.68s 3.49r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.98u 0.72s 3.63r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 4.07u 0.72s 3.57r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.98u 0.70s 3.43r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 4.58u 0.70s 3.58r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.90u 0.70s 3.46r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.85u 0.71s 3.52r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.70u 0.69s 3.64r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... 3.79u 0.68s 3.41r go build -o /tmp/jujud github.com/juju/juju/cmd/jujud ... This CL reduces the overall all.bash time from 4m22s to 4m17s on my laptop. Not much faster, but also not slower. See also #4719, #20137, #20372. Change-Id: I101d5363f8c55bf4825167a5f6954862739bf000 Reviewed-on: https://go-review.googlesource.com/75473 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: David Crawshaw <crawshaw@golang.org>
2017-10-31 19:50:48 -06:00
cmd.Env = append(cmd.Env, "GOCACHE=off")
err := cmd.Run()
if rerr := os.Rename(moved, goroot); rerr != nil {
log.Fatalf("failed to restore GOROOT: %v", rerr)
}
return err
},
})
}
// Test that internal linking of standard packages does not
// require libgcc. This ensures that we can install a Go
// release on a system that does not have a C compiler
// installed and still build Go programs (that don't use cgo).
for _, pkg := range cgoPackages {
if !t.internalLink() {
break
}
// ARM libgcc may be Thumb, which internal linking does not support.
if goarch == "arm" {
break
}
pkg := pkg
var run string
if pkg == "net" {
run = "TestTCPStress"
}
t.tests = append(t.tests, distTest{
name: "nolibgcc:" + pkg,
heading: "Testing without libgcc.",
fn: func(dt *distTest) error {
t.addCmd(dt, "src", t.goTest(), "-ldflags=-linkmode=internal -libgcc=none", pkg, t.runFlag(run))
return nil
},
})
}
// Test internal linking of PIE binaries where it is supported.
if goos == "linux" && goarch == "amd64" && !isAlpineLinux() {
// Issue 18243: We don't have a way to set the default
// dynamic linker used in internal linking mode. So
// this test is skipped on Alpine.
t.tests = append(t.tests, distTest{
name: "pie_internal",
heading: "internal linking of -buildmode=pie",
fn: func(dt *distTest) error {
t.addCmd(dt, "src", t.goTest(), "reflect", "-buildmode=pie", "-ldflags=-linkmode=internal", t.timeout(60))
return nil
},
})
}
// sync tests
if goos != "js" { // js doesn't support -cpu=10
t.tests = append(t.tests, distTest{
name: "sync_cpu",
heading: "sync -cpu=10",
fn: func(dt *distTest) error {
t.addCmd(dt, "src", t.goTest(), "sync", t.timeout(120), "-cpu=10", t.runFlag(""))
return nil
},
})
}
if t.raceDetectorSupported() {
t.tests = append(t.tests, distTest{
name: "race",
heading: "Testing race detector",
fn: t.raceTest,
})
}
if t.cgoEnabled && !t.iOS() {
// Disabled on iOS. golang.org/issue/15919
t.tests = append(t.tests, distTest{
name: "cgo_stdio",
heading: "../misc/cgo/stdio",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/cgo/stdio", "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".")
return nil
},
})
t.tests = append(t.tests, distTest{
name: "cgo_life",
heading: "../misc/cgo/life",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/cgo/life", "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".")
return nil
},
})
fortran := os.Getenv("FC")
if fortran == "" {
fortran, _ = exec.LookPath("gfortran")
}
if t.hasBash() && fortran != "" {
t.tests = append(t.tests, distTest{
name: "cgo_fortran",
heading: "../misc/cgo/fortran",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/cgo/fortran", "./test.bash", fortran)
return nil
},
})
}
if t.hasSwig() && goos != "android" {
t.tests = append(t.tests, distTest{
name: "swig_stdio",
heading: "../misc/swig/stdio",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/swig/stdio", t.goTest())
return nil
},
})
if t.hasCxx() {
t.tests = append(t.tests, distTest{
name: "swig_callback",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/swig/callback", t.goTest())
return nil
},
})
}
}
}
if t.cgoEnabled {
t.tests = append(t.tests, distTest{
name: "cgo_test",
heading: "../misc/cgo/test",
fn: t.cgoTest,
})
}
if t.hasBash() && t.cgoEnabled && goos != "android" && goos != "darwin" {
t.registerTest("testgodefs", "../misc/cgo/testgodefs", "./test.bash")
}
// Don't run these tests with $GO_GCFLAGS because most of them
// assume that they can run "go install" with no -gcflags and not
// recompile the entire standard library. If make.bash ran with
// special -gcflags, that's not true.
if t.cgoEnabled && gogcflags == "" {
if t.cgoTestSOSupported() {
t.tests = append(t.tests, distTest{
name: "testso",
heading: "../misc/cgo/testso",
fn: func(dt *distTest) error {
return t.cgoTestSO(dt, "misc/cgo/testso")
},
})
t.tests = append(t.tests, distTest{
name: "testsovar",
heading: "../misc/cgo/testsovar",
fn: func(dt *distTest) error {
return t.cgoTestSO(dt, "misc/cgo/testsovar")
},
})
}
if t.supportedBuildmode("c-archive") {
t.registerHostTest("testcarchive", "../misc/cgo/testcarchive", "misc/cgo/testcarchive", "carchive_test.go")
}
if t.supportedBuildmode("c-shared") {
t.registerHostTest("testcshared", "../misc/cgo/testcshared", "misc/cgo/testcshared", "cshared_test.go")
}
if t.supportedBuildmode("shared") {
t.registerTest("testshared", "../misc/cgo/testshared", t.goTest(), t.timeout(600))
}
if t.supportedBuildmode("plugin") {
t.registerTest("testplugin", "../misc/cgo/testplugin", "./test.bash")
}
if gohostos == "linux" && goarch == "amd64" {
t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
}
if goos == "linux" && (goarch == "amd64" || goarch == "arm64") {
t.registerHostTest("testsanitizers/msan", "../misc/cgo/testsanitizers", "misc/cgo/testsanitizers", ".")
}
if t.hasBash() && goos != "android" && !t.iOS() && gohostos != "windows" {
t.registerHostTest("cgo_errors", "../misc/cgo/errors", "misc/cgo/errors", ".")
}
if gohostos == "linux" && t.extLink() {
t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go")
}
}
// Doc tests only run on builders.
// They find problems approximately never.
if t.hasBash() && goos != "nacl" && goos != "js" && goos != "android" && !t.iOS() && os.Getenv("GO_BUILDER_NAME") != "" {
t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
t.registerTest("wiki", "../doc/articles/wiki", "./test.bash")
t.registerTest("codewalk", "../doc/codewalk", "time", "./run")
}
if goos != "android" && !t.iOS() {
t.registerTest("bench_go1", "../test/bench/go1", t.goTest(), t.timeout(600))
}
if goos != "android" && !t.iOS() {
// Only start multiple test dir shards on builders,
// where they get distributed to multiple machines.
// See issue 20141.
nShards := 1
if os.Getenv("GO_BUILDER_NAME") != "" {
nShards = 10
}
for shard := 0; shard < nShards; shard++ {
shard := shard
t.tests = append(t.tests, distTest{
name: fmt.Sprintf("test:%d_%d", shard, nShards),
heading: "../test",
fn: func(dt *distTest) error { return t.testDirTest(dt, shard, nShards) },
})
}
}
if goos != "nacl" && goos != "android" && !t.iOS() && goos != "js" {
t.tests = append(t.tests, distTest{
name: "api",
heading: "API check",
fn: func(dt *distTest) error {
if t.compileOnly {
t.addCmd(dt, "src", "go", "build", filepath.Join(goroot, "src/cmd/api/run.go"))
return nil
}
t.addCmd(dt, "src", "go", "run", filepath.Join(goroot, "src/cmd/api/run.go"))
return nil
},
})
}
}
// isRegisteredTestName reports whether a test named testName has already
// been registered.
func (t *tester) isRegisteredTestName(testName string) bool {
for _, tt := range t.tests {
if tt.name == testName {
return true
}
}
return false
}
func (t *tester) registerTest1(seq bool, name, dirBanner string, cmdline ...interface{}) {
bin, args := flattenCmdline(cmdline)
if bin == "time" && !t.haveTime {
bin, args = args[0], args[1:]
}
if t.isRegisteredTestName(name) {
panic("duplicate registered test name " + name)
}
t.tests = append(t.tests, distTest{
name: name,
heading: dirBanner,
fn: func(dt *distTest) error {
if seq {
t.runPending(dt)
timelog("start", name)
defer timelog("end", name)
return t.dirCmd(filepath.Join(goroot, "src", dirBanner), bin, args).Run()
}
t.addCmd(dt, filepath.Join(goroot, "src", dirBanner), bin, args)
return nil
},
})
}
func (t *tester) registerTest(name, dirBanner string, cmdline ...interface{}) {
t.registerTest1(false, name, dirBanner, cmdline...)
}
func (t *tester) registerSeqTest(name, dirBanner string, cmdline ...interface{}) {
t.registerTest1(true, name, dirBanner, cmdline...)
}
func (t *tester) bgDirCmd(dir, bin string, args ...string) *exec.Cmd {
cmd := exec.Command(bin, args...)
if filepath.IsAbs(dir) {
cmd.Dir = dir
} else {
cmd.Dir = filepath.Join(goroot, dir)
}
return cmd
}
func (t *tester) dirCmd(dir string, cmdline ...interface{}) *exec.Cmd {
bin, args := flattenCmdline(cmdline)
cmd := t.bgDirCmd(dir, bin, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if vflag > 1 {
errprintf("%s\n", strings.Join(cmd.Args, " "))
}
return cmd
}
// flattenCmdline flattens a mixture of string and []string as single list
// and then interprets it as a command line: first element is binary, then args.
func flattenCmdline(cmdline []interface{}) (bin string, args []string) {
var list []string
for _, x := range cmdline {
switch x := x.(type) {
case string:
list = append(list, x)
case []string:
list = append(list, x...)
default:
panic("invalid addCmd argument type: " + reflect.TypeOf(x).String())
}
}
// The go command is too picky about duplicated flags.
// Drop all but the last of the allowed duplicated flags.
drop := make([]bool, len(list))
have := map[string]int{}
for i := 1; i < len(list); i++ {
j := strings.Index(list[i], "=")
if j < 0 {
continue
}
flag := list[i][:j]
switch flag {
case "-run", "-tags":
if have[flag] != 0 {
drop[have[flag]] = true
}
have[flag] = i
}
}
out := list[:0]
for i, x := range list {
if !drop[i] {
out = append(out, x)
}
}
list = out
return list[0], list[1:]
}
func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec.Cmd {
bin, args := flattenCmdline(cmdline)
w := &work{
dt: dt,
cmd: t.bgDirCmd(dir, bin, args...),
}
t.worklist = append(t.worklist, w)
return w.cmd
}
func (t *tester) iOS() bool {
return goos == "darwin" && (goarch == "arm" || goarch == "arm64")
}
func (t *tester) out(v string) {
if t.banner == "" {
return
}
fmt.Println("\n" + t.banner + v)
}
func (t *tester) extLink() bool {
pair := gohostos + "-" + goarch
switch pair {
case "android-arm",
"darwin-386", "darwin-amd64", "darwin-arm", "darwin-arm64",
"dragonfly-amd64",
"freebsd-386", "freebsd-amd64", "freebsd-arm",
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-s390x",
"netbsd-386", "netbsd-amd64",
"openbsd-386", "openbsd-amd64",
"windows-386", "windows-amd64":
return true
}
return false
}
func (t *tester) internalLink() bool {
if gohostos == "dragonfly" {
// linkmode=internal fails on dragonfly since errno is a TLS relocation.
return false
}
if gohostarch == "ppc64le" {
// linkmode=internal fails on ppc64le because cmd/link doesn't
// handle the TOC correctly (issue 15409).
return false
}
if goos == "android" {
return false
}
if goos == "darwin" && (goarch == "arm" || goarch == "arm64") {
return false
}
// Internally linking cgo is incomplete on some architectures.
// https://golang.org/issue/10373
// https://golang.org/issue/14449
if goarch == "arm64" || goarch == "mips64" || goarch == "mips64le" || goarch == "mips" || goarch == "mipsle" {
return false
}
if isAlpineLinux() {
// Issue 18243.
return false
}
return true
}
func (t *tester) supportedBuildmode(mode string) bool {
pair := goos + "-" + goarch
switch mode {
case "c-archive":
if !t.extLink() {
return false
}
switch pair {
case "darwin-386", "darwin-amd64", "darwin-arm", "darwin-arm64",
"linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x",
"freebsd-amd64",
"windows-amd64", "windows-386":
return true
}
return false
case "c-shared":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
"darwin-amd64", "darwin-386",
"freebsd-amd64",
"android-arm", "android-arm64", "android-386",
"windows-amd64", "windows-386":
return true
}
return false
case "shared":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x":
return true
}
return false
case "plugin":
// linux-arm64 is missing because it causes the external linker
// to crash, see https://golang.org/issue/17138
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le":
return true
case "darwin-amd64":
return true
}
return false
case "pie":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
"android-amd64", "android-arm", "android-arm64", "android-386":
return true
case "darwin-amd64":
return true
}
return false
default:
log.Fatalf("internal error: unknown buildmode %s", mode)
return false
}
}
func (t *tester) registerHostTest(name, heading, dir, pkg string) {
t.tests = append(t.tests, distTest{
name: name,
heading: heading,
fn: func(dt *distTest) error {
t.runPending(dt)
timelog("start", name)
defer timelog("end", name)
return t.runHostTest(dir, pkg)
},
})
}
func (t *tester) runHostTest(dir, pkg string) error {
defer os.Remove(filepath.Join(goroot, dir, "test.test"))
cmd := t.dirCmd(dir, t.goTest(), "-c", "-o", "test.test", pkg)
cmd.Env = append(os.Environ(), "GOARCH="+gohostarch, "GOOS="+gohostos)
if err := cmd.Run(); err != nil {
return err
}
return t.dirCmd(dir, "./test.test").Run()
}
func (t *tester) cgoTest(dt *distTest) error {
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=auto")
if t.internalLink() {
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal", "-ldflags", "-linkmode=internal")
}
pair := gohostos + "-" + goarch
switch pair {
case "darwin-386", "darwin-amd64",
"openbsd-386", "openbsd-amd64",
"windows-386", "windows-amd64":
// test linkmode=external, but __thread not supported, so skip testtls.
if !t.extLink() {
break
}
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external")
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s")
case "android-arm",
"dragonfly-amd64",
"freebsd-386", "freebsd-amd64", "freebsd-arm",
"linux-386", "linux-amd64", "linux-arm", "linux-ppc64le", "linux-s390x",
"netbsd-386", "netbsd-amd64":
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=auto")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=external")
switch pair {
case "netbsd-386", "netbsd-amd64":
// no static linking
case "freebsd-arm":
// -fPIC compiled tls code will use __tls_get_addr instead
// of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr
// is implemented in rtld-elf, so -fPIC isn't compatible with
// static linking on FreeBSD/ARM with clang. (cgo depends on
// -fPIC fundamentally.)
default:
cmd := t.dirCmd("misc/cgo/test",
compilerEnvLookup(defaultcc, goos, goarch), "-xc", "-o", "/dev/null", "-static", "-")
cmd.Stdin = strings.NewReader("int main() {}")
if err := cmd.Run(); err != nil {
fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.")
} else {
if goos != "android" {
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
}
t.addCmd(dt, "misc/cgo/nocgo", t.goTest())
t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-ldflags", `-linkmode=external`)
if goos != "android" {
t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
// -static in CGO_LDFLAGS triggers a different code path
// than -static in -extldflags, so test both.
// See issue #16651.
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static")
cmd.Env = append(os.Environ(), "CGO_LDFLAGS=-static -pthread")
}
}
if t.supportedBuildmode("pie") {
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-buildmode=pie")
t.addCmd(dt, "misc/cgo/nocgo", t.goTest(), "-buildmode=pie")
}
}
}
return nil
}
// run pending test commands, in parallel, emitting headers as appropriate.
// When finished, emit header for nextTest, which is going to run after the
// pending commands are done (and runPending returns).
// A test should call runPending if it wants to make sure that it is not
// running in parallel with earlier tests, or if it has some other reason
// for needing the earlier tests to be done.
func (t *tester) runPending(nextTest *distTest) {
cmd/link: set runtime.GOROOT default during link Suppose you build the Go toolchain in directory A, move the whole thing to directory B, and then use it from B to build a new program hello.exe, and then run hello.exe, and hello.exe crashes with a stack trace into the standard library. Long ago, you'd have seen hello.exe print file names in the A directory tree, even though the files had moved to the B directory tree. About two years ago we changed the compiler to write down these files with the name "$GOROOT" (that literal string) instead of A, so that the final link from B could replace "$GOROOT" with B, so that hello.exe's crash would show the correct source file paths in the stack trace. (golang.org/cl/18200) Now suppose that you do the same thing but hello.exe doesn't crash: it prints fmt.Println(runtime.GOROOT()). And you run hello.exe after clearing $GOROOT from the environment. Long ago, you'd have seen hello.exe print A instead of B. Before this CL, you'd still see hello.exe print A instead of B. This case is the one instance where a moved toolchain still divulges its origin. Not anymore. After this CL, hello.exe will print B, because the linker sets runtime/internal/sys.DefaultGoroot with the effective GOROOT from link time. This makes the default result of runtime.GOROOT once again match the file names recorded in the binary, after two years of divergence. With that cleared up, we can reintroduce GOROOT into the link action ID and also reenable TestExecutableGOROOT/RelocatedExe. When $GOROOT_FINAL is set during link, it is used in preference to $GOROOT, as always, but it was easier to explain the behavior above without introducing that complication. Fixes #22155. Fixes #20284. Fixes #22475. Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016 Reviewed-on: https://go-review.googlesource.com/86835 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 09:59:29 -07:00
checkNotStale("go", "std")
worklist := t.worklist
t.worklist = nil
for _, w := range worklist {
w.start = make(chan bool)
w.end = make(chan bool)
go func(w *work) {
if !<-w.start {
timelog("skip", w.dt.name)
w.out = []byte(fmt.Sprintf("skipped due to earlier error\n"))
} else {
timelog("start", w.dt.name)
w.out, w.err = w.cmd.CombinedOutput()
}
timelog("end", w.dt.name)
w.end <- true
}(w)
}
started := 0
ended := 0
var last *distTest
for ended < len(worklist) {
for started < len(worklist) && started-ended < maxbg {
//println("start", started)
w := worklist[started]
started++
w.start <- !t.failed || t.keepGoing
}
w := worklist[ended]
dt := w.dt
if dt.heading != "" && t.lastHeading != dt.heading {
t.lastHeading = dt.heading
t.out(dt.heading)
}
if dt != last {
// Assumes all the entries for a single dt are in one worklist.
last = w.dt
if vflag > 0 {
fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
}
}
if vflag > 1 {
errprintf("%s\n", strings.Join(w.cmd.Args, " "))
}
//println("wait", ended)
ended++
<-w.end
os.Stdout.Write(w.out)
if w.err != nil {
log.Printf("Failed: %v", w.err)
t.failed = true
}
cmd/link: set runtime.GOROOT default during link Suppose you build the Go toolchain in directory A, move the whole thing to directory B, and then use it from B to build a new program hello.exe, and then run hello.exe, and hello.exe crashes with a stack trace into the standard library. Long ago, you'd have seen hello.exe print file names in the A directory tree, even though the files had moved to the B directory tree. About two years ago we changed the compiler to write down these files with the name "$GOROOT" (that literal string) instead of A, so that the final link from B could replace "$GOROOT" with B, so that hello.exe's crash would show the correct source file paths in the stack trace. (golang.org/cl/18200) Now suppose that you do the same thing but hello.exe doesn't crash: it prints fmt.Println(runtime.GOROOT()). And you run hello.exe after clearing $GOROOT from the environment. Long ago, you'd have seen hello.exe print A instead of B. Before this CL, you'd still see hello.exe print A instead of B. This case is the one instance where a moved toolchain still divulges its origin. Not anymore. After this CL, hello.exe will print B, because the linker sets runtime/internal/sys.DefaultGoroot with the effective GOROOT from link time. This makes the default result of runtime.GOROOT once again match the file names recorded in the binary, after two years of divergence. With that cleared up, we can reintroduce GOROOT into the link action ID and also reenable TestExecutableGOROOT/RelocatedExe. When $GOROOT_FINAL is set during link, it is used in preference to $GOROOT, as always, but it was easier to explain the behavior above without introducing that complication. Fixes #22155. Fixes #20284. Fixes #22475. Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016 Reviewed-on: https://go-review.googlesource.com/86835 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 09:59:29 -07:00
checkNotStale("go", "std")
}
if t.failed && !t.keepGoing {
log.Fatal("FAILED")
}
if dt := nextTest; dt != nil {
if dt.heading != "" && t.lastHeading != dt.heading {
t.lastHeading = dt.heading
t.out(dt.heading)
}
if vflag > 0 {
fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
}
}
}
func (t *tester) cgoTestSOSupported() bool {
if goos == "android" || t.iOS() {
// No exec facility on Android or iOS.
return false
}
if goarch == "ppc64" {
// External linking not implemented on ppc64 (issue #8912).
return false
}
if goarch == "mips64le" || goarch == "mips64" {
// External linking not implemented on mips64.
return false
}
return true
}
func (t *tester) cgoTestSO(dt *distTest, testpath string) error {
t.runPending(dt)
timelog("start", dt.name)
defer timelog("end", dt.name)
dir := filepath.Join(goroot, testpath)
// build shared object
output, err := exec.Command("go", "env", "CC").Output()
if err != nil {
return fmt.Errorf("Error running go env CC: %v", err)
}
cc := strings.TrimSuffix(string(output), "\n")
if cc == "" {
return errors.New("CC environment variable (go env CC) cannot be empty")
}
output, err = exec.Command("go", "env", "GOGCCFLAGS").Output()
if err != nil {
return fmt.Errorf("Error running go env GOGCCFLAGS: %v", err)
}
gogccflags := strings.Split(strings.TrimSuffix(string(output), "\n"), " ")
ext := "so"
args := append(gogccflags, "-shared")
switch goos {
case "darwin":
ext = "dylib"
args = append(args, "-undefined", "suppress", "-flat_namespace")
case "windows":
ext = "dll"
args = append(args, "-DEXPORT_DLL")
}
sofname := "libcgosotest." + ext
args = append(args, "-o", sofname, "cgoso_c.c")
if err := t.dirCmd(dir, cc, args).Run(); err != nil {
return err
}
defer os.Remove(filepath.Join(dir, sofname))
if err := t.dirCmd(dir, "go", "build", "-o", "main.exe", "main.go").Run(); err != nil {
return err
}
defer os.Remove(filepath.Join(dir, "main.exe"))
cmd := t.dirCmd(dir, "./main.exe")
if goos != "windows" {
s := "LD_LIBRARY_PATH"
if goos == "darwin" {
s = "DYLD_LIBRARY_PATH"
}
cmd.Env = append(os.Environ(), s+"=.")
// On FreeBSD 64-bit architectures, the 32-bit linker looks for
// different environment variables.
if goos == "freebsd" && gohostarch == "386" {
cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.")
}
}
return cmd.Run()
}
func (t *tester) hasBash() bool {
switch gohostos {
case "windows", "plan9":
return false
}
return true
}
func (t *tester) hasCxx() bool {
cxx, _ := exec.LookPath(compilerEnvLookup(defaultcxx, goos, goarch))
return cxx != ""
}
func (t *tester) hasSwig() bool {
swig, err := exec.LookPath("swig")
if err != nil {
return false
}
// Check that swig was installed with Go support by checking
// that a go directory exists inside the swiglib directory.
// See https://golang.org/issue/23469.
output, err := exec.Command(swig, "-go", "-swiglib").Output()
if err != nil {
return false
}
swigDir := strings.TrimSpace(string(output))
_, err = os.Stat(filepath.Join(swigDir, "go"))
if err != nil {
return false
}
// Check that swig has a new enough version.
// See https://golang.org/issue/22858.
out, err := exec.Command(swig, "-version").CombinedOutput()
if err != nil {
return false
}
re := regexp.MustCompile(`[vV]ersion +([\d]+)([.][\d]+)?([.][\d]+)?`)
matches := re.FindSubmatch(out)
if matches == nil {
// Can't find version number; hope for the best.
return true
}
major, err := strconv.Atoi(string(matches[1]))
if err != nil {
// Can't find version number; hope for the best.
return true
}
if major < 3 {
return false
}
if major > 3 {
// 4.0 or later
return true
}
// We have SWIG version 3.x.
if len(matches[2]) > 0 {
minor, err := strconv.Atoi(string(matches[2][1:]))
if err != nil {
return true
}
if minor > 0 {
// 3.1 or later
return true
}
}
// We have SWIG version 3.0.x.
if len(matches[3]) > 0 {
patch, err := strconv.Atoi(string(matches[3][1:]))
if err != nil {
return true
}
if patch < 6 {
// Before 3.0.6.
return false
}
}
return true
}
func (t *tester) raceDetectorSupported() bool {
switch gohostos {
case "linux", "darwin", "freebsd", "windows":
// The race detector doesn't work on Alpine Linux:
// golang.org/issue/14481
return t.cgoEnabled && (goarch == "amd64" || goarch == "ppc64le") && gohostos == goos && !isAlpineLinux()
}
return false
}
func isAlpineLinux() bool {
if runtime.GOOS != "linux" {
return false
}
fi, err := os.Lstat("/etc/alpine-release")
return err == nil && fi.Mode().IsRegular()
}
func (t *tester) runFlag(rx string) string {
if t.compileOnly {
return "-run=^$"
}
if rx == "" && goos == "js" && goarch == "wasm" {
return "-run=^Test" // exclude examples; Issue 25913
}
return "-run=" + rx
}
func (t *tester) raceTest(dt *distTest) error {
t.addCmd(dt, "src", t.goTest(), "-race", "-i", "runtime/race", "flag", "os", "os/exec")
t.addCmd(dt, "src", t.goTest(), "-race", t.runFlag("Output"), "runtime/race")
t.addCmd(dt, "src", t.goTest(), "-race", t.runFlag("TestParse|TestEcho|TestStdinCloseRace|TestClosedPipeRace|TestTypeRace|TestFdRace|TestFdReadRace|TestFileCloseRace"), "flag", "net", "os", "os/exec", "encoding/gob")
// We don't want the following line, because it
// slows down all.bash (by 10 seconds on my laptop).
// The race builder should catch any error here, but doesn't.
// TODO(iant): Figure out how to catch this.
// t.addCmd(dt, "src", t.goTest(), "-race", "-run=TestParallelTest", "cmd/go")
if t.cgoEnabled {
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-race")
cmd.Env = append(os.Environ(), "GOTRACEBACK=2")
}
if t.extLink() {
// Test with external linking; see issue 9133.
t.addCmd(dt, "src", t.goTest(), "-race", "-ldflags=-linkmode=external", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
}
return nil
}
var runtest struct {
sync.Once
exe string
err error
}
func (t *tester) testDirTest(dt *distTest, shard, shards int) error {
runtest.Do(func() {
const exe = "runtest.exe" // named exe for Windows, but harmless elsewhere
cmd := t.dirCmd("test", "go", "build", "-o", exe, "run.go")
cmd.Env = append(os.Environ(), "GOOS="+gohostos, "GOARCH="+gohostarch)
runtest.exe = filepath.Join(cmd.Dir, exe)
if err := cmd.Run(); err != nil {
runtest.err = err
return
}
xatexit(func() {
os.Remove(runtest.exe)
})
})
if runtest.err != nil {
return runtest.err
}
if t.compileOnly {
return nil
}
t.addCmd(dt, "test", runtest.exe,
fmt.Sprintf("--shard=%d", shard),
fmt.Sprintf("--shards=%d", shards),
)
return nil
}
// cgoPackages is the standard packages that use cgo.
var cgoPackages = []string{
"crypto/x509",
"net",
"os/user",
}
var funcBenchmark = []byte("\nfunc Benchmark")
// packageHasBenchmarks reports whether pkg has benchmarks.
// On any error, it conservatively returns true.
//
// This exists just to eliminate work on the builders, since compiling
// a test in race mode just to discover it has no benchmarks costs a
// second or two per package, and this function returns false for
// about 100 packages.
func (t *tester) packageHasBenchmarks(pkg string) bool {
pkgDir := filepath.Join(goroot, "src", pkg)
d, err := os.Open(pkgDir)
if err != nil {
return true // conservatively
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return true // conservatively
}
for _, name := range names {
if !strings.HasSuffix(name, "_test.go") {
continue
}
slurp, err := ioutil.ReadFile(filepath.Join(pkgDir, name))
if err != nil {
return true // conservatively
}
if bytes.Contains(slurp, funcBenchmark) {
return true
}
}
return false
}