diff --git a/misc/android/go_android_exec.go b/misc/android/go_android_exec.go index 166ced8d0f..73530f0dd2 100644 --- a/misc/android/go_android_exec.go +++ b/misc/android/go_android_exec.go @@ -51,8 +51,8 @@ func run(args ...string) string { } const ( - deviceGoroot = "/data/local/tmp/goroot" - deviceGopath = "/data/local/tmp/gopath" + deviceRoot = "/data/local/tmp/go_exec_android" + deviceGoroot = deviceRoot + "/goroot" ) func main() { @@ -77,10 +77,16 @@ func main() { // wait for sys.boot_completed. run("wait-for-device", "exec-out", "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;") + // Done once per make.bash. + adbCopyGoroot() + // Prepare a temporary directory that will be cleaned up at the end. - deviceGotmp := fmt.Sprintf("/data/local/tmp/%s-%d", - filepath.Base(os.Args[1]), os.Getpid()) - run("exec-out", "mkdir", "-p", deviceGotmp) + // Binary names can conflict. + // E.g. template.test from the {html,text}/template packages. + binName := filepath.Base(os.Args[1]) + deviceGotmp := fmt.Sprintf(deviceRoot+"/%s-%d", binName, os.Getpid()) + deviceGopath := deviceGotmp + "/gopath" + defer run("exec-out", "rm", "-rf", deviceGotmp) // Clean up. // Determine the package by examining the current working // directory, which will look something like @@ -88,24 +94,27 @@ func main() { // We extract everything after the $GOROOT or $GOPATH to run on the // same relative directory on the target device. subdir, inGoRoot := subdir() - deviceCwd := filepath.Join(deviceGoroot, subdir) - if !inGoRoot { - deviceCwd = filepath.Join(deviceGopath, subdir) + deviceCwd := filepath.Join(deviceGopath, subdir) + if inGoRoot { + deviceCwd = filepath.Join(deviceGoroot, subdir) } else { - adbSyncGoroot() - } - run("exec-out", "mkdir", "-p", deviceCwd) + run("exec-out", "mkdir", "-p", deviceCwd) + adbCopyTestdata(deviceCwd, subdir) + + // Copy .go files from the package. + goFiles, err := filepath.Glob("*.go") + if err != nil { + log.Fatal(err) + } + if len(goFiles) > 0 { + args := append(append([]string{"push"}, goFiles...), deviceCwd) + run(args...) + } + } - // Binary names can conflict. - // E.g. template.test from the {html,text}/template packages. - binName := fmt.Sprintf("%s-%d", filepath.Base(os.Args[1]), os.Getpid()) deviceBin := fmt.Sprintf("%s/%s", deviceGotmp, binName) run("push", os.Args[1], deviceBin) - if _, err := os.Stat("testdata"); err == nil { - run("push", "--sync", "testdata", deviceCwd) - } - // Forward SIGQUIT from the go command to show backtraces from // the binary instead of from this wrapper. quit := make(chan os.Signal, 1) @@ -125,6 +134,9 @@ func main() { cmd := `export TMPDIR="` + deviceGotmp + `"` + `; export GOROOT="` + deviceGoroot + `"` + `; export GOPATH="` + deviceGopath + `"` + + `; export CGO_ENABLED=0` + + `; export GOCACHE="` + deviceRoot + `/gocache"` + + `; export PATH=$PATH:"` + deviceGoroot + `/bin"` + `; cd "` + deviceCwd + `"` + "; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") + "; echo -n " + exitstr + "$?" @@ -132,8 +144,6 @@ func main() { signal.Reset(syscall.SIGQUIT) close(quit) - run("exec-out", "rm", "-rf", deviceGotmp) // Clean up. - exitIdx := strings.LastIndex(output, exitstr) if exitIdx == -1 { log.Fatalf("no exit code: %q", output) @@ -186,9 +196,34 @@ func subdir() (pkgpath string, underGoRoot bool) { return "", false } -// adbSyncGoroot ensures that files necessary for testing the Go standard -// packages are present on the attached device. -func adbSyncGoroot() { +// adbCopyTestdata copies testdata directories from subdir to deviceCwd +// on the device. +// It is common for tests to reach out into testdata from parent +// packages, so copy testdata directories all the way up to the root +// of subdir. +func adbCopyTestdata(deviceCwd, subdir string) { + dir := "" + for { + testdata := filepath.Join(dir, "testdata") + if _, err := os.Stat(testdata); err == nil { + devicePath := filepath.Join(deviceCwd, dir) + run("exec-out", "mkdir", "-p", devicePath) + run("push", testdata, devicePath) + } + if subdir == "." { + break + } + subdir = filepath.Dir(subdir) + dir = filepath.Join(dir, "..") + } +} + +// adbCopyGoroot clears deviceRoot for previous versions of GOROOT, GOPATH +// and temporary data. Then, it copies relevant parts of GOROOT to the device, +// including the go tool built for android. +// A lock file ensures this only happens once, even with concurrent exec +// wrappers. +func adbCopyGoroot() { // Also known by cmd/dist. The bootstrap command deletes the file. statPath := filepath.Join(os.TempDir(), "go_android_exec-adb-sync-status") stat, err := os.OpenFile(statPath, os.O_CREATE|os.O_RDWR, 0666) @@ -196,7 +231,7 @@ func adbSyncGoroot() { log.Fatal(err) } defer stat.Close() - // Serialize check and syncing. + // Serialize check and copying. if err := syscall.Flock(int(stat.Fd()), syscall.LOCK_EX); err != nil { log.Fatal(err) } @@ -207,23 +242,29 @@ func adbSyncGoroot() { if string(s) == "done" { return } - devRoot := "/data/local/tmp/goroot" - run("exec-out", "rm", "-rf", devRoot) - run("exec-out", "mkdir", "-p", devRoot+"/pkg") + // Delete GOROOT, GOPATH and any leftover test data. + run("exec-out", "rm", "-rf", deviceRoot) + deviceBin := filepath.Join(deviceGoroot, "bin") + run("exec-out", "mkdir", "-p", deviceBin) goroot := runtime.GOROOT() + // Build go for android. goCmd := filepath.Join(goroot, "bin", "go") - runtimea, err := exec.Command(goCmd, "list", "-f", "{{.Target}}", "runtime").Output() + tmpGo, err := ioutil.TempFile("", "go_android_exec-cmd-go-*") if err != nil { log.Fatal(err) } - pkgdir := filepath.Dir(string(runtimea)) - if pkgdir == "" { - log.Fatal("could not find android pkg dir") + tmpGo.Close() + defer os.Remove(tmpGo.Name()) + + if out, err := exec.Command(goCmd, "build", "-o", tmpGo.Name(), "cmd/go").CombinedOutput(); err != nil { + log.Fatalf("failed to build go tool for device: %s\n%v", out, err) } - for _, dir := range []string{"src", "test", "lib"} { - run("push", filepath.Join(goroot, dir), filepath.Join(devRoot)) + deviceGo := filepath.Join(deviceBin, "go") + run("push", tmpGo.Name(), deviceGo) + for _, dir := range []string{"pkg", "src", "test", "lib", "api"} { + run("push", filepath.Join(goroot, dir), filepath.Join(deviceGoroot)) } - run("push", filepath.Join(pkgdir), filepath.Join(devRoot, "pkg/")) + if _, err := stat.Write([]byte("done")); err != nil { log.Fatal(err) } diff --git a/misc/cgo/life/life_test.go b/misc/cgo/life/life_test.go index 0f024c9d1d..3c95d87d8a 100644 --- a/misc/cgo/life/life_test.go +++ b/misc/cgo/life/life_test.go @@ -46,6 +46,9 @@ func testMain(m *testing.M) int { } func TestTestRun(t *testing.T) { + if os.Getenv("GOOS") == "android" { + t.Skip("the go tool runs with CGO_ENABLED=0 on the android device") + } out, err := exec.Command("go", "env", "GOROOT").Output() if err != nil { t.Fatal(err) diff --git a/misc/cgo/stdio/stdio_test.go b/misc/cgo/stdio/stdio_test.go index 85ab6ae3e5..ab5d328f67 100644 --- a/misc/cgo/stdio/stdio_test.go +++ b/misc/cgo/stdio/stdio_test.go @@ -46,6 +46,9 @@ func testMain(m *testing.M) int { } func TestTestRun(t *testing.T) { + if os.Getenv("GOOS") == "android" { + t.Skip("subpackage stdio is not available on android") + } out, err := exec.Command("go", "env", "GOROOT").Output() if err != nil { t.Fatal(err)