// 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/pkg/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) }