]> Cypherpunks.ru repositories - gostls13.git/blob - misc/android/go_android_exec.go
all: make copyright headers consistent with one space after period
[gostls13.git] / misc / android / go_android_exec.go
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.
4
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.
7 package main
8
9 import (
10         "bytes"
11         "fmt"
12         "go/build"
13         "io"
14         "log"
15         "os"
16         "os/exec"
17         "path/filepath"
18         "runtime"
19         "strconv"
20         "strings"
21 )
22
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, " "))
29         err := cmd.Run()
30         if err != nil {
31                 log.Fatalf("adb %s: %v", strings.Join(args, " "), err)
32         }
33         return buf.String()
34 }
35
36 const (
37         // Directory structure on the target device androidtest.bash assumes.
38         deviceGoroot = "/data/local/tmp/goroot"
39         deviceGopath = "/data/local/tmp/gopath"
40 )
41
42 func main() {
43         log.SetFlags(0)
44         log.SetPrefix("go_android_exec: ")
45
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)
50
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)
58         if !inGoRoot {
59                 deviceCwd = filepath.Join(deviceGopath, subdir)
60         }
61
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())
66
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'")
75
76         // The adb shell command will return an exit code of 0 regardless
77         // of the command run. E.g.
78         //      $ adb shell false
79         //      $ echo $?
80         //      0
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)
91
92         run("shell", "rm", "-rf", deviceGotmp) // Clean up.
93
94         output = output[strings.LastIndex(output, "\n")+1:]
95         if !strings.HasPrefix(output, exitstr) {
96                 log.Fatalf("no exit code: %q", output)
97         }
98         code, err := strconv.Atoi(output[len(exitstr):])
99         if err != nil {
100                 log.Fatalf("bad exit code: %v", err)
101         }
102         os.Exit(code)
103 }
104
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()
109         if err != nil {
110                 log.Fatal(err)
111         }
112         if root := runtime.GOROOT(); strings.HasPrefix(cwd, root) {
113                 subdir, err := filepath.Rel(root, cwd)
114                 if err != nil {
115                         log.Fatal(err)
116                 }
117                 return subdir, true
118         }
119
120         for _, p := range filepath.SplitList(build.Default.GOPATH) {
121                 if !strings.HasPrefix(cwd, p) {
122                         continue
123                 }
124                 subdir, err := filepath.Rel(p, cwd)
125                 if err == nil {
126                         return subdir, false
127                 }
128         }
129         log.Fatalf("the current path %q is not in either GOROOT(%q) or GOPATH(%q)",
130                 cwd, runtime.GOROOT(), build.Default.GOPATH)
131         return "", false
132 }