-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgo_android_exec.go
132 lines (118 loc) · 3.89 KB
/
go_android_exec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// 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"
"go/build"
"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()
}
const (
// Directory structure on the target device androidtest.bash assumes.
deviceGoroot = "/data/local/tmp/goroot"
deviceGopath = "/data/local/tmp/gopath"
)
func main() {
log.SetFlags(0)
log.SetPrefix("go_android_exec: ")
// 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("shell", "mkdir", "-p", deviceGotmp)
// Determine the package by examining the current working
// directory, which will look something like
// "$GOROOT/src/mime/multipart" or "$GOPATH/src/golang.org/x/mobile".
// 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)
}
// Binary names can conflict.
// E.g. template.test from the {html,text}/template packages.
binName := filepath.Base(os.Args[1])
deviceBin := fmt.Sprintf("%s/%s-%d", deviceGotmp, 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="` + deviceGotmp + `"` +
`; export GOROOT="` + deviceGoroot + `"` +
`; export GOPATH="` + deviceGopath + `"` +
`; cd "` + deviceCwd + `"` +
"; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
"; echo -n " + exitstr + "$?"
output := run("shell", cmd)
run("shell", "rm", "-rf", deviceGotmp) // Clean up.
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)
}
// subdir determines the package based on the current working directory,
// and returns the path to the package source relative to $GOROOT (or $GOPATH).
func subdir() (pkgpath string, underGoRoot bool) {
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) {
subdir, err := filepath.Rel(root, cwd)
if err != nil {
log.Fatal(err)
}
return subdir, true
}
for _, p := range filepath.SplitList(build.Default.GOPATH) {
if !strings.HasPrefix(cwd, p) {
continue
}
subdir, err := filepath.Rel(p, cwd)
if err == nil {
return subdir, false
}
}
log.Fatalf("the current path %q is not in either GOROOT(%q) or GOPATH(%q)",
cwd, runtime.GOROOT(), build.Default.GOPATH)
return "", false
}