github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/misc/android/go_android_exec.go (about)

     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  	"io"
    13  	"log"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"runtime"
    18  	"strconv"
    19  	"strings"
    20  )
    21  
    22  func run(args ...string) string {
    23  	buf := new(bytes.Buffer)
    24  	cmd := exec.Command("adb", args...)
    25  	cmd.Stdout = io.MultiWriter(os.Stdout, buf)
    26  	cmd.Stderr = os.Stderr
    27  	log.Printf("adb %s", strings.Join(args, " "))
    28  	err := cmd.Run()
    29  	if err != nil {
    30  		log.Fatalf("adb %s: %v", strings.Join(args, " "), err)
    31  	}
    32  	return buf.String()
    33  }
    34  
    35  func main() {
    36  	log.SetFlags(0)
    37  	log.SetPrefix("go_android_exec: ")
    38  
    39  	// Determine thepackage by examining the current working
    40  	// directory, which will look something like
    41  	// "$GOROOT/src/mime/multipart". We extract everything
    42  	// after the $GOROOT to run on the same relative directory
    43  	// on the target device.
    44  	//
    45  	// TODO(crawshaw): Pick useful subdir when we are not
    46  	// inside a GOROOT, e.g. we are in a GOPATH.
    47  	cwd, err := os.Getwd()
    48  	if err != nil {
    49  		log.Fatal(err)
    50  	}
    51  	subdir, err := filepath.Rel(runtime.GOROOT(), cwd)
    52  	if err != nil {
    53  		log.Fatal(err)
    54  	}
    55  	subdir = filepath.ToSlash(subdir)
    56  
    57  	// Binary names can conflict.
    58  	// E.g. template.test from the {html,text}/template packages.
    59  	binName := filepath.Base(os.Args[1])
    60  	deviceGoroot := "/data/local/tmp/goroot"
    61  	deviceBin := fmt.Sprintf("%s/%s-%d", deviceGoroot, binName, os.Getpid())
    62  
    63  	// The push of the binary happens in parallel with other tests.
    64  	// Unfortunately, a simultaneous call to adb shell hold open
    65  	// file descriptors, so it is necessary to push then move to
    66  	// avoid a "text file busy" error on execution.
    67  	// https://code.google.com/p/android/issues/detail?id=65857
    68  	run("push", os.Args[1], deviceBin+"-tmp")
    69  	run("shell", "cp '"+deviceBin+"-tmp' '"+deviceBin+"'")
    70  	run("shell", "rm '"+deviceBin+"-tmp'")
    71  
    72  	// The adb shell command will return an exit code of 0 regardless
    73  	// of the command run. E.g.
    74  	//	$ adb shell false
    75  	//	$ echo $?
    76  	//	0
    77  	// https://code.google.com/p/android/issues/detail?id=3254
    78  	// So we append the exitcode to the output and parse it from there.
    79  	const exitstr = "exitcode="
    80  	cmd := `export TMPDIR="/data/local/tmp"` +
    81  		`; export GOROOT="` + deviceGoroot + `"` +
    82  		`; cd "$GOROOT/` + subdir + `"` +
    83  		"; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
    84  		"; echo -n " + exitstr + "$?"
    85  	output := run("shell", cmd)
    86  	run("shell", "rm '"+deviceBin+"'") // cleanup
    87  	output = output[strings.LastIndex(output, "\n")+1:]
    88  	if !strings.HasPrefix(output, exitstr) {
    89  		log.Fatalf("no exit code: %q", output)
    90  	}
    91  	code, err := strconv.Atoi(output[len(exitstr):])
    92  	if err != nil {
    93  		log.Fatalf("bad exit code: %v", err)
    94  	}
    95  	os.Exit(code)
    96  }