| 
									
										
										
										
											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 ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	"go/build" | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	"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() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	// Directory structure on the target device androidtest.bash assumes. | 
					
						
							|  |  |  | 	deviceGoroot = "/data/local/tmp/goroot" | 
					
						
							|  |  |  | 	deviceGopath = "/data/local/tmp/gopath" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | func main() { | 
					
						
							|  |  |  | 	log.SetFlags(0) | 
					
						
							|  |  |  | 	log.SetPrefix("go_android_exec: ") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	// 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 | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							|  |  |  | 	subdir, inGoRoot := subdir() | 
					
						
							|  |  |  | 	deviceCwd := filepath.Join(deviceGoroot, subdir) | 
					
						
							|  |  |  | 	if !inGoRoot { | 
					
						
							|  |  |  | 		deviceCwd = filepath.Join(deviceGopath, subdir) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Binary names can conflict. | 
					
						
							|  |  |  | 	// E.g. template.test from the {html,text}/template packages. | 
					
						
							|  |  |  | 	binName := filepath.Base(os.Args[1]) | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	deviceBin := fmt.Sprintf("%s/%s-%d", deviceGotmp, binName, os.Getpid()) | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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. | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 	//      $ adb shell false | 
					
						
							|  |  |  | 	//      $ echo $? | 
					
						
							|  |  |  | 	//      0 | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 	// 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=" | 
					
						
							| 
									
										
										
										
											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 + `"` + | 
					
						
							|  |  |  | 		`; cd "` + deviceCwd + `"` + | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 		"; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") + | 
					
						
							|  |  |  | 		"; echo -n " + exitstr + "$?" | 
					
						
							|  |  |  | 	output := run("shell", cmd) | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	run("shell", "rm", "-rf", deviceGotmp) // Clean up. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-03 11:41:26 +02:00
										 |  |  | 	exitIdx := strings.LastIndex(output, exitstr) | 
					
						
							|  |  |  | 	if exitIdx == -1 { | 
					
						
							| 
									
										
										
										
											2014-07-09 06:56:49 -04:00
										 |  |  | 		log.Fatalf("no exit code: %q", output) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 		log.Fatalf("bad exit code: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	os.Exit(code) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-15 16:47:41 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | // 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 | 
					
						
							|  |  |  | } |