go_android_exec.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. //go:build ignore
  5. // +build ignore
  6. // This program can be used as go_android_GOARCH_exec by the Go tool.
  7. // It executes binaries on an android device using adb.
  8. package main
  9. import (
  10. "bytes"
  11. "errors"
  12. "fmt"
  13. "go/build"
  14. "io"
  15. "log"
  16. "os"
  17. "os/exec"
  18. "os/signal"
  19. "path/filepath"
  20. "runtime"
  21. "strconv"
  22. "strings"
  23. "syscall"
  24. )
  25. func run(args ...string) (string, error) {
  26. cmd := adbCmd(args...)
  27. buf := new(bytes.Buffer)
  28. cmd.Stdout = io.MultiWriter(os.Stdout, buf)
  29. // If the adb subprocess somehow hangs, go test will kill this wrapper
  30. // and wait for our os.Stderr (and os.Stdout) to close as a result.
  31. // However, if the os.Stderr (or os.Stdout) file descriptors are
  32. // passed on, the hanging adb subprocess will hold them open and
  33. // go test will hang forever.
  34. //
  35. // Avoid that by wrapping stderr, breaking the short circuit and
  36. // forcing cmd.Run to use another pipe and goroutine to pass
  37. // along stderr from adb.
  38. cmd.Stderr = struct{ io.Writer }{os.Stderr}
  39. err := cmd.Run()
  40. if err != nil {
  41. return "", fmt.Errorf("adb %s: %v", strings.Join(args, " "), err)
  42. }
  43. return buf.String(), nil
  44. }
  45. func adb(args ...string) error {
  46. if out, err := adbCmd(args...).CombinedOutput(); err != nil {
  47. fmt.Fprintf(os.Stderr, "adb %s\n%s", strings.Join(args, " "), out)
  48. return err
  49. }
  50. return nil
  51. }
  52. func adbCmd(args ...string) *exec.Cmd {
  53. if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" {
  54. args = append(strings.Split(flags, " "), args...)
  55. }
  56. return exec.Command("adb", args...)
  57. }
  58. const (
  59. deviceRoot = "/data/local/tmp/go_android_exec"
  60. deviceGoroot = deviceRoot + "/goroot"
  61. )
  62. func main() {
  63. log.SetFlags(0)
  64. log.SetPrefix("go_android_exec: ")
  65. exitCode, err := runMain()
  66. if err != nil {
  67. log.Fatal(err)
  68. }
  69. os.Exit(exitCode)
  70. }
  71. func runMain() (int, error) {
  72. // Concurrent use of adb is flaky, so serialize adb commands.
  73. // See https://github.com/golang/go/issues/23795 or
  74. // https://issuetracker.google.com/issues/73230216.
  75. lockPath := filepath.Join(os.TempDir(), "go_android_exec-adb-lock")
  76. lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0666)
  77. if err != nil {
  78. return 0, err
  79. }
  80. defer lock.Close()
  81. if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
  82. return 0, err
  83. }
  84. // In case we're booting a device or emulator alongside all.bash, wait for
  85. // it to be ready. adb wait-for-device is not enough, we have to
  86. // wait for sys.boot_completed.
  87. if err := adb("wait-for-device", "exec-out", "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"); err != nil {
  88. return 0, err
  89. }
  90. // Done once per make.bash.
  91. if err := adbCopyGoroot(); err != nil {
  92. return 0, err
  93. }
  94. // Prepare a temporary directory that will be cleaned up at the end.
  95. // Binary names can conflict.
  96. // E.g. template.test from the {html,text}/template packages.
  97. binName := filepath.Base(os.Args[1])
  98. deviceGotmp := fmt.Sprintf(deviceRoot+"/%s-%d", binName, os.Getpid())
  99. deviceGopath := deviceGotmp + "/gopath"
  100. defer adb("exec-out", "rm", "-rf", deviceGotmp) // Clean up.
  101. // Determine the package by examining the current working
  102. // directory, which will look something like
  103. // "$GOROOT/src/mime/multipart" or "$GOPATH/src/golang.org/x/mobile".
  104. // We extract everything after the $GOROOT or $GOPATH to run on the
  105. // same relative directory on the target device.
  106. subdir, inGoRoot, err := subdir()
  107. if err != nil {
  108. return 0, err
  109. }
  110. deviceCwd := filepath.Join(deviceGopath, subdir)
  111. if inGoRoot {
  112. deviceCwd = filepath.Join(deviceGoroot, subdir)
  113. } else {
  114. if err := adb("exec-out", "mkdir", "-p", deviceCwd); err != nil {
  115. return 0, err
  116. }
  117. if err := adbCopyTree(deviceCwd, subdir); err != nil {
  118. return 0, err
  119. }
  120. // Copy .go files from the package.
  121. goFiles, err := filepath.Glob("*.go")
  122. if err != nil {
  123. return 0, err
  124. }
  125. if len(goFiles) > 0 {
  126. args := append(append([]string{"push"}, goFiles...), deviceCwd)
  127. if err := adb(args...); err != nil {
  128. return 0, err
  129. }
  130. }
  131. }
  132. deviceBin := fmt.Sprintf("%s/%s", deviceGotmp, binName)
  133. if err := adb("push", os.Args[1], deviceBin); err != nil {
  134. return 0, err
  135. }
  136. // Forward SIGQUIT from the go command to show backtraces from
  137. // the binary instead of from this wrapper.
  138. quit := make(chan os.Signal, 1)
  139. signal.Notify(quit, syscall.SIGQUIT)
  140. go func() {
  141. for range quit {
  142. // We don't have the PID of the running process; use the
  143. // binary name instead.
  144. adb("exec-out", "killall -QUIT "+binName)
  145. }
  146. }()
  147. // In light of
  148. // https://code.google.com/p/android/issues/detail?id=3254
  149. // dont trust the exitcode of adb. Instead, append the exitcode to
  150. // the output and parse it from there.
  151. const exitstr = "exitcode="
  152. cmd := `export TMPDIR="` + deviceGotmp + `"` +
  153. `; export GOROOT="` + deviceGoroot + `"` +
  154. `; export GOPATH="` + deviceGopath + `"` +
  155. `; export CGO_ENABLED=0` +
  156. `; export GOPROXY=` + os.Getenv("GOPROXY") +
  157. `; export GOCACHE="` + deviceRoot + `/gocache"` +
  158. `; export PATH=$PATH:"` + deviceGoroot + `/bin"` +
  159. `; cd "` + deviceCwd + `"` +
  160. "; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
  161. "; echo -n " + exitstr + "$?"
  162. output, err := run("exec-out", cmd)
  163. signal.Reset(syscall.SIGQUIT)
  164. close(quit)
  165. if err != nil {
  166. return 0, err
  167. }
  168. exitIdx := strings.LastIndex(output, exitstr)
  169. if exitIdx == -1 {
  170. return 0, fmt.Errorf("no exit code: %q", output)
  171. }
  172. code, err := strconv.Atoi(output[exitIdx+len(exitstr):])
  173. if err != nil {
  174. return 0, fmt.Errorf("bad exit code: %v", err)
  175. }
  176. return code, nil
  177. }
  178. // subdir determines the package based on the current working directory,
  179. // and returns the path to the package source relative to $GOROOT (or $GOPATH).
  180. func subdir() (pkgpath string, underGoRoot bool, err error) {
  181. cwd, err := os.Getwd()
  182. if err != nil {
  183. return "", false, err
  184. }
  185. cwd, err = filepath.EvalSymlinks(cwd)
  186. if err != nil {
  187. return "", false, err
  188. }
  189. goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
  190. if err != nil {
  191. return "", false, err
  192. }
  193. if subdir, err := filepath.Rel(goroot, cwd); err == nil {
  194. if !strings.Contains(subdir, "..") {
  195. return subdir, true, nil
  196. }
  197. }
  198. for _, p := range filepath.SplitList(build.Default.GOPATH) {
  199. pabs, err := filepath.EvalSymlinks(p)
  200. if err != nil {
  201. return "", false, err
  202. }
  203. if subdir, err := filepath.Rel(pabs, cwd); err == nil {
  204. if !strings.Contains(subdir, "..") {
  205. return subdir, false, nil
  206. }
  207. }
  208. }
  209. return "", false, fmt.Errorf("the current path %q is not in either GOROOT(%q) or GOPATH(%q)",
  210. cwd, runtime.GOROOT(), build.Default.GOPATH)
  211. }
  212. // adbCopyTree copies testdata, go.mod, go.sum files from subdir
  213. // and from parent directories all the way up to the root of subdir.
  214. // go.mod and go.sum files are needed for the go tool modules queries,
  215. // and the testdata directories for tests. It is common for tests to
  216. // reach out into testdata from parent packages.
  217. func adbCopyTree(deviceCwd, subdir string) error {
  218. dir := ""
  219. for {
  220. for _, path := range []string{"testdata", "go.mod", "go.sum"} {
  221. path := filepath.Join(dir, path)
  222. if _, err := os.Stat(path); err != nil {
  223. continue
  224. }
  225. devicePath := filepath.Join(deviceCwd, dir)
  226. if err := adb("exec-out", "mkdir", "-p", devicePath); err != nil {
  227. return err
  228. }
  229. if err := adb("push", path, devicePath); err != nil {
  230. return err
  231. }
  232. }
  233. if subdir == "." {
  234. break
  235. }
  236. subdir = filepath.Dir(subdir)
  237. dir = filepath.Join(dir, "..")
  238. }
  239. return nil
  240. }
  241. // adbCopyGoroot clears deviceRoot for previous versions of GOROOT, GOPATH
  242. // and temporary data. Then, it copies relevant parts of GOROOT to the device,
  243. // including the go tool built for android.
  244. // A lock file ensures this only happens once, even with concurrent exec
  245. // wrappers.
  246. func adbCopyGoroot() error {
  247. // Also known by cmd/dist. The bootstrap command deletes the file.
  248. statPath := filepath.Join(os.TempDir(), "go_android_exec-adb-sync-status")
  249. stat, err := os.OpenFile(statPath, os.O_CREATE|os.O_RDWR, 0666)
  250. if err != nil {
  251. return err
  252. }
  253. defer stat.Close()
  254. // Serialize check and copying.
  255. if err := syscall.Flock(int(stat.Fd()), syscall.LOCK_EX); err != nil {
  256. return err
  257. }
  258. s, err := io.ReadAll(stat)
  259. if err != nil {
  260. return err
  261. }
  262. if string(s) == "done" {
  263. return nil
  264. }
  265. // Delete GOROOT, GOPATH and any leftover test data.
  266. if err := adb("exec-out", "rm", "-rf", deviceRoot); err != nil {
  267. return err
  268. }
  269. deviceBin := filepath.Join(deviceGoroot, "bin")
  270. if err := adb("exec-out", "mkdir", "-p", deviceBin); err != nil {
  271. return err
  272. }
  273. goroot := runtime.GOROOT()
  274. // Build go for android.
  275. goCmd := filepath.Join(goroot, "bin", "go")
  276. tmpGo, err := os.CreateTemp("", "go_android_exec-cmd-go-*")
  277. if err != nil {
  278. return err
  279. }
  280. tmpGo.Close()
  281. defer os.Remove(tmpGo.Name())
  282. if out, err := exec.Command(goCmd, "build", "-o", tmpGo.Name(), "cmd/go").CombinedOutput(); err != nil {
  283. return fmt.Errorf("failed to build go tool for device: %s\n%v", out, err)
  284. }
  285. deviceGo := filepath.Join(deviceBin, "go")
  286. if err := adb("push", tmpGo.Name(), deviceGo); err != nil {
  287. return err
  288. }
  289. for _, dir := range []string{"src", "test", "lib", "api"} {
  290. if err := adb("push", filepath.Join(goroot, dir), filepath.Join(deviceGoroot)); err != nil {
  291. return err
  292. }
  293. }
  294. // Copy only the relevant from pkg.
  295. if err := adb("exec-out", "mkdir", "-p", filepath.Join(deviceGoroot, "pkg", "tool")); err != nil {
  296. return err
  297. }
  298. if err := adb("push", filepath.Join(goroot, "pkg", "include"), filepath.Join(deviceGoroot, "pkg")); err != nil {
  299. return err
  300. }
  301. runtimea, err := exec.Command(goCmd, "list", "-f", "{{.Target}}", "runtime").Output()
  302. pkgdir := filepath.Dir(string(runtimea))
  303. if pkgdir == "" {
  304. return errors.New("could not find android pkg dir")
  305. }
  306. if err := adb("push", pkgdir, filepath.Join(deviceGoroot, "pkg")); err != nil {
  307. return err
  308. }
  309. tooldir := filepath.Join(goroot, "pkg", "tool", filepath.Base(pkgdir))
  310. if err := adb("push", tooldir, filepath.Join(deviceGoroot, "pkg", "tool")); err != nil {
  311. return err
  312. }
  313. if _, err := stat.Write([]byte("done")); err != nil {
  314. return err
  315. }
  316. return nil
  317. }