1 // Copyright 2014 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.
5 // This program can be used as go_android_GOARCH_exec by the Go tool.
6 // It executes binaries on an android device using adb.
23 func run(args ...string) string {
24 buf := new(bytes.Buffer)
25 cmd := exec.Command("adb", args...)
26 cmd.Stdout = io.MultiWriter(os.Stdout, buf)
27 cmd.Stderr = os.Stderr
28 log.Printf("adb %s", strings.Join(args, " "))
31 log.Fatalf("adb %s: %v", strings.Join(args, " "), err)
37 // Directory structure on the target device androidtest.bash assumes.
38 deviceGoroot = "/data/local/tmp/goroot"
39 deviceGopath = "/data/local/tmp/gopath"
44 log.SetPrefix("go_android_exec: ")
46 // Prepare a temporary directory that will be cleaned up at the end.
47 deviceGotmp := fmt.Sprintf("/data/local/tmp/%s-%d",
48 filepath.Base(os.Args[1]), os.Getpid())
49 run("shell", "mkdir", "-p", deviceGotmp)
51 // Determine the package by examining the current working
52 // directory, which will look something like
53 // "$GOROOT/src/mime/multipart" or "$GOPATH/src/golang.org/x/mobile".
54 // We extract everything after the $GOROOT or $GOPATH to run on the
55 // same relative directory on the target device.
56 subdir, inGoRoot := subdir()
57 deviceCwd := filepath.Join(deviceGoroot, subdir)
59 deviceCwd = filepath.Join(deviceGopath, subdir)
62 // Binary names can conflict.
63 // E.g. template.test from the {html,text}/template packages.
64 binName := filepath.Base(os.Args[1])
65 deviceBin := fmt.Sprintf("%s/%s-%d", deviceGotmp, binName, os.Getpid())
67 // The push of the binary happens in parallel with other tests.
68 // Unfortunately, a simultaneous call to adb shell hold open
69 // file descriptors, so it is necessary to push then move to
70 // avoid a "text file busy" error on execution.
71 // https://code.google.com/p/android/issues/detail?id=65857
72 run("push", os.Args[1], deviceBin+"-tmp")
73 run("shell", "cp '"+deviceBin+"-tmp' '"+deviceBin+"'")
74 run("shell", "rm '"+deviceBin+"-tmp'")
76 // The adb shell command will return an exit code of 0 regardless
77 // of the command run. E.g.
81 // https://code.google.com/p/android/issues/detail?id=3254
82 // So we append the exitcode to the output and parse it from there.
83 const exitstr = "exitcode="
84 cmd := `export TMPDIR="` + deviceGotmp + `"` +
85 `; export GOROOT="` + deviceGoroot + `"` +
86 `; export GOPATH="` + deviceGopath + `"` +
87 `; cd "` + deviceCwd + `"` +
88 "; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
89 "; echo -n " + exitstr + "$?"
90 output := run("shell", cmd)
92 run("shell", "rm", "-rf", deviceGotmp) // Clean up.
94 output = output[strings.LastIndex(output, "\n")+1:]
95 if !strings.HasPrefix(output, exitstr) {
96 log.Fatalf("no exit code: %q", output)
98 code, err := strconv.Atoi(output[len(exitstr):])
100 log.Fatalf("bad exit code: %v", err)
105 // subdir determines the package based on the current working directory,
106 // and returns the path to the package source relative to $GOROOT (or $GOPATH).
107 func subdir() (pkgpath string, underGoRoot bool) {
108 cwd, err := os.Getwd()
112 if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) {
113 subdir, err := filepath.Rel(root, cwd)
120 for _, p := range filepath.SplitList(build.Default.GOPATH) {
121 if !strings.HasPrefix(cwd, p) {
124 subdir, err := filepath.Rel(p, cwd)
129 log.Fatalf("the current path %q is not in either GOROOT(%q) or GOPATH(%q)",
130 cwd, runtime.GOROOT(), build.Default.GOPATH)