}
const (
- deviceGoroot = "/data/local/tmp/goroot"
- deviceGopath = "/data/local/tmp/gopath"
+ deviceRoot = "/data/local/tmp/go_exec_android"
+ deviceGoroot = deviceRoot + "/goroot"
)
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
// 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)
+ 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...)
+ }
}
- run("exec-out", "mkdir", "-p", deviceCwd)
- // 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)
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 + "$?"
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)
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)
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)
}
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)
}