| 
									
										
										
										
											2016-04-10 14:32:26 -07:00
										 |  |  | // Copyright 2014 The Go Authors. All rights reserved. | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | // 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 ( | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							| 
									
										
										
										
											2018-05-23 16:31:36 +02:00
										 |  |  | 	"os/signal" | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-05-23 16:31:36 +02:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | func run(args ...string) (string, error) { | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | 	cmd := adbCmd(args...) | 
					
						
							| 
									
										
										
										
											2022-09-04 17:21:08 +08:00
										 |  |  | 	buf := new(strings.Builder) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	cmd.Stdout = io.MultiWriter(os.Stdout, buf) | 
					
						
							| 
									
										
										
										
											2017-05-01 20:35:08 +02:00
										 |  |  | 	// If the adb subprocess somehow hangs, go test will kill this wrapper | 
					
						
							|  |  |  | 	// and wait for our os.Stderr (and os.Stdout) to close as a result. | 
					
						
							|  |  |  | 	// However, if the os.Stderr (or os.Stdout) file descriptors are | 
					
						
							|  |  |  | 	// passed on, the hanging adb subprocess will hold them open and | 
					
						
							|  |  |  | 	// go test will hang forever. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Avoid that by wrapping stderr, breaking the short circuit and | 
					
						
							|  |  |  | 	// forcing cmd.Run to use another pipe and goroutine to pass | 
					
						
							|  |  |  | 	// along stderr from adb. | 
					
						
							|  |  |  | 	cmd.Stderr = struct{ io.Writer }{os.Stderr} | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	err := cmd.Run() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return "", fmt.Errorf("adb %s: %v", strings.Join(args, " "), err) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	return buf.String(), nil | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | func adb(args ...string) error { | 
					
						
							|  |  |  | 	if out, err := adbCmd(args...).CombinedOutput(); err != nil { | 
					
						
							|  |  |  | 		fmt.Fprintf(os.Stderr, "adb %s\n%s", strings.Join(args, " "), out) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func adbCmd(args ...string) *exec.Cmd { | 
					
						
							|  |  |  | 	if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" { | 
					
						
							|  |  |  | 		args = append(strings.Split(flags, " "), args...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return exec.Command("adb", args...) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	deviceRoot   = "/data/local/tmp/go_android_exec" | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 	deviceGoroot = deviceRoot + "/goroot" | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | func main() { | 
					
						
							|  |  |  | 	log.SetFlags(0) | 
					
						
							|  |  |  | 	log.SetPrefix("go_android_exec: ") | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	exitCode, err := runMain() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	os.Exit(exitCode) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | func runMain() (int, error) { | 
					
						
							| 
									
										
										
										
											2019-02-25 10:52:42 +01:00
										 |  |  | 	// Concurrent use of adb is flaky, so serialize adb commands. | 
					
						
							|  |  |  | 	// See https://github.com/golang/go/issues/23795 or | 
					
						
							|  |  |  | 	// https://issuetracker.google.com/issues/73230216. | 
					
						
							|  |  |  | 	lockPath := filepath.Join(os.TempDir(), "go_android_exec-adb-lock") | 
					
						
							|  |  |  | 	lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0666) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2019-02-25 10:52:42 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer lock.Close() | 
					
						
							|  |  |  | 	if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2019-02-25 10:52:42 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 12:08:00 +01:00
										 |  |  | 	// In case we're booting a device or emulator alongside all.bash, wait for | 
					
						
							|  |  |  | 	// it to be ready. adb wait-for-device is not enough, we have to | 
					
						
							| 
									
										
										
										
											2019-02-24 16:46:23 +01:00
										 |  |  | 	// wait for sys.boot_completed. | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | 	if err := adb("wait-for-device", "exec-out", "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-24 16:46:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 	// Done once per make.bash. | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	if err := adbCopyGoroot(); err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	// Prepare a temporary directory that will be cleaned up at the end. | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 	// Binary names can conflict. | 
					
						
							|  |  |  | 	// E.g. template.test from the {html,text}/template packages. | 
					
						
							|  |  |  | 	binName := filepath.Base(os.Args[1]) | 
					
						
							|  |  |  | 	deviceGotmp := fmt.Sprintf(deviceRoot+"/%s-%d", binName, os.Getpid()) | 
					
						
							|  |  |  | 	deviceGopath := deviceGotmp + "/gopath" | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | 	defer adb("exec-out", "rm", "-rf", deviceGotmp) // Clean up. | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Determine the package by examining the current working | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	// directory, which will look something like | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	// "$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. | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 	importPath, isStd, modPath, modDir, err := pkgPath() | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	var deviceCwd string | 
					
						
							|  |  |  | 	if isStd { | 
					
						
							|  |  |  | 		// Note that we use path.Join here instead of filepath.Join: | 
					
						
							|  |  |  | 		// The device paths should be slash-separated even if the go_android_exec | 
					
						
							|  |  |  | 		// wrapper itself is compiled for Windows. | 
					
						
							|  |  |  | 		deviceCwd = path.Join(deviceGoroot, "src", importPath) | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 		deviceCwd = path.Join(deviceGopath, "src", importPath) | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 		if modDir != "" { | 
					
						
							|  |  |  | 			// In module mode, the user may reasonably expect the entire module | 
					
						
							|  |  |  | 			// to be present. Copy it over. | 
					
						
							|  |  |  | 			deviceModDir := path.Join(deviceGopath, "src", modPath) | 
					
						
							|  |  |  | 			if err := adb("exec-out", "mkdir", "-p", path.Dir(deviceModDir)); err != nil { | 
					
						
							|  |  |  | 				return 0, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// We use a single recursive 'adb push' of the module root instead of | 
					
						
							|  |  |  | 			// walking the tree and copying it piecewise. If the directory tree | 
					
						
							|  |  |  | 			// contains nested modules this could push a lot of unnecessary contents, | 
					
						
							|  |  |  | 			// but for the golang.org/x repos it seems to be significantly (~2x) | 
					
						
							|  |  |  | 			// faster than copying one file at a time (via filepath.WalkDir), | 
					
						
							|  |  |  | 			// apparently due to high latency in 'adb' commands. | 
					
						
							|  |  |  | 			if err := adb("push", modDir, deviceModDir); err != nil { | 
					
						
							|  |  |  | 				return 0, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if err := adb("exec-out", "mkdir", "-p", deviceCwd); err != nil { | 
					
						
							|  |  |  | 				return 0, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err := adbCopyTree(deviceCwd, importPath); err != nil { | 
					
						
							|  |  |  | 				return 0, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 			// Copy .go files from the package. | 
					
						
							|  |  |  | 			goFiles, err := filepath.Glob("*.go") | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 				return 0, err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 			if len(goFiles) > 0 { | 
					
						
							|  |  |  | 				args := append(append([]string{"push"}, goFiles...), deviceCwd) | 
					
						
							|  |  |  | 				if err := adb(args...); err != nil { | 
					
						
							|  |  |  | 					return 0, err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 16:31:36 +02:00
										 |  |  | 	deviceBin := fmt.Sprintf("%s/%s", deviceGotmp, binName) | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | 	if err := adb("push", os.Args[1], deviceBin); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 16:31:36 +02:00
										 |  |  | 	// Forward SIGQUIT from the go command to show backtraces from | 
					
						
							|  |  |  | 	// the binary instead of from this wrapper. | 
					
						
							|  |  |  | 	quit := make(chan os.Signal, 1) | 
					
						
							|  |  |  | 	signal.Notify(quit, syscall.SIGQUIT) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for range quit { | 
					
						
							|  |  |  | 			// We don't have the PID of the running process; use the | 
					
						
							|  |  |  | 			// binary name instead. | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | 			adb("exec-out", "killall -QUIT "+binName) | 
					
						
							| 
									
										
										
										
											2018-05-23 16:31:36 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2019-03-02 13:07:54 +01:00
										 |  |  | 	// In light of | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	// https://code.google.com/p/android/issues/detail?id=3254 | 
					
						
							| 
									
										
										
										
											2019-03-02 13:07:54 +01:00
										 |  |  | 	// dont trust the exitcode of adb. Instead, append the exitcode to | 
					
						
							|  |  |  | 	// the output and parse it from there. | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	const exitstr = "exitcode=" | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	cmd := `export TMPDIR="` + deviceGotmp + `"` + | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 		`; export GOROOT="` + deviceGoroot + `"` + | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 		`; export GOPATH="` + deviceGopath + `"` + | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 		`; export CGO_ENABLED=0` + | 
					
						
							| 
									
										
										
										
											2019-03-19 06:59:51 +01:00
										 |  |  | 		`; export GOPROXY=` + os.Getenv("GOPROXY") + | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 		`; export GOCACHE="` + deviceRoot + `/gocache"` + | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 		`; export PATH="` + deviceGoroot + `/bin":$PATH` + | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 		`; cd "` + deviceCwd + `"` + | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 		"; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") + | 
					
						
							|  |  |  | 		"; echo -n " + exitstr + "$?" | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	output, err := run("exec-out", cmd) | 
					
						
							| 
									
										
										
										
											2018-05-23 16:31:36 +02:00
										 |  |  | 	signal.Reset(syscall.SIGQUIT) | 
					
						
							|  |  |  | 	close(quit) | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-03 11:41:26 +02:00
										 |  |  | 	exitIdx := strings.LastIndex(output, exitstr) | 
					
						
							|  |  |  | 	if exitIdx == -1 { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return 0, fmt.Errorf("no exit code: %q", output) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-03 11:41:26 +02:00
										 |  |  | 	code, err := strconv.Atoi(output[exitIdx+len(exitstr):]) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return 0, fmt.Errorf("bad exit code: %v", err) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	return code, nil | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | // pkgPath determines the package import path of the current working directory, | 
					
						
							|  |  |  | // and indicates whether it is | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | // and returns the path to the package source relative to $GOROOT (or $GOPATH). | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | func pkgPath() (importPath string, isStd bool, modPath, modDir string, err error) { | 
					
						
							|  |  |  | 	errorf := func(format string, args ...any) (string, bool, string, string, error) { | 
					
						
							|  |  |  | 		return "", false, "", "", fmt.Errorf(format, args...) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	goTool, err := goTool() | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 		return errorf("%w", err) | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 	cmd := exec.Command(goTool, "list", "-e", "-f", "{{.ImportPath}}:{{.Standard}}{{with .Module}}:{{.Path}}:{{.Dir}}{{end}}", ".") | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	out, err := cmd.Output() | 
					
						
							| 
									
										
										
										
											2019-03-01 08:25:35 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 		if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 			return errorf("%v: %s", cmd, ee.Stderr) | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 		return errorf("%v: %w", cmd, err) | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 	parts := strings.SplitN(string(bytes.TrimSpace(out)), ":", 4) | 
					
						
							|  |  |  | 	if len(parts) < 2 { | 
					
						
							|  |  |  | 		return errorf("%v: missing ':' in output: %q", cmd, out) | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 	importPath = parts[0] | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	if importPath == "" || importPath == "." { | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 		return errorf("current directory does not have a Go import path") | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 	isStd, err = strconv.ParseBool(parts[1]) | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 		return errorf("%v: non-boolean .Standard in output: %q", cmd, out) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(parts) >= 4 { | 
					
						
							|  |  |  | 		modPath = parts[2] | 
					
						
							|  |  |  | 		modDir = parts[3] | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-25 11:31:49 -04:00
										 |  |  | 	return importPath, isStd, modPath, modDir, nil | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 18:13:38 +01:00
										 |  |  | // adbCopyTree copies testdata, go.mod, go.sum files from subdir | 
					
						
							|  |  |  | // and from parent directories all the way up to the root of subdir. | 
					
						
							|  |  |  | // go.mod and go.sum files are needed for the go tool modules queries, | 
					
						
							|  |  |  | // and the testdata directories for tests.  It is common for tests to | 
					
						
							|  |  |  | // reach out into testdata from parent packages. | 
					
						
							|  |  |  | func adbCopyTree(deviceCwd, subdir string) error { | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 	dir := "" | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 		for _, name := range []string{"testdata", "go.mod", "go.sum"} { | 
					
						
							|  |  |  | 			hostPath := filepath.Join(dir, name) | 
					
						
							|  |  |  | 			if _, err := os.Stat(hostPath); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 18:13:38 +01:00
										 |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 			devicePath := path.Join(deviceCwd, dir) | 
					
						
							| 
									
										
										
										
											2019-05-08 23:45:06 +02:00
										 |  |  | 			if err := adb("exec-out", "mkdir", "-p", devicePath); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 			if err := adb("push", hostPath, devicePath); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if subdir == "." { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		subdir = filepath.Dir(subdir) | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 		dir = path.Join(dir, "..") | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // adbCopyGoroot clears deviceRoot for previous versions of GOROOT, GOPATH | 
					
						
							|  |  |  | // and temporary data. Then, it copies relevant parts of GOROOT to the device, | 
					
						
							|  |  |  | // including the go tool built for android. | 
					
						
							|  |  |  | // A lock file ensures this only happens once, even with concurrent exec | 
					
						
							|  |  |  | // wrappers. | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | func adbCopyGoroot() error { | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	goTool, err := goTool() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cmd := exec.Command(goTool, "version") | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	out, err := cmd.Output() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("%v: %w", cmd, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	goVersion := string(out) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	// Also known by cmd/dist. The bootstrap command deletes the file. | 
					
						
							|  |  |  | 	statPath := filepath.Join(os.TempDir(), "go_android_exec-adb-sync-status") | 
					
						
							|  |  |  | 	stat, err := os.OpenFile(statPath, os.O_CREATE|os.O_RDWR, 0666) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer stat.Close() | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 	// Serialize check and copying. | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	if err := syscall.Flock(int(stat.Fd()), syscall.LOCK_EX); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-03 08:10:47 +00:00
										 |  |  | 	s, err := io.ReadAll(stat) | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	if string(s) == goVersion { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	goroot, err := findGoroot() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Delete the device's GOROOT, GOPATH and any leftover test data, | 
					
						
							|  |  |  | 	// and recreate GOROOT. | 
					
						
							|  |  |  | 	if err := adb("exec-out", "rm", "-rf", deviceRoot); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Build Go for Android. | 
					
						
							|  |  |  | 	cmd = exec.Command(goTool, "install", "cmd") | 
					
						
							|  |  |  | 	out, err = cmd.CombinedOutput() | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 		if len(bytes.TrimSpace(out)) > 0 { | 
					
						
							|  |  |  | 			log.Printf("\n%s", out) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return fmt.Errorf("%v: %w", cmd, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := adb("exec-out", "mkdir", "-p", deviceGoroot); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	// Copy the Android tools from the relevant bin subdirectory to GOROOT/bin. | 
					
						
							|  |  |  | 	cmd = exec.Command(goTool, "list", "-f", "{{.Target}}", "cmd/go") | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	out, err = cmd.Output() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("%v: %w", cmd, err) | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	platformBin := filepath.Dir(string(bytes.TrimSpace(out))) | 
					
						
							|  |  |  | 	if platformBin == "." { | 
					
						
							|  |  |  | 		return errors.New("failed to locate cmd/go for target platform") | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	if err := adb("push", platformBin, path.Join(deviceGoroot, "bin")); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	// Copy only the relevant subdirectories from pkg: pkg/include and the | 
					
						
							|  |  |  | 	// platform-native binaries in pkg/tool. | 
					
						
							|  |  |  | 	if err := adb("exec-out", "mkdir", "-p", path.Join(deviceGoroot, "pkg", "tool")); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	if err := adb("push", filepath.Join(goroot, "pkg", "include"), path.Join(deviceGoroot, "pkg", "include")); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	cmd = exec.Command(goTool, "list", "-f", "{{.Target}}", "cmd/compile") | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 	out, err = cmd.Output() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("%v: %w", cmd, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	platformToolDir := filepath.Dir(string(bytes.TrimSpace(out))) | 
					
						
							|  |  |  | 	if platformToolDir == "." { | 
					
						
							|  |  |  | 		return errors.New("failed to locate cmd/compile for target platform") | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	relToolDir, err := filepath.Rel(filepath.Join(goroot), platformToolDir) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	if err := adb("push", platformToolDir, path.Join(deviceGoroot, relToolDir)); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-06 12:53:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 	// Copy all other files from GOROOT. | 
					
						
							|  |  |  | 	dirents, err := os.ReadDir(goroot) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, de := range dirents { | 
					
						
							|  |  |  | 		switch de.Name() { | 
					
						
							|  |  |  | 		case "bin", "pkg": | 
					
						
							|  |  |  | 			// We already created GOROOT/bin and GOROOT/pkg above; skip those. | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := adb("push", filepath.Join(goroot, de.Name()), path.Join(deviceGoroot, de.Name())); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err := stat.WriteString(goVersion); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-09 02:30:35 +01:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2019-02-24 15:18:02 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-02-28 17:07:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | func findGoroot() (string, error) { | 
					
						
							|  |  |  | 	gorootOnce.Do(func() { | 
					
						
							|  |  |  | 		// If runtime.GOROOT reports a non-empty path, assume that it is valid. | 
					
						
							|  |  |  | 		// (It may be empty if this binary was built with -trimpath.) | 
					
						
							|  |  |  | 		gorootPath = runtime.GOROOT() | 
					
						
							|  |  |  | 		if gorootPath != "" { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// runtime.GOROOT is empty — perhaps go_android_exec was built with | 
					
						
							|  |  |  | 		// -trimpath and GOROOT is unset. Try 'go env GOROOT' as a fallback, | 
					
						
							|  |  |  | 		// assuming that the 'go' command in $PATH is the correct one. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cmd := exec.Command("go", "env", "GOROOT") | 
					
						
							|  |  |  | 		cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 		out, err := cmd.Output() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			gorootErr = fmt.Errorf("%v: %w", cmd, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		gorootPath = string(bytes.TrimSpace(out)) | 
					
						
							|  |  |  | 		if gorootPath == "" { | 
					
						
							|  |  |  | 			gorootErr = errors.New("GOROOT not found") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return gorootPath, gorootErr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func goTool() (string, error) { | 
					
						
							|  |  |  | 	goroot, err := findGoroot() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return filepath.Join(goroot, "bin", "go"), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	gorootOnce sync.Once | 
					
						
							|  |  |  | 	gorootPath string | 
					
						
							|  |  |  | 	gorootErr  error | 
					
						
							|  |  |  | ) |