1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
26 flag.BoolVar(&t.listMode, "list", false, "list available tests")
27 flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages")
28 flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
29 flag.BoolVar(&t.race, "race", false, "run in race builder mode (different set of tests)")
30 flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
31 flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
32 "run only those tests matching the regular expression; empty means to run all. "+
33 "Special exception: if the string begins with '!', the match is inverted.")
34 xflagparse(-1) // any number of args
38 // tester executes cmdtest.
46 runRxWant bool // want runRx to match (true) or not match (false)
47 runNames []string // tests to run, exclusive with runRx; empty means all
48 banner string // prefix, or "" for none
57 haveTime bool // the 'time' binary is available
63 // A distTest is a test run by dist test.
64 // Each test has a unique name and belongs to a group (heading)
65 type distTest struct {
66 name string // unique test name; may be filtered with -run flag
67 heading string // group section; this header is printed before the test is run.
71 func mustEnv(k string) string {
74 log.Fatalf("Unset environment variable %v", k)
79 func (t *tester) run() {
80 t.goroot = mustEnv("GOROOT")
81 t.goos = mustEnv("GOOS")
82 t.gohostos = mustEnv("GOHOSTOS")
83 t.goarch = mustEnv("GOARCH")
84 t.gohostarch = mustEnv("GOHOSTARCH")
85 slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output()
87 log.Fatalf("Error running go env CGO_ENABLED: %v", err)
89 t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp)))
90 if flag.NArg() > 0 && t.runRxStr != "" {
91 log.Fatalf("the -run regular expression flag is mutually exclusive with test name arguments")
93 t.runNames = flag.Args()
96 if _, err := exec.LookPath("time"); err == nil {
102 t.out("Building packages and commands.")
103 cmd := exec.Command("go", "install", "-a", "-v", "std", "cmd")
104 cmd.Stdout = os.Stdout
105 cmd.Stderr = os.Stderr
106 if err := cmd.Run(); err != nil {
107 log.Fatalf("building packages and commands: %v", err)
112 // Install the Mach exception handler used to intercept
113 // EXC_BAD_ACCESS and convert it into a Go panic. This is
114 // necessary for a Go program running under lldb (the way
115 // we run tests). It is disabled by default because iOS
116 // apps are not allowed to access the exc_server symbol.
117 cmd := exec.Command("go", "install", "-a", "-tags", "lldb", "runtime/cgo")
118 cmd.Stdout = os.Stdout
119 cmd.Stderr = os.Stderr
120 if err := cmd.Run(); err != nil {
121 log.Fatalf("building mach exception handler: %v", err)
125 cmd := exec.Command("go", "install", "-a", "runtime/cgo")
126 cmd.Stdout = os.Stdout
127 cmd.Stderr = os.Stderr
128 if err := cmd.Run(); err != nil {
129 log.Fatalf("reverting mach exception handler: %v", err)
135 if t.goarch == "arm" || t.goos == "windows" {
138 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
139 t.timeoutScale, err = strconv.Atoi(s)
141 log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err)
145 if t.runRxStr != "" {
146 if t.runRxStr[0] == '!' {
148 t.runRxStr = t.runRxStr[1:]
152 t.runRx = regexp.MustCompile(t.runRxStr)
157 for _, tt := range t.tests {
163 // we must unset GOROOT_FINAL before tests, because runtime/debug requires
164 // correct access to source code, so if we have GOROOT_FINAL in effect,
165 // at least runtime/debug test will fail.
166 os.Unsetenv("GOROOT_FINAL")
168 for _, name := range t.runNames {
169 if !t.isRegisteredTestName(name) {
170 log.Fatalf("unknown test %q", name)
174 var lastHeading string
176 for _, dt := range t.tests {
177 if !t.shouldRunTest(dt.name) {
181 if dt.heading != "" && lastHeading != dt.heading {
182 lastHeading = dt.heading
186 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
188 if err := dt.fn(); err != nil {
191 log.Printf("Failed: %v", err)
193 log.Fatalf("Failed: %v", err)
198 fmt.Println("\nFAILED")
200 } else if t.partial {
201 fmt.Println("\nALL TESTS PASSED (some were excluded)")
203 fmt.Println("\nALL TESTS PASSED")
207 func (t *tester) shouldRunTest(name string) bool {
209 return t.runRx.MatchString(name) == t.runRxWant
211 if len(t.runNames) == 0 {
214 for _, runName := range t.runNames {
222 func (t *tester) tags() string {
229 func (t *tester) timeout(sec int) string {
230 return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale))
233 // ranGoTest and stdMatches are state closed over by the stdlib
234 // testing func in registerStdTest below. The tests are run
235 // sequentially, so there's no need for locks.
237 // ranGoBench and benchMatches are the same, but are only used
244 benchMatches []string
247 func (t *tester) registerStdTest(pkg string) {
248 testName := "go_test:" + pkg
249 if t.runRx == nil || t.runRx.MatchString(testName) {
250 stdMatches = append(stdMatches, pkg)
252 t.tests = append(t.tests, distTest{
254 heading: "Testing packages.",
265 "-gcflags=" + os.Getenv("GO_GCFLAGS"),
268 args = append(args, "-race")
270 args = append(args, stdMatches...)
271 cmd := exec.Command("go", args...)
272 cmd.Stdout = os.Stdout
273 cmd.Stderr = os.Stderr
279 // TODO: Remove when SSA codegen is used by default.
280 func (t *tester) registerSSATest(pkg string) {
286 t.tests = append(t.tests, distTest{
287 name: "go_test_ssa:" + pkg,
288 heading: "Testing packages with SSA codegen.",
293 t.timeout(180 * 3), // SSA generates slower code right now
294 "-gcflags=" + os.Getenv("GO_GCFLAGS"),
297 args = append(args, "-race")
299 args = append(args, pkg)
300 cmd := exec.Command("go", args...)
301 cmd.Env = mergeEnvLists([]string{"GOSSAPKG=" + path.Base(pkg)}, os.Environ())
302 cmd.Stdout = os.Stdout
303 cmd.Stderr = os.Stderr
309 func (t *tester) registerRaceBenchTest(pkg string) {
310 testName := "go_test_bench:" + pkg
311 if t.runRx == nil || t.runRx.MatchString(testName) {
312 benchMatches = append(benchMatches, pkg)
314 t.tests = append(t.tests, distTest{
316 heading: "Running benchmarks briefly.",
326 "-run=^$", // nothing. only benchmarks.
331 args = append(args, benchMatches...)
332 cmd := exec.Command("go", args...)
333 cmd.Stdout = os.Stdout
334 cmd.Stderr = os.Stderr
340 func (t *tester) registerTests() {
341 // Fast path to avoid the ~1 second of `go list std cmd` when
342 // the caller lists specific tests to run. (as the continuous
343 // build coordinator does).
344 if len(t.runNames) > 0 {
345 for _, name := range t.runNames {
346 if strings.HasPrefix(name, "go_test:") {
347 t.registerStdTest(strings.TrimPrefix(name, "go_test:"))
349 if strings.HasPrefix(name, "go_test_bench:") {
350 t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:"))
352 if t.goarch == "amd64" && strings.HasPrefix(name, "go_test_ssa:") {
353 t.registerSSATest(strings.TrimPrefix(name, "go_test_ssa:"))
357 // Use a format string to only list packages and commands that have tests.
358 const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}"
359 cmd := exec.Command("go", "list", "-f", format, "std")
361 cmd.Args = append(cmd.Args, "cmd")
363 all, err := cmd.CombinedOutput()
365 log.Fatalf("Error running go list std cmd: %v, %s", err, all)
367 pkgs := strings.Fields(string(all))
368 for _, pkg := range pkgs {
369 t.registerStdTest(pkg)
371 if t.goarch == "amd64" {
372 for _, pkg := range pkgs {
373 t.registerSSATest(pkg)
377 for _, pkg := range pkgs {
378 t.registerRaceBenchTest(pkg)
387 // Runtime CPU tests.
388 testName := "runtime:cpu124"
389 t.tests = append(t.tests, distTest{
391 heading: "GOMAXPROCS=2 runtime -cpu=1,2,4",
393 cmd := t.dirCmd("src", "go", "test", "-short", t.timeout(300), t.tags(), "-cpu=1,2,4", "runtime")
394 // We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code,
395 // creation of first goroutines and first garbage collections in the parallel setting.
396 cmd.Env = mergeEnvLists([]string{"GOMAXPROCS=2"}, os.Environ())
402 t.tests = append(t.tests, distTest{
404 heading: "sync -cpu=10",
406 return t.dirCmd("src", "go", "test", "-short", t.timeout(120), t.tags(), "-cpu=10", "sync").Run()
410 if t.cgoEnabled && t.goos != "android" && !t.iOS() {
411 // Disabled on android and iOS. golang.org/issue/8345
412 t.tests = append(t.tests, distTest{
414 heading: "../misc/cgo/stdio",
416 return t.dirCmd("misc/cgo/stdio",
417 "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run()
420 t.tests = append(t.tests, distTest{
422 heading: "../misc/cgo/life",
424 return t.dirCmd("misc/cgo/life",
425 "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run()
429 if t.cgoEnabled && t.goos != "android" && !t.iOS() {
430 // TODO(crawshaw): reenable on android and iOS
431 // golang.org/issue/8345
433 // These tests are not designed to run off the host.
434 t.tests = append(t.tests, distTest{
436 heading: "../misc/cgo/test",
441 if t.raceDetectorSupported() {
442 t.tests = append(t.tests, distTest{
444 heading: "Testing race detector",
449 if t.hasBash() && t.cgoEnabled && t.goos != "android" && t.goos != "darwin" {
450 t.registerTest("testgodefs", "../misc/cgo/testgodefs", "./test.bash")
453 if t.cgoTestSOSupported() {
454 t.tests = append(t.tests, distTest{
456 heading: "../misc/cgo/testso",
458 return t.cgoTestSO("misc/cgo/testso")
461 t.tests = append(t.tests, distTest{
463 heading: "../misc/cgo/testsovar",
465 return t.cgoTestSO("misc/cgo/testsovar")
469 if t.supportedBuildmode("c-archive") {
470 t.registerTest("testcarchive", "../misc/cgo/testcarchive", "./test.bash")
472 if t.supportedBuildmode("c-shared") {
473 t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash")
475 if t.supportedBuildmode("shared") {
476 t.registerTest("testshared", "../misc/cgo/testshared", "go", "test")
478 if t.gohostos == "linux" && t.goarch == "amd64" {
479 t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
481 if t.gohostos == "linux" && t.goarch == "amd64" {
482 t.registerTest("testsanitizers", "../misc/cgo/testsanitizers", "./test.bash")
484 if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" {
485 t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash")
487 if t.gohostos == "linux" && t.extLink() {
488 t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go")
491 if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !t.iOS() {
492 t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
493 t.registerTest("wiki", "../doc/articles/wiki", "./test.bash")
494 t.registerTest("codewalk", "../doc/codewalk", "time", "./run")
495 for _, name := range t.shootoutTests() {
496 if name == "spectralnorm" && os.Getenv("GO_BUILDER_NAME") == "linux-arm-arm5" {
497 // Heavy on floating point and takes over 20 minutes with softfloat.
498 // Disabled per Issue 12688.
501 t.registerTest("shootout:"+name, "../test/bench/shootout", "time", "./timing.sh", "-test", name)
504 if t.goos != "android" && !t.iOS() {
505 t.registerTest("bench_go1", "../test/bench/go1", "go", "test")
507 if t.goos != "android" && !t.iOS() {
509 for shard := 0; shard < nShards; shard++ {
511 t.tests = append(t.tests, distTest{
512 name: fmt.Sprintf("test:%d_%d", shard, nShards),
514 fn: func() error { return t.testDirTest(shard, nShards) },
518 if t.goos != "nacl" && t.goos != "android" && !t.iOS() {
519 t.tests = append(t.tests, distTest{
521 heading: "API check",
523 return t.dirCmd("src", "go", "run", filepath.Join(t.goroot, "src/cmd/api/run.go")).Run()
529 // isRegisteredTestName reports whether a test named testName has already
531 func (t *tester) isRegisteredTestName(testName string) bool {
532 for _, tt := range t.tests {
533 if tt.name == testName {
540 func (t *tester) registerTest(name, dirBanner, bin string, args ...string) {
541 if bin == "time" && !t.haveTime {
542 bin, args = args[0], args[1:]
544 if t.isRegisteredTestName(name) {
545 panic("duplicate registered test name " + name)
547 t.tests = append(t.tests, distTest{
551 return t.dirCmd(filepath.Join(t.goroot, "src", dirBanner), bin, args...).Run()
556 func (t *tester) dirCmd(dir string, bin string, args ...string) *exec.Cmd {
557 cmd := exec.Command(bin, args...)
558 if filepath.IsAbs(dir) {
561 cmd.Dir = filepath.Join(t.goroot, dir)
563 cmd.Stdout = os.Stdout
564 cmd.Stderr = os.Stderr
566 errprintf("%s\n", strings.Join(cmd.Args, " "))
571 func (t *tester) iOS() bool {
572 return t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64")
575 func (t *tester) out(v string) {
579 fmt.Println("\n" + t.banner + v)
582 func (t *tester) extLink() bool {
583 pair := t.gohostos + "-" + t.goarch
586 "darwin-arm", "darwin-arm64",
587 "dragonfly-386", "dragonfly-amd64",
588 "freebsd-386", "freebsd-amd64", "freebsd-arm",
589 "linux-386", "linux-amd64", "linux-arm", "linux-arm64",
590 "netbsd-386", "netbsd-amd64",
591 "openbsd-386", "openbsd-amd64",
592 "windows-386", "windows-amd64":
594 case "darwin-386", "darwin-amd64":
595 // linkmode=external fails on OS X 10.6 and earlier == Darwin
597 unameR, err := exec.Command("uname", "-r").Output()
599 log.Fatalf("uname -r: %v", err)
601 major, _ := strconv.Atoi(string(unameR[:bytes.IndexByte(unameR, '.')]))
607 func (t *tester) supportedBuildmode(mode string) bool {
608 pair := t.goos + "-" + t.goarch
615 case "darwin-386", "darwin-amd64", "darwin-arm", "darwin-arm64",
616 "linux-amd64", "linux-386":
621 // TODO(hyangah): add linux-386.
623 case "linux-amd64", "darwin-amd64", "android-arm", "linux-arm":
634 log.Fatal("internal error: unknown buildmode %s", mode)
639 func (t *tester) cgoTest() error {
640 env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
642 if t.goos == "android" || t.iOS() {
643 cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags())
648 cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto")
650 if err := cmd.Run(); err != nil {
654 if t.gohostos != "dragonfly" {
655 // linkmode=internal fails on dragonfly since errno is a TLS relocation.
656 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal")
658 if err := cmd.Run(); err != nil {
663 pair := t.gohostos + "-" + t.goarch
665 case "darwin-386", "darwin-amd64",
666 "openbsd-386", "openbsd-amd64",
667 "windows-386", "windows-amd64":
668 // test linkmode=external, but __thread not supported, so skip testtls.
672 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
674 if err := cmd.Run(); err != nil {
677 cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external -s")
679 if err := cmd.Run(); err != nil {
683 "dragonfly-386", "dragonfly-amd64",
684 "freebsd-386", "freebsd-amd64", "freebsd-arm",
685 "linux-386", "linux-amd64", "linux-arm",
686 "netbsd-386", "netbsd-amd64":
688 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
690 if err := cmd.Run(); err != nil {
693 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=auto")
695 if err := cmd.Run(); err != nil {
698 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=external")
700 if err := cmd.Run(); err != nil {
705 case "netbsd-386", "netbsd-amd64":
708 // -fPIC compiled tls code will use __tls_get_addr instead
709 // of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr
710 // is implemented in rtld-elf, so -fPIC isn't compatible with
711 // static linking on FreeBSD/ARM with clang. (cgo depends on
712 // -fPIC fundamentally.)
715 cmd := t.dirCmd("misc/cgo/test",
716 cc, "-xc", "-o", "/dev/null", "-static", "-")
718 cmd.Stdin = strings.NewReader("int main() {}")
719 if err := cmd.Run(); err != nil {
720 fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.")
722 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
724 if err := cmd.Run(); err != nil {
728 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test")
730 if err := cmd.Run(); err != nil {
734 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external`)
736 if err := cmd.Run(); err != nil {
740 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`)
742 if err := cmd.Run(); err != nil {
747 if pair != "freebsd-amd64" { // clang -pie fails to link misc/cgo/test
748 cmd := t.dirCmd("misc/cgo/test",
749 cc, "-xc", "-o", "/dev/null", "-pie", "-")
751 cmd.Stdin = strings.NewReader("int main() {}")
752 if err := cmd.Run(); err != nil {
753 fmt.Println("No support for -pie found, skip cgo PIE test.")
755 cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
757 if err := cmd.Run(); err != nil {
758 return fmt.Errorf("pie cgo/test: %v", err)
760 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
762 if err := cmd.Run(); err != nil {
763 return fmt.Errorf("pie cgo/testtls: %v", err)
765 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`)
767 if err := cmd.Run(); err != nil {
768 return fmt.Errorf("pie cgo/nocgo: %v", err)
778 func (t *tester) cgoTestSOSupported() bool {
779 if t.goos == "android" || t.iOS() {
780 // No exec facility on Android or iOS.
783 if t.goarch == "ppc64le" || t.goarch == "ppc64" {
784 // External linking not implemented on ppc64 (issue #8912).
790 func (t *tester) cgoTestSO(testpath string) error {
791 dir := filepath.Join(t.goroot, testpath)
793 // build shared object
794 output, err := exec.Command("go", "env", "CC").Output()
796 return fmt.Errorf("Error running go env CC: %v", err)
798 cc := strings.TrimSuffix(string(output), "\n")
800 return errors.New("CC environment variable (go env CC) cannot be empty")
802 output, err = exec.Command("go", "env", "GOGCCFLAGS").Output()
804 return fmt.Errorf("Error running go env GOGCCFLAGS: %v", err)
806 gogccflags := strings.Split(strings.TrimSuffix(string(output), "\n"), " ")
809 args := append(gogccflags, "-shared")
813 args = append(args, "-undefined", "suppress", "-flat_namespace")
816 args = append(args, "-DEXPORT_DLL")
818 sofname := "libcgosotest." + ext
819 args = append(args, "-o", sofname, "cgoso_c.c")
821 if err := t.dirCmd(dir, cc, args...).Run(); err != nil {
824 defer os.Remove(filepath.Join(dir, sofname))
826 if err := t.dirCmd(dir, "go", "build", "-o", "main.exe", "main.go").Run(); err != nil {
829 defer os.Remove(filepath.Join(dir, "main.exe"))
831 cmd := t.dirCmd(dir, "./main.exe")
832 if t.goos != "windows" {
833 s := "LD_LIBRARY_PATH"
834 if t.goos == "darwin" {
835 s = "DYLD_LIBRARY_PATH"
837 cmd.Env = mergeEnvLists([]string{s + "=."}, os.Environ())
842 func (t *tester) hasBash() bool {
844 case "windows", "plan9":
850 func (t *tester) raceDetectorSupported() bool {
852 case "linux", "darwin", "freebsd", "windows":
853 return t.cgoEnabled && t.goarch == "amd64" && t.gohostos == t.goos
858 func (t *tester) raceTest() error {
859 if err := t.dirCmd("src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec").Run(); err != nil {
862 if err := t.dirCmd("src", "go", "test", "-race", "-run=Output", "runtime/race").Run(); err != nil {
865 if err := t.dirCmd("src", "go", "test", "-race", "-short", "flag", "os/exec").Run(); err != nil {
869 env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
870 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-race", "-short")
872 if err := cmd.Run(); err != nil {
877 // Test with external linking; see issue 9133.
878 if err := t.dirCmd("src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", "flag", "os/exec").Run(); err != nil {
885 func (t *tester) testDirTest(shard, shards int) error {
886 const runExe = "runtest.exe" // named exe for Windows, but harmless elsewhere
887 cmd := t.dirCmd("test", "go", "build", "-o", runExe, "run.go")
888 cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ())
889 if err := cmd.Run(); err != nil {
892 absExe := filepath.Join(cmd.Dir, runExe)
893 defer os.Remove(absExe)
894 return t.dirCmd("test", absExe,
895 fmt.Sprintf("--shard=%d", shard),
896 fmt.Sprintf("--shards=%d", shards),
900 func (t *tester) shootoutTests() []string {
901 sh, err := ioutil.ReadFile(filepath.Join(t.goroot, "test", "bench", "shootout", "timing.sh"))
905 m := regexp.MustCompile(`(?m)^\s+run="([\w+ ]+)"\s*$`).FindSubmatch(sh)
907 log.Fatal("failed to find run=\"...\" line in test/bench/shootout/timing.sh")
909 return strings.Fields(string(m[1]))
912 // mergeEnvLists merges the two environment lists such that
913 // variables with the same name in "in" replace those in "out".
914 // out may be mutated.
915 func mergeEnvLists(in, out []string) []string {
917 for _, inkv := range in {
918 k := strings.SplitAfterN(inkv, "=", 2)[0]
919 for i, outkv := range out {
920 if strings.HasPrefix(outkv, k) {
925 out = append(out, inkv)