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. 4 5 package main 6 7 import ( 8 "bytes" 9 "errors" 10 "flag" 11 "fmt" 12 "log" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "regexp" 17 "strconv" 18 "strings" 19 "time" 20 ) 21 22 func cmdtest() { 23 var t tester 24 flag.BoolVar(&t.listMode, "list", false, "list available tests") 25 flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages") 26 flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred") 27 flag.BoolVar(&t.race, "race", false, "run in race builder mode (different set of tests)") 28 flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners") 29 flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"), 30 "run only those tests matching the regular expression; empty means to run all. "+ 31 "Special exception: if the string begins with '!', the match is inverted.") 32 xflagparse(-1) // any number of args 33 t.run() 34 } 35 36 // tester executes cmdtest. 37 type tester struct { 38 race bool 39 listMode bool 40 noRebuild bool 41 keepGoing bool 42 runRxStr string 43 runRx *regexp.Regexp 44 runRxWant bool // want runRx to match (true) or not match (false) 45 runNames []string // tests to run, exclusive with runRx; empty means all 46 banner string // prefix, or "" for none 47 48 goroot string 49 goarch string 50 gohostarch string 51 goos string 52 gohostos string 53 cgoEnabled bool 54 partial bool 55 haveTime bool // the 'time' binary is available 56 57 tests []distTest 58 timeoutScale int 59 } 60 61 // A distTest is a test run by dist test. 62 // Each test has a unique name and belongs to a group (heading) 63 type distTest struct { 64 name string // unique test name; may be filtered with -run flag 65 heading string // group section; this header is printed before the test is run. 66 fn func() error 67 } 68 69 func mustEnv(k string) string { 70 v := os.Getenv(k) 71 if v == "" { 72 log.Fatalf("Unset environment variable %v", k) 73 } 74 return v 75 } 76 77 func (t *tester) run() { 78 t.goroot = mustEnv("GOROOT") 79 t.goos = mustEnv("GOOS") 80 t.gohostos = mustEnv("GOHOSTOS") 81 t.goarch = mustEnv("GOARCH") 82 t.gohostarch = mustEnv("GOHOSTARCH") 83 slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output() 84 if err != nil { 85 log.Fatalf("Error running go env CGO_ENABLED: %v", err) 86 } 87 t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp))) 88 if flag.NArg() > 0 && t.runRxStr != "" { 89 log.Fatalf("the -run regular expression flag is mutually exclusive with test name arguments") 90 } 91 t.runNames = flag.Args() 92 93 if t.hasBash() { 94 if _, err := exec.LookPath("time"); err == nil { 95 t.haveTime = true 96 } 97 } 98 99 if !t.noRebuild { 100 t.out("Building packages and commands.") 101 cmd := exec.Command("go", "install", "-a", "-v", "std", "cmd") 102 cmd.Stdout = os.Stdout 103 cmd.Stderr = os.Stderr 104 if err := cmd.Run(); err != nil { 105 log.Fatalf("building packages and commands: %v", err) 106 } 107 } 108 109 if t.iOS() { 110 // Install the Mach exception handler used to intercept 111 // EXC_BAD_ACCESS and convert it into a Go panic. This is 112 // necessary for a Go program running under lldb (the way 113 // we run tests). It is disabled by default because iOS 114 // apps are not allowed to access the exc_server symbol. 115 cmd := exec.Command("go", "install", "-a", "-tags", "lldb", "runtime/cgo") 116 cmd.Stdout = os.Stdout 117 cmd.Stderr = os.Stderr 118 if err := cmd.Run(); err != nil { 119 log.Fatalf("building mach exception handler: %v", err) 120 } 121 122 defer func() { 123 cmd := exec.Command("go", "install", "-a", "runtime/cgo") 124 cmd.Stdout = os.Stdout 125 cmd.Stderr = os.Stderr 126 if err := cmd.Run(); err != nil { 127 log.Fatalf("reverting mach exception handler: %v", err) 128 } 129 }() 130 } 131 132 t.timeoutScale = 1 133 if t.goarch == "arm" || t.goos == "windows" { 134 t.timeoutScale = 2 135 } 136 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { 137 t.timeoutScale, err = strconv.Atoi(s) 138 if err != nil { 139 log.Fatalf("failed to parse $GO_TEST_TIMEOUT_SCALE = %q as integer: %v", s, err) 140 } 141 } 142 143 if t.runRxStr != "" { 144 if t.runRxStr[0] == '!' { 145 t.runRxWant = false 146 t.runRxStr = t.runRxStr[1:] 147 } else { 148 t.runRxWant = true 149 } 150 t.runRx = regexp.MustCompile(t.runRxStr) 151 } 152 153 t.registerTests() 154 if t.listMode { 155 for _, tt := range t.tests { 156 fmt.Println(tt.name) 157 } 158 return 159 } 160 161 // we must unset GOROOT_FINAL before tests, because runtime/debug requires 162 // correct access to source code, so if we have GOROOT_FINAL in effect, 163 // at least runtime/debug test will fail. 164 os.Unsetenv("GOROOT_FINAL") 165 166 for _, name := range t.runNames { 167 if !t.isRegisteredTestName(name) { 168 log.Fatalf("unknown test %q", name) 169 } 170 } 171 172 var lastHeading string 173 ok := true 174 for _, dt := range t.tests { 175 if !t.shouldRunTest(dt.name) { 176 t.partial = true 177 continue 178 } 179 if dt.heading != "" && lastHeading != dt.heading { 180 lastHeading = dt.heading 181 t.out(dt.heading) 182 } 183 if vflag > 0 { 184 fmt.Printf("# go tool dist test -run=^%s$\n", dt.name) 185 } 186 if err := dt.fn(); err != nil { 187 ok = false 188 if t.keepGoing { 189 log.Printf("Failed: %v", err) 190 } else { 191 log.Fatalf("Failed: %v", err) 192 } 193 } 194 } 195 if !ok { 196 fmt.Println("\nFAILED") 197 os.Exit(1) 198 } else if t.partial { 199 fmt.Println("\nALL TESTS PASSED (some were excluded)") 200 } else { 201 fmt.Println("\nALL TESTS PASSED") 202 } 203 } 204 205 func (t *tester) shouldRunTest(name string) bool { 206 if t.runRx != nil { 207 return t.runRx.MatchString(name) == t.runRxWant 208 } 209 if len(t.runNames) == 0 { 210 return true 211 } 212 for _, runName := range t.runNames { 213 if runName == name { 214 return true 215 } 216 } 217 return false 218 } 219 220 func (t *tester) tags() string { 221 if t.iOS() { 222 return "-tags=lldb" 223 } 224 return "-tags=" 225 } 226 227 func (t *tester) timeout(sec int) string { 228 return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale)) 229 } 230 231 // ranGoTest and stdMatches are state closed over by the stdlib 232 // testing func in registerStdTest below. The tests are run 233 // sequentially, so there's no need for locks. 234 // 235 // ranGoBench and benchMatches are the same, but are only used 236 // in -race mode. 237 var ( 238 ranGoTest bool 239 stdMatches []string 240 241 ranGoBench bool 242 benchMatches []string 243 ) 244 245 func (t *tester) registerStdTest(pkg string) { 246 testName := "go_test:" + pkg 247 if t.runRx == nil || t.runRx.MatchString(testName) { 248 stdMatches = append(stdMatches, pkg) 249 } 250 t.tests = append(t.tests, distTest{ 251 name: testName, 252 heading: "Testing packages.", 253 fn: func() error { 254 if ranGoTest { 255 return nil 256 } 257 ranGoTest = true 258 args := []string{ 259 "test", 260 "-short", 261 t.tags(), 262 t.timeout(180), 263 "-gcflags=" + os.Getenv("GO_GCFLAGS"), 264 } 265 if t.race { 266 args = append(args, "-race") 267 } 268 args = append(args, stdMatches...) 269 cmd := exec.Command("go", args...) 270 cmd.Stdout = os.Stdout 271 cmd.Stderr = os.Stderr 272 return cmd.Run() 273 }, 274 }) 275 } 276 277 func (t *tester) registerRaceBenchTest(pkg string) { 278 testName := "go_test_bench:" + pkg 279 if t.runRx == nil || t.runRx.MatchString(testName) { 280 benchMatches = append(benchMatches, pkg) 281 } 282 t.tests = append(t.tests, distTest{ 283 name: testName, 284 heading: "Running benchmarks briefly.", 285 fn: func() error { 286 if ranGoBench { 287 return nil 288 } 289 ranGoBench = true 290 args := []string{ 291 "test", 292 "-short", 293 "-race", 294 "-run=^$", // nothing. only benchmarks. 295 "-bench=.*", 296 "-benchtime=.1s", 297 "-cpu=4", 298 } 299 args = append(args, benchMatches...) 300 cmd := exec.Command("go", args...) 301 cmd.Stdout = os.Stdout 302 cmd.Stderr = os.Stderr 303 return cmd.Run() 304 }, 305 }) 306 } 307 308 func (t *tester) registerTests() { 309 // Fast path to avoid the ~1 second of `go list std cmd` when 310 // the caller lists specific tests to run. (as the continuous 311 // build coordinator does). 312 if len(t.runNames) > 0 { 313 for _, name := range t.runNames { 314 if strings.HasPrefix(name, "go_test:") { 315 t.registerStdTest(strings.TrimPrefix(name, "go_test:")) 316 } 317 if strings.HasPrefix(name, "go_test_bench:") { 318 t.registerRaceBenchTest(strings.TrimPrefix(name, "go_test_bench:")) 319 } 320 } 321 } else { 322 // Use a format string to only list packages and commands that have tests. 323 const format = "{{if (or .TestGoFiles .XTestGoFiles)}}{{.ImportPath}}{{end}}" 324 cmd := exec.Command("go", "list", "-f", format, "std") 325 if !t.race { 326 cmd.Args = append(cmd.Args, "cmd") 327 } 328 all, err := cmd.CombinedOutput() 329 if err != nil { 330 log.Fatalf("Error running go list std cmd: %v, %s", err, all) 331 } 332 pkgs := strings.Fields(string(all)) 333 for _, pkg := range pkgs { 334 t.registerStdTest(pkg) 335 } 336 if t.race { 337 for _, pkg := range pkgs { 338 t.registerRaceBenchTest(pkg) 339 } 340 } 341 } 342 343 if t.race { 344 return 345 } 346 347 // Runtime CPU tests. 348 testName := "runtime:cpu124" 349 t.tests = append(t.tests, distTest{ 350 name: testName, 351 heading: "GOMAXPROCS=2 runtime -cpu=1,2,4", 352 fn: func() error { 353 cmd := t.dirCmd("src", "go", "test", "-short", t.timeout(300), t.tags(), "runtime", "-cpu=1,2,4") 354 // We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code, 355 // creation of first goroutines and first garbage collections in the parallel setting. 356 cmd.Env = mergeEnvLists([]string{"GOMAXPROCS=2"}, os.Environ()) 357 return cmd.Run() 358 }, 359 }) 360 361 // sync tests 362 t.tests = append(t.tests, distTest{ 363 name: "sync_cpu", 364 heading: "sync -cpu=10", 365 fn: func() error { 366 return t.dirCmd("src", "go", "test", "sync", "-short", t.timeout(120), t.tags(), "-cpu=10").Run() 367 }, 368 }) 369 370 if t.cgoEnabled && t.goos != "android" && !t.iOS() { 371 // Disabled on android and iOS. golang.org/issue/8345 372 t.tests = append(t.tests, distTest{ 373 name: "cgo_stdio", 374 heading: "../misc/cgo/stdio", 375 fn: func() error { 376 return t.dirCmd("misc/cgo/stdio", 377 "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run() 378 }, 379 }) 380 t.tests = append(t.tests, distTest{ 381 name: "cgo_life", 382 heading: "../misc/cgo/life", 383 fn: func() error { 384 return t.dirCmd("misc/cgo/life", 385 "go", "run", filepath.Join(os.Getenv("GOROOT"), "test/run.go"), "-", ".").Run() 386 }, 387 }) 388 } 389 if t.cgoEnabled && t.goos != "android" && !t.iOS() { 390 // TODO(crawshaw): reenable on android and iOS 391 // golang.org/issue/8345 392 // 393 // These tests are not designed to run off the host. 394 t.tests = append(t.tests, distTest{ 395 name: "cgo_test", 396 heading: "../misc/cgo/test", 397 fn: t.cgoTest, 398 }) 399 } 400 401 if t.raceDetectorSupported() { 402 t.tests = append(t.tests, distTest{ 403 name: "race", 404 heading: "Testing race detector", 405 fn: t.raceTest, 406 }) 407 } 408 409 if t.hasBash() && t.cgoEnabled && t.goos != "android" && t.goos != "darwin" { 410 t.registerTest("testgodefs", "../misc/cgo/testgodefs", "./test.bash") 411 } 412 if t.cgoEnabled { 413 if t.cgoTestSOSupported() { 414 t.tests = append(t.tests, distTest{ 415 name: "testso", 416 heading: "../misc/cgo/testso", 417 fn: func() error { 418 return t.cgoTestSO("misc/cgo/testso") 419 }, 420 }) 421 t.tests = append(t.tests, distTest{ 422 name: "testsovar", 423 heading: "../misc/cgo/testsovar", 424 fn: func() error { 425 return t.cgoTestSO("misc/cgo/testsovar") 426 }, 427 }) 428 } 429 if t.supportedBuildmode("c-archive") { 430 t.registerTest("testcarchive", "../misc/cgo/testcarchive", "./test.bash") 431 } 432 if t.supportedBuildmode("c-shared") { 433 t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash") 434 } 435 if t.supportedBuildmode("shared") { 436 t.registerTest("testshared", "../misc/cgo/testshared", "go", "test") 437 } 438 if t.gohostos == "linux" && t.goarch == "amd64" { 439 t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go") 440 } 441 if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" { 442 t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash") 443 } 444 if t.gohostos == "linux" && t.extLink() { 445 t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go") 446 } 447 } 448 if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !t.iOS() { 449 t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go") 450 t.registerTest("wiki", "../doc/articles/wiki", "./test.bash") 451 t.registerTest("codewalk", "../doc/codewalk", "time", "./run") 452 t.registerTest("shootout", "../test/bench/shootout", "time", "./timing.sh", "-test") 453 } 454 if t.goos != "android" && !t.iOS() { 455 t.registerTest("bench_go1", "../test/bench/go1", "go", "test") 456 } 457 if t.goos != "android" && !t.iOS() { 458 const nShards = 5 459 for shard := 0; shard < nShards; shard++ { 460 shard := shard 461 t.tests = append(t.tests, distTest{ 462 name: fmt.Sprintf("test:%d_%d", shard, nShards), 463 heading: "../test", 464 fn: func() error { return t.testDirTest(shard, nShards) }, 465 }) 466 } 467 } 468 if t.goos != "nacl" && t.goos != "android" && !t.iOS() { 469 t.tests = append(t.tests, distTest{ 470 name: "api", 471 heading: "API check", 472 fn: func() error { 473 return t.dirCmd("src", "go", "run", filepath.Join(t.goroot, "src/cmd/api/run.go")).Run() 474 }, 475 }) 476 } 477 } 478 479 // isRegisteredTestName reports whether a test named testName has already 480 // been registered. 481 func (t *tester) isRegisteredTestName(testName string) bool { 482 for _, tt := range t.tests { 483 if tt.name == testName { 484 return true 485 } 486 } 487 return false 488 } 489 490 func (t *tester) registerTest(name, dirBanner, bin string, args ...string) { 491 if bin == "time" && !t.haveTime { 492 bin, args = args[0], args[1:] 493 } 494 if t.isRegisteredTestName(name) { 495 panic("duplicate registered test name " + name) 496 } 497 t.tests = append(t.tests, distTest{ 498 name: name, 499 heading: dirBanner, 500 fn: func() error { 501 return t.dirCmd(filepath.Join(t.goroot, "src", dirBanner), bin, args...).Run() 502 }, 503 }) 504 } 505 506 func (t *tester) dirCmd(dir string, bin string, args ...string) *exec.Cmd { 507 cmd := exec.Command(bin, args...) 508 if filepath.IsAbs(dir) { 509 cmd.Dir = dir 510 } else { 511 cmd.Dir = filepath.Join(t.goroot, dir) 512 } 513 cmd.Stdout = os.Stdout 514 cmd.Stderr = os.Stderr 515 if vflag > 1 { 516 errprintf("%s\n", strings.Join(cmd.Args, " ")) 517 } 518 return cmd 519 } 520 521 func (t *tester) iOS() bool { 522 return t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64") 523 } 524 525 func (t *tester) out(v string) { 526 if t.banner == "" { 527 return 528 } 529 fmt.Println("\n" + t.banner + v) 530 } 531 532 func (t *tester) extLink() bool { 533 pair := t.gohostos + "-" + t.goarch 534 switch pair { 535 case "android-arm", 536 "darwin-arm", "darwin-arm64", 537 "dragonfly-386", "dragonfly-amd64", 538 "freebsd-386", "freebsd-amd64", "freebsd-arm", 539 "linux-386", "linux-amd64", "linux-arm", "linux-arm64", 540 "netbsd-386", "netbsd-amd64", 541 "openbsd-386", "openbsd-amd64", 542 "windows-386", "windows-amd64": 543 return true 544 case "darwin-386", "darwin-amd64": 545 // linkmode=external fails on OS X 10.6 and earlier == Darwin 546 // 10.8 and earlier. 547 unameR, err := exec.Command("uname", "-r").Output() 548 if err != nil { 549 log.Fatalf("uname -r: %v", err) 550 } 551 major, _ := strconv.Atoi(string(unameR[:bytes.IndexByte(unameR, '.')])) 552 return major > 10 553 } 554 return false 555 } 556 557 func (t *tester) supportedBuildmode(mode string) bool { 558 pair := t.goos + "-" + t.goarch 559 switch mode { 560 case "c-archive": 561 if !t.extLink() { 562 return false 563 } 564 switch pair { 565 case "darwin-amd64", "darwin-arm", "darwin-arm64", 566 "linux-amd64", "linux-386": 567 return true 568 } 569 return false 570 case "c-shared": 571 // TODO(hyangah): add linux-386. 572 switch pair { 573 case "linux-amd64", "darwin-amd64", "android-arm": 574 return true 575 } 576 return false 577 case "shared": 578 switch pair { 579 case "linux-amd64": 580 return true 581 } 582 return false 583 default: 584 log.Fatal("internal error: unknown buildmode %s", mode) 585 return false 586 } 587 } 588 589 func (t *tester) cgoTest() error { 590 env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ()) 591 592 if t.goos == "android" || t.iOS() { 593 cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags()) 594 cmd.Env = env 595 return cmd.Run() 596 } 597 598 cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto") 599 cmd.Env = env 600 if err := cmd.Run(); err != nil { 601 return err 602 } 603 604 if t.gohostos != "dragonfly" { 605 // linkmode=internal fails on dragonfly since errno is a TLS relocation. 606 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal") 607 cmd.Env = env 608 if err := cmd.Run(); err != nil { 609 return err 610 } 611 } 612 613 pair := t.gohostos + "-" + t.goarch 614 switch pair { 615 case "darwin-386", "darwin-amd64", 616 "openbsd-386", "openbsd-amd64", 617 "windows-386", "windows-amd64": 618 // test linkmode=external, but __thread not supported, so skip testtls. 619 if !t.extLink() { 620 break 621 } 622 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external") 623 cmd.Env = env 624 if err := cmd.Run(); err != nil { 625 return err 626 } 627 cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external -s") 628 cmd.Env = env 629 if err := cmd.Run(); err != nil { 630 return err 631 } 632 case "android-arm", 633 "dragonfly-386", "dragonfly-amd64", 634 "freebsd-386", "freebsd-amd64", "freebsd-arm", 635 "linux-386", "linux-amd64", "linux-arm", 636 "netbsd-386", "netbsd-amd64": 637 638 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external") 639 cmd.Env = env 640 if err := cmd.Run(); err != nil { 641 return err 642 } 643 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=auto") 644 cmd.Env = env 645 if err := cmd.Run(); err != nil { 646 return err 647 } 648 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", "-linkmode=external") 649 cmd.Env = env 650 if err := cmd.Run(); err != nil { 651 return err 652 } 653 654 switch pair { 655 case "netbsd-386", "netbsd-amd64": 656 // no static linking 657 case "freebsd-arm": 658 // -fPIC compiled tls code will use __tls_get_addr instead 659 // of __aeabi_read_tp, however, on FreeBSD/ARM, __tls_get_addr 660 // is implemented in rtld-elf, so -fPIC isn't compatible with 661 // static linking on FreeBSD/ARM with clang. (cgo depends on 662 // -fPIC fundamentally.) 663 default: 664 cc := mustEnv("CC") 665 cmd := t.dirCmd("misc/cgo/test", 666 cc, "-xc", "-o", "/dev/null", "-static", "-") 667 cmd.Env = env 668 cmd.Stdin = strings.NewReader("int main() {}") 669 if err := cmd.Run(); err != nil { 670 fmt.Println("No support for static linking found (lacks libc.a?), skip cgo static linking test.") 671 } else { 672 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) 673 cmd.Env = env 674 if err := cmd.Run(); err != nil { 675 return err 676 } 677 678 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test") 679 cmd.Env = env 680 if err := cmd.Run(); err != nil { 681 return err 682 } 683 684 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external`) 685 cmd.Env = env 686 if err := cmd.Run(); err != nil { 687 return err 688 } 689 690 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-static -pthread"`) 691 cmd.Env = env 692 if err := cmd.Run(); err != nil { 693 return err 694 } 695 } 696 697 if pair != "freebsd-amd64" { // clang -pie fails to link misc/cgo/test 698 cmd := t.dirCmd("misc/cgo/test", 699 cc, "-xc", "-o", "/dev/null", "-pie", "-") 700 cmd.Env = env 701 cmd.Stdin = strings.NewReader("int main() {}") 702 if err := cmd.Run(); err != nil { 703 fmt.Println("No support for -pie found, skip cgo PIE test.") 704 } else { 705 cmd = t.dirCmd("misc/cgo/test", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`) 706 cmd.Env = env 707 if err := cmd.Run(); err != nil { 708 return fmt.Errorf("pie cgo/test: %v", err) 709 } 710 cmd = t.dirCmd("misc/cgo/testtls", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`) 711 cmd.Env = env 712 if err := cmd.Run(); err != nil { 713 return fmt.Errorf("pie cgo/testtls: %v", err) 714 } 715 cmd = t.dirCmd("misc/cgo/nocgo", "go", "test", "-ldflags", `-linkmode=external -extldflags "-pie"`) 716 cmd.Env = env 717 if err := cmd.Run(); err != nil { 718 return fmt.Errorf("pie cgo/nocgo: %v", err) 719 } 720 } 721 } 722 } 723 } 724 725 return nil 726 } 727 728 func (t *tester) cgoTestSOSupported() bool { 729 if t.goos == "android" || t.iOS() { 730 // No exec facility on Android or iOS. 731 return false 732 } 733 if t.goarch == "ppc64le" || t.goarch == "ppc64" { 734 // External linking not implemented on ppc64 (issue #8912). 735 return false 736 } 737 return true 738 } 739 740 func (t *tester) cgoTestSO(testpath string) error { 741 dir := filepath.Join(t.goroot, testpath) 742 743 // build shared object 744 output, err := exec.Command("go", "env", "CC").Output() 745 if err != nil { 746 return fmt.Errorf("Error running go env CC: %v", err) 747 } 748 cc := strings.TrimSuffix(string(output), "\n") 749 if cc == "" { 750 return errors.New("CC environment variable (go env CC) cannot be empty") 751 } 752 output, err = exec.Command("go", "env", "GOGCCFLAGS").Output() 753 if err != nil { 754 return fmt.Errorf("Error running go env GOGCCFLAGS: %v", err) 755 } 756 gogccflags := strings.Split(strings.TrimSuffix(string(output), "\n"), " ") 757 758 ext := "so" 759 args := append(gogccflags, "-shared") 760 switch t.goos { 761 case "darwin": 762 ext = "dylib" 763 args = append(args, "-undefined", "suppress", "-flat_namespace") 764 case "windows": 765 ext = "dll" 766 args = append(args, "-DEXPORT_DLL") 767 } 768 sofname := "libcgosotest." + ext 769 args = append(args, "-o", sofname, "cgoso_c.c") 770 771 if err := t.dirCmd(dir, cc, args...).Run(); err != nil { 772 return err 773 } 774 defer os.Remove(filepath.Join(dir, sofname)) 775 776 if err := t.dirCmd(dir, "go", "build", "-o", "main.exe", "main.go").Run(); err != nil { 777 return err 778 } 779 defer os.Remove(filepath.Join(dir, "main.exe")) 780 781 cmd := t.dirCmd(dir, "./main.exe") 782 if t.goos != "windows" { 783 s := "LD_LIBRARY_PATH" 784 if t.goos == "darwin" { 785 s = "DYLD_LIBRARY_PATH" 786 } 787 cmd.Env = mergeEnvLists([]string{s + "=."}, os.Environ()) 788 } 789 return cmd.Run() 790 } 791 792 func (t *tester) hasBash() bool { 793 switch t.gohostos { 794 case "windows", "plan9": 795 return false 796 } 797 return true 798 } 799 800 func (t *tester) raceDetectorSupported() bool { 801 switch t.gohostos { 802 case "linux", "darwin", "freebsd", "windows": 803 return t.cgoEnabled && t.goarch == "amd64" && t.gohostos == t.goos 804 } 805 return false 806 } 807 808 func (t *tester) raceTest() error { 809 if err := t.dirCmd("src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec").Run(); err != nil { 810 return err 811 } 812 if err := t.dirCmd("src", "go", "test", "-race", "-run=Output", "runtime/race").Run(); err != nil { 813 return err 814 } 815 if err := t.dirCmd("src", "go", "test", "-race", "-short", "flag", "os/exec").Run(); err != nil { 816 return err 817 } 818 if t.cgoEnabled { 819 env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ()) 820 cmd := t.dirCmd("misc/cgo/test", "go", "test", "-race", "-short") 821 cmd.Env = env 822 if err := cmd.Run(); err != nil { 823 return err 824 } 825 } 826 if t.extLink() { 827 // Test with external linking; see issue 9133. 828 if err := t.dirCmd("src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", "flag", "os/exec").Run(); err != nil { 829 return err 830 } 831 } 832 return nil 833 } 834 835 func (t *tester) testDirTest(shard, shards int) error { 836 const runExe = "runtest.exe" // named exe for Windows, but harmless elsewhere 837 cmd := t.dirCmd("test", "go", "build", "-o", runExe, "run.go") 838 cmd.Env = mergeEnvLists([]string{"GOOS=" + t.gohostos, "GOARCH=" + t.gohostarch, "GOMAXPROCS="}, os.Environ()) 839 if err := cmd.Run(); err != nil { 840 return err 841 } 842 absExe := filepath.Join(cmd.Dir, runExe) 843 defer os.Remove(absExe) 844 return t.dirCmd("test", absExe, 845 fmt.Sprintf("--shard=%d", shard), 846 fmt.Sprintf("--shards=%d", shards), 847 ).Run() 848 } 849 850 // mergeEnvLists merges the two environment lists such that 851 // variables with the same name in "in" replace those in "out". 852 // out may be mutated. 853 func mergeEnvLists(in, out []string) []string { 854 NextVar: 855 for _, inkv := range in { 856 k := strings.SplitAfterN(inkv, "=", 2)[0] 857 for i, outkv := range out { 858 if strings.HasPrefix(outkv, k) { 859 out[i] = inkv 860 continue NextVar 861 } 862 } 863 out = append(out, inkv) 864 } 865 return out 866 } 867