2
0
mirror of synced 2025-02-23 06:48:15 +00:00

cmd/gomobile: add -n and -x flags

Besides being useful for debugging, I intend to use these in a
followup CL to write some simple tests.

Change-Id: I9420c0b72111bcc86ce0330886106221aab533ba
Reviewed-on: https://go-review.googlesource.com/4690
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
David Crawshaw 2015-02-12 08:23:25 -05:00
parent 7e40b31538
commit 1a5645cad8
2 changed files with 190 additions and 74 deletions

View File

@ -23,6 +23,8 @@ import (
var ctx = build.Default
var pkg *build.Package
var ndkccpath string
var tmpdir string
var cmdBuild = &command{
run: runBuild,
@ -50,12 +52,12 @@ These build flags are shared by the build, install, and test commands.
For documentation, see 'go help build':
-a
-i
-n
-x
-tags 'tag list'
`,
}
// TODO: -n
// TODO: -x
// TODO: -mobile
func runBuild(cmd *command) error {
@ -93,11 +95,18 @@ func runBuild(cmd *command) error {
return fmt.Errorf(`%s does not import "golang.org/x/mobile/app"`, pkg.ImportPath)
}
workPath, err := ioutil.TempDir("", "gobuildapk-work-")
if err != nil {
return err
if buildN {
tmpdir = "$WORK"
} else {
tmpdir, err = ioutil.TempDir("", "gobuildapk-work-")
if err != nil {
return err
}
}
defer removeAll(tmpdir)
if buildX {
fmt.Fprintln(os.Stderr, "WORK="+tmpdir)
}
defer os.RemoveAll(workPath)
libName := path.Base(pkg.ImportPath)
manifestData, err := ioutil.ReadFile(filepath.Join(pkg.Dir, "AndroidManifest.xml"))
@ -126,20 +135,21 @@ func runBuild(cmd *command) error {
return err
}
}
libPath := filepath.Join(workPath, "lib"+libName+".so")
libPath := filepath.Join(tmpdir, "lib"+libName+".so")
gopath := goEnv("GOPATH")
var ccpath string
for _, p := range filepath.SplitList(gopath) {
ccpath = filepath.Join(p, filepath.FromSlash("pkg/gomobile/android-"+ndkVersion+"/arm/bin"))
if _, err = os.Stat(ccpath); err == nil {
ndkccpath = filepath.Join(p, filepath.FromSlash("pkg/gomobile/android-"+ndkVersion))
if _, err = os.Stat(filepath.Join(ndkccpath, "arm", "bin")); err == nil {
break
}
}
if err != nil || ccpath == "" {
if err != nil || ndkccpath == "" {
// TODO(crawshaw): call gomobile init
return fmt.Errorf("android %s toolchain not installed in $GOPATH/pkg/gomobile, run gomobile init", ndkVersion)
return fmt.Errorf("android toolchain not installed in $GOPATH/pkg/gomobile, run:\n\tgomobile init")
}
if buildX {
fmt.Fprintln(os.Stderr, "NDKCCPATH="+ndkccpath)
}
gocmd := exec.Command(
@ -164,14 +174,19 @@ func runBuild(cmd *command) error {
`GOARCH=arm`,
`GOARM=7`,
`CGO_ENABLED=1`,
`CC=` + filepath.Join(ccpath, "arm-linux-androideabi-gcc"),
`CXX=` + filepath.Join(ccpath, "arm-linux-androideabi-g++"),
`CC=` + filepath.Join(ndkccpath, "arm", "bin", "arm-linux-androideabi-gcc"),
`CXX=` + filepath.Join(ndkccpath, "arm", "bin", "arm-linux-androideabi-g++"),
`GOGCCFLAGS="-fPIC -marm -pthread -fmessage-length=0"`,
`GOROOT=` + goEnv("GOROOT"),
`GOPATH=` + gopath,
}
if err := gocmd.Run(); err != nil {
return err
if buildX {
printcmd("%s", strings.Join(gocmd.Env, " ")+" "+strings.Join(gocmd.Args, " "))
}
if !buildN {
if err := gocmd.Run(); err != nil {
return err
}
}
block, _ := pem.Decode([]byte(debugCert))
@ -194,9 +209,21 @@ func runBuild(cmd *command) error {
return err
}
apkw := NewWriter(out, privKey)
var apkw *Writer
if !buildN {
apkw = NewWriter(out, privKey)
}
apkwcreate := func(name string) (io.Writer, error) {
if buildV {
fmt.Fprintf(os.Stderr, "apk: %s\n", name)
}
if buildN {
return ioutil.Discard, nil
}
return apkw.Create(name)
}
w, err := apkw.Create("AndroidManifest.xml")
w, err := apkwcreate("AndroidManifest.xml")
if err != nil {
return err
}
@ -204,16 +231,18 @@ func runBuild(cmd *command) error {
return err
}
r, err := os.Open(libPath)
w, err = apkwcreate("lib/armeabi/lib" + libName + ".so")
if err != nil {
return err
}
w, err = apkw.Create("lib/armeabi/lib" + libName + ".so")
if err != nil {
return err
}
if _, err := io.Copy(w, r); err != nil {
return err
if !buildN {
r, err := os.Open(libPath)
if err != nil {
return err
}
if _, err := io.Copy(w, r); err != nil {
return err
}
}
// Add any assets.
@ -238,7 +267,7 @@ func runBuild(cmd *command) error {
return nil
}
name := "assets/" + path[len(assetsDir)+1:]
w, err := apkw.Create(name)
w, err := apkwcreate(name)
if err != nil {
return err
}
@ -254,14 +283,30 @@ func runBuild(cmd *command) error {
// TODO: add gdbserver to apk?
if buildN {
return nil
}
return apkw.Close()
}
func printcmd(format string, args ...interface{}) {
cmd := fmt.Sprintf(format+"\n", args...)
if tmpdir != "" {
cmd = strings.Replace(cmd, tmpdir, "$WORK", -1)
}
if ndkccpath != "" {
cmd = strings.Replace(cmd, ndkccpath, "$NDKCCPATH", -1)
}
fmt.Fprint(os.Stderr, cmd)
}
// "Build flags", used by multiple commands.
var (
buildA bool // -a
buildV bool // -v
buildI bool // -i
buildN bool // -n
buildV bool // -v
buildX bool // -x
buildO *string // -o
)
@ -271,20 +316,21 @@ func addBuildFlags(cmd *command) {
cmd.flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "")
}
func addBuildFlagsNXV(cmd *command) {
// TODO: -n, -x
func addBuildFlagsNVX(cmd *command) {
cmd.flag.BoolVar(&buildN, "n", false, "")
cmd.flag.BoolVar(&buildV, "v", false, "")
cmd.flag.BoolVar(&buildX, "x", false, "")
}
func init() {
buildO = cmdBuild.flag.String("o", "", "output file")
addBuildFlags(cmdBuild)
addBuildFlagsNXV(cmdBuild)
addBuildFlagsNVX(cmdBuild)
addBuildFlags(cmdInstall)
addBuildFlagsNXV(cmdInstall)
addBuildFlagsNVX(cmdInstall)
addBuildFlagsNXV(cmdInit)
addBuildFlagsNVX(cmdInit)
}
// A random uninteresting private key.

View File

@ -57,20 +57,33 @@ func runInit(cmd *command) error {
if len(gopaths) == 0 {
return fmt.Errorf("GOPATH is not set")
}
ndkccpath = filepath.Join(gopaths[0], filepath.FromSlash("pkg/gomobile/android-"+ndkVersion))
if buildX {
fmt.Fprintln(os.Stderr, "NDKCCPATH="+ndkccpath)
}
dst := filepath.Join(gopaths[0], filepath.FromSlash("pkg/gomobile/android-"+ndkVersion+"/arm"))
if err := os.RemoveAll(dst); err != nil && !os.IsExist(err) {
if err := removeAll(ndkccpath); err != nil && !os.IsExist(err) {
return err
}
if err := os.MkdirAll(filepath.Join(dst, "sysroot", "usr"), 0755); err != nil {
dst := filepath.Join(ndkccpath, "arm")
dstSysroot := filepath.Join(dst, "sysroot", "usr")
if err := mkdir(dstSysroot); err != nil {
return err
}
tmpdir, err := ioutil.TempDir(dst, "gomobile-init-")
if err != nil {
return err
if buildN {
tmpdir = filepath.Join(ndkccpath, "work")
} else {
var err error
tmpdir, err = ioutil.TempDir(ndkccpath, "gomobile-init-")
if err != nil {
return err
}
}
defer os.RemoveAll(tmpdir)
if buildX {
fmt.Fprintln(os.Stderr, "WORK="+tmpdir)
}
defer removeAll(tmpdir)
ndkName := "android-" + ndkVersion + "-" + runtime.GOOS + "-" + arch + "."
if runtime.GOOS == "windows" {
@ -78,46 +91,27 @@ func runInit(cmd *command) error {
} else {
ndkName += "bin"
}
f, err := os.OpenFile(filepath.Join(tmpdir, ndkName), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0755)
if err != nil {
return err
}
url := "http://dl.google.com/android/ndk/" + ndkName
if buildV {
fmt.Fprintf(os.Stderr, "fetching %s\n", url)
}
// TODO(crawshaw): The arm compiler toolchain compresses to 33 MB, less than a tenth of the NDK. Provide an alternative binary download.
resp, err := http.Get(url)
if err != nil {
if err := fetch(filepath.Join(tmpdir, ndkName), url); err != nil {
return err
}
_, err = io.Copy(f, resp.Body)
err2 := resp.Body.Close()
err3 := f.Close()
if err != nil {
return err
}
if err2 != nil {
return err2
}
if err3 != nil {
return err3
}
inflate := exec.Command(filepath.Join(tmpdir, ndkName))
inflate.Dir = tmpdir
out, err := inflate.CombinedOutput()
if err != nil {
if buildV {
os.Stderr.Write(out)
if buildX {
printcmd("%s", inflate.Args[0])
}
if !buildN {
out, err := inflate.CombinedOutput()
if err != nil {
if buildV {
os.Stderr.Write(out)
}
return err
}
return err
}
srcSysroot := filepath.Join(tmpdir, "android-ndk-r10d", "platforms", "android-15", "arch-arm", "usr")
dstSysroot := filepath.Join(dst, "sysroot", "usr")
if err := move(dstSysroot, srcSysroot, "include", "lib"); err != nil {
return err
}
@ -128,11 +122,11 @@ func runInit(cmd *command) error {
}
linkpath := filepath.Join(dst, "arm-linux-androideabi", "bin")
if err := os.MkdirAll(linkpath, 0755); err != nil {
if err := mkdir(linkpath); err != nil {
return err
}
for _, name := range []string{"ld", "ld.gold", "as", "gcc", "g++"} {
if err := os.Symlink(filepath.Join(dst, "bin", "arm-linux-androideabi-"+name), filepath.Join(linkpath, name)); err != nil {
if err := symlink(filepath.Join(dst, "bin", "arm-linux-androideabi-"+name), filepath.Join(linkpath, name)); err != nil {
return err
}
}
@ -161,22 +155,55 @@ func runInit(cmd *command) error {
make.Stdout = os.Stdout
make.Stderr = os.Stderr
}
if buildX {
printcmd("%s", strings.Join(make.Env, " ")+" "+strings.Join(make.Args, " "))
}
if buildN {
return nil
}
if err := make.Run(); err != nil {
return err
}
return nil
}
func move(dst, src string, names ...string) error {
for _, name := range names {
if err := os.Rename(filepath.Join(src, name), filepath.Join(dst, name)); err != nil {
srcf := filepath.Join(src, name)
dstf := filepath.Join(dst, name)
if buildX {
printcmd("mv %s %s", srcf, dstf)
}
if buildN {
continue
}
if err := os.Rename(srcf, dstf); err != nil {
return err
}
}
return nil
}
func mkdir(dir string) error {
if buildX {
printcmd("mkdir -p %s", dir)
}
if buildN {
return nil
}
return os.MkdirAll(dir, 0755)
}
func symlink(src, dst string) error {
if buildX {
printcmd("ln -s %s %s", src, dst)
}
if buildN {
return nil
}
return os.Symlink(src, dst)
}
func checkGoVersion() error {
if err := exec.Command("which", "go").Run(); err != nil {
return fmt.Errorf(`no Go tool on $PATH`)
@ -188,6 +215,49 @@ func checkGoVersion() error {
return nil
}
func fetch(dst, url string) error {
if buildV {
fmt.Fprintf(os.Stderr, "fetching %s\n", url)
}
if buildX {
printcmd("curl -o%s %s", dst, url)
}
if buildN {
return nil
}
f, err := os.OpenFile(dst, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0755)
if err != nil {
return err
}
// TODO(crawshaw): The arm compiler toolchain compresses to 33 MB, less than a tenth of the NDK. Provide an alternative binary download.
resp, err := http.Get(url)
if err != nil {
return err
}
_, err = io.Copy(f, resp.Body)
err2 := resp.Body.Close()
err3 := f.Close()
if err != nil {
return err
}
if err2 != nil {
return err2
}
return err3
}
func removeAll(path string) error {
if buildX {
printcmd("rm -r -f %q", path)
}
if buildN {
return nil
}
return os.RemoveAll(path)
}
func goEnv(name string) string {
if val := os.Getenv(name); val != "" {
return val