summaryrefslogtreecommitdiff
path: root/misc/android/go_android_exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'misc/android/go_android_exec.go')
-rw-r--r--misc/android/go_android_exec.go96
1 files changed, 96 insertions, 0 deletions
diff --git a/misc/android/go_android_exec.go b/misc/android/go_android_exec.go
new file mode 100644
index 000000000..e32a805a8
--- /dev/null
+++ b/misc/android/go_android_exec.go
@@ -0,0 +1,96 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program can be used as go_android_GOARCH_exec by the Go tool.
+// It executes binaries on an android device using adb.
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+func run(args ...string) string {
+ buf := new(bytes.Buffer)
+ cmd := exec.Command("adb", args...)
+ cmd.Stdout = io.MultiWriter(os.Stdout, buf)
+ cmd.Stderr = os.Stderr
+ log.Printf("adb %s", strings.Join(args, " "))
+ err := cmd.Run()
+ if err != nil {
+ log.Fatalf("adb %s: %v", strings.Join(args, " "), err)
+ }
+ return buf.String()
+}
+
+func main() {
+ log.SetFlags(0)
+ log.SetPrefix("go_android_exec: ")
+
+ // Determine thepackage by examining the current working
+ // directory, which will look something like
+ // "$GOROOT/src/mime/multipart". We extract everything
+ // after the $GOROOT to run on the same relative directory
+ // on the target device.
+ //
+ // TODO(crawshaw): Pick useful subdir when we are not
+ // inside a GOROOT, e.g. we are in a GOPATH.
+ cwd, err := os.Getwd()
+ if err != nil {
+ log.Fatal(err)
+ }
+ subdir, err := filepath.Rel(runtime.GOROOT(), cwd)
+ if err != nil {
+ log.Fatal(err)
+ }
+ subdir = filepath.ToSlash(subdir)
+
+ // Binary names can conflict.
+ // E.g. template.test from the {html,text}/template packages.
+ binName := filepath.Base(os.Args[1])
+ deviceGoroot := "/data/local/tmp/goroot"
+ deviceBin := fmt.Sprintf("%s/%s-%d", deviceGoroot, binName, os.Getpid())
+
+ // The push of the binary happens in parallel with other tests.
+ // Unfortunately, a simultaneous call to adb shell hold open
+ // file descriptors, so it is necessary to push then move to
+ // avoid a "text file busy" error on execution.
+ // https://code.google.com/p/android/issues/detail?id=65857
+ run("push", os.Args[1], deviceBin+"-tmp")
+ run("shell", "cp '"+deviceBin+"-tmp' '"+deviceBin+"'")
+ run("shell", "rm '"+deviceBin+"-tmp'")
+
+ // The adb shell command will return an exit code of 0 regardless
+ // of the command run. E.g.
+ // $ adb shell false
+ // $ echo $?
+ // 0
+ // https://code.google.com/p/android/issues/detail?id=3254
+ // So we append the exitcode to the output and parse it from there.
+ const exitstr = "exitcode="
+ cmd := `export TMPDIR="/data/local/tmp"` +
+ `; export GOROOT="` + deviceGoroot + `"` +
+ `; cd "$GOROOT/` + subdir + `"` +
+ "; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
+ "; echo -n " + exitstr + "$?"
+ output := run("shell", cmd)
+ run("shell", "rm '"+deviceBin+"'") // cleanup
+ output = output[strings.LastIndex(output, "\n")+1:]
+ if !strings.HasPrefix(output, exitstr) {
+ log.Fatalf("no exit code: %q", output)
+ }
+ code, err := strconv.Atoi(output[len(exitstr):])
+ if err != nil {
+ log.Fatalf("bad exit code: %v", err)
+ }
+ os.Exit(code)
+}