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

cmd/gomobile: add -target flag

Also add a very simple test for gomobile build, some
documentation cleanup, and the -o flag for bind.

Change-Id: I719b92010ba2a5813049827c99502828788611ef
Reviewed-on: https://go-review.googlesource.com/11253
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
David Crawshaw 2015-06-19 12:27:15 -04:00
parent 6490f64aef
commit 10a37afdaf
6 changed files with 141 additions and 60 deletions

View File

@ -35,43 +35,39 @@ import (
var cmdBind = &command{
run: runBind,
Name: "bind",
Usage: "[package]",
Usage: "[-target android|ios] [-o output] [build flags] [package]",
Short: "build a shared library for android APK and iOS app",
Long: `
Bind generates language bindings like gobind (golang.org/x/mobile/cmd/gobind)
for a package and builds a shared library for each platform from the go binding
code.
Bind generates language bindings for the package named by the import
path, and compiles a library for the named target system.
For Android, the bind command produces an AAR (Android ARchive) file that
archives the precompiled Java API stub classes, the compiled shared libraries,
and all asset files in the /assets subdirectory under the package directory.
The output AAR file name is '<package_name>.aar'.
The -target flag takes a target system name, either android (the
default) or ios.
The AAR file is commonly used for binary distribution of an Android library
project and most Android IDEs support AAR import. For example, in Android
Studio (1.2+), an AAR file can be imported using the module import wizard
(File > New > New Module > Import .JAR or .AAR package), and setting it as
a new dependency (File > Project Structure > Dependencies).
For -target android, the bind command produces an AAR (Android ARchive)
file that archives the precompiled Java API stub classes, the compiled
shared libraries, and all asset files in the /assets subdirectory under
the package directory. The output is named '<package_name>.aar' by
default. This AAR file is commonly used for binary distribution of an
Android library project and most Android IDEs support AAR import. For
example, in Android Studio (1.2+), an AAR file can be imported using
the module import wizard (File > New > New Module > Import .JAR or
.AAR package), and setting it as a new dependency
(File > Project Structure > Dependencies). This requires 'javac'
(version 1.7+) and Android SDK (API level 9 or newer) to build the
library for Android. The environment variable ANDROID_HOME must be set
to the path to Android SDK.
This command requires 'javac' (version 1.7+) and Android SDK (API level 9
or newer) to build the library for Android. The environment variable
ANDROID_HOME must be set to the path to Android SDK.
For -target ios, gomobile must be run on an OS X machine with Xcode
installed. Support is not complete.
The -v flag provides verbose output, including the list of packages built.
These build flags are shared by the build command.
For documentation, see 'go help build':
-a
-i
-n
-x
-tags 'tag list'
The build flags -a, -i, -n, -x, and -tags are shared with the build command.
For documentation, see 'go help build'.
`,
}
// TODO: -mobile
// TODO: reuse the -o option to specify the output file name?
func runBind(cmd *command) error {
cwd, err := os.Getwd()
if err != nil {
@ -93,6 +89,15 @@ func runBind(cmd *command) error {
return err
}
switch *buildTarget {
case "android":
// implementation is below
case "ios":
return fmt.Errorf(`-target=ios not yet supported`)
default:
return fmt.Errorf(`unknown -target, %q.`, *buildTarget)
}
if sdkDir := os.Getenv("ANDROID_HOME"); sdkDir == "" {
return fmt.Errorf("this command requires ANDROID_HOME environment variable (path to the Android SDK)")
}
@ -318,8 +323,14 @@ func main() {
// javac and jar commands are needed to build classes.jar.
func buildAAR(androidDir string, pkg *build.Package) (err error) {
var out io.Writer = ioutil.Discard
if *buildO == "" {
*buildO = pkg.Name + ".aar"
}
if !strings.HasSuffix(*buildO, ".apk") {
return fmt.Errorf("output file name %q does not end in '.aar'", *buildO)
}
if !buildN {
f, err := os.Create(pkg.Name + ".aar")
f, err := os.Create(*buildO)
if err != nil {
return err
}

View File

@ -30,37 +30,36 @@ var tmpdir string
var cmdBuild = &command{
run: runBuild,
Name: "build",
Usage: "[-o output] [-i] [build flags] [package]",
Usage: "[-target android|ios] [-o output] [build flags] [package]",
Short: "compile android APK and iOS app",
Long: `
Build compiles and encodes the app named by the import path.
The named package must define a main function.
If an AndroidManifest.xml is defined in the package directory, it is
added to the APK file. Otherwise, a default manifest is generated.
The -target flag takes a target system name, either android (the
default) or ios.
For -target android, if an AndroidManifest.xml is defined in the
package directory, it is added to the APK output. Otherwise, a default
manifest is generated.
For -target ios, gomobile must be run on an OS X machine with Xcode
installed. Support is not complete.
If the package directory contains an assets subdirectory, its contents
are copied into the APK file.
are copied into the output.
The -o flag specifies the output file name. If not specified, the
output file name depends on the package built. The output file must end
in '.apk'.
output file name depends on the package built.
The -v flag provides verbose output, including the list of packages built.
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'
The build flags -a, -i, -n, -x, and -tags are shared with the build command.
For documentation, see 'go help build'.
`,
}
// TODO: -mobile
func runBuild(cmd *command) (err error) {
cwd, err := os.Getwd()
if err != nil {
@ -81,6 +80,15 @@ func runBuild(cmd *command) (err error) {
return err
}
switch *buildTarget {
case "android":
// implementation is below
case "ios":
return fmt.Errorf(`-target=ios not yet supported`)
default:
return fmt.Errorf(`unknown -target, %q.`, *buildTarget)
}
if pkg.Name != "main" {
// Not an app, don't build a final package.
return goAndroidBuild(pkg.ImportPath, "")
@ -108,7 +116,7 @@ func runBuild(cmd *command) (err error) {
}
defer removeAll(tmpdir)
if buildX {
fmt.Fprintln(os.Stderr, "WORK="+tmpdir)
fmt.Fprintln(xout, "WORK="+tmpdir)
}
libName := path.Base(pkg.ImportPath)
@ -312,9 +320,13 @@ var (
buildV bool // -v
buildX bool // -x
buildO *string // -o
buildTarget *string // -target
)
func addBuildFlags(cmd *command) {
buildO = cmd.flag.String("o", "", "")
buildTarget = cmd.flag.String("target", "android", "")
cmd.flag.BoolVar(&buildA, "a", false, "")
cmd.flag.BoolVar(&buildI, "i", false, "")
cmd.flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "")
@ -438,7 +450,6 @@ func pkgImportsAudio(pkg *build.Package) bool {
}
func init() {
buildO = cmdBuild.flag.String("o", "", "output file")
addBuildFlags(cmdBuild)
addBuildFlagsNVX(cmdBuild)

View File

@ -0,0 +1,53 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"os"
"path/filepath"
"testing"
"text/template"
)
func TestBuild(t *testing.T) {
buf := new(bytes.Buffer)
defer func() {
xout = os.Stderr
buildN = false
buildX = false
}()
xout = buf
buildN = true
buildX = true
gopath = filepath.SplitList(os.Getenv("GOPATH"))[0]
if goos == "windows" {
os.Setenv("HOMEDRIVE", "C:")
}
cmdBuild.flag.Parse([]string{"golang.org/x/mobile/example/basic"})
if *buildTarget != "android" {
t.Fatalf("-target=%q, want android", *buildTarget)
}
err := runBuild(cmdBuild)
if err != nil {
t.Log(buf.String())
t.Fatal(err)
}
diff, err := diffOutput(buf.String(), buildTmpl)
if err != nil {
t.Fatalf("computing diff failed: %v", err)
}
if diff != "" {
t.Errorf("unexpected output:\n%s", diff)
}
}
var buildTmpl = template.Must(template.New("output").Parse(`WORK=$WORK
GOMOBILE={{.GOPATH}}/pkg/gomobile
GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CC=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-gcc{{.EXE}} CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-g++{{.EXE}} GOGCCFLAGS="-fPIC -marm -pthread -fmessage-length=0" GOROOT=$GOROOT GOPATH=$HOME GOMOBILEPATH=$GOMOBILE/android-{{.NDK}}/arm/bin go build -tags="" -toolexec=$GOMOBILE/android-{{.NDK}}/arm/bin/toolexec -x -buildmode=c-shared -o $WORK/libbasic.so golang.org/x/mobile/example/basic
rm -r -f "$WORK"
`))

View File

@ -76,7 +76,7 @@ func init() {
func runInit(cmd *command) error {
version, err := goVersion()
if err != nil {
return err
return fmt.Errorf("%v: %s", err, version)
}
gopaths := filepath.SplitList(goEnv("GOPATH"))
@ -426,7 +426,7 @@ func goVersion() ([]byte, error) {
if !bytes.Contains(buildHelp, []byte("-toolexec")) {
return nil, fmt.Errorf("installed Go tool does not support -toolexec")
}
return exec.Command(gobin, "version").Output()
return exec.Command(gobin, "version").CombinedOutput()
}
// checkVersionMatch makes sure that the go command in the path matches

View File

@ -13,20 +13,23 @@ import (
"text/template"
)
var gopath string
func TestInit(t *testing.T) {
buf := new(bytes.Buffer)
gopath := os.Getenv("GOPATH")
gopathorig := os.Getenv("GOPATH")
defer func() {
xout = os.Stderr
buildN = false
buildX = false
os.Setenv("GOPATH", gopath)
os.Setenv("GOPATH", gopathorig)
}()
xout = buf
buildN = true
buildX = true
// Test that first GOPATH element is chosen correctly.
paths := []string{"GOPATH1", "/path2", "/path3"}
gopath = "/GOPATH1"
paths := []string{"/GOPATH1", "/path2", "/path3"}
os.Setenv("GOPATH", strings.Join(paths, string(os.PathListSeparator)))
os.Setenv("GOROOT_BOOTSTRAP", "go1.4")
if goos == "windows" {
@ -55,6 +58,7 @@ func diffOutput(got string, wantTmpl *template.Template) (string, error) {
NDK: ndkVersion,
GOOS: goos,
GOARCH: goarch,
GOPATH: gopath,
NDKARCH: ndkarch,
BuildScript: unixBuildScript,
}
@ -76,6 +80,7 @@ type outputData struct {
NDK string
GOOS string
GOARCH string
GOPATH string
NDKARCH string
EXE string // .extension for executables. (ex. ".exe" for windows)
BuildScript string
@ -86,9 +91,9 @@ const (
windowsBuildScript = `TEMP=$WORK TMP=$WORK HOMEDRIVE=C: HOMEPATH=$HOMEPATH GOROOT_BOOTSTRAP=go1.4 $WORK/go/src/make.bat --no-clean`
)
var initTmpl = template.Must(template.New("output").Parse(`GOMOBILE=GOPATH1/pkg/gomobile
var initTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
mkdir -p $GOMOBILE/android-{{.NDK}}
WORK=GOPATH1/pkg/gomobile/work
WORK=/GOPATH1/pkg/gomobile/work
mkdir -p $WORK/go/pkg
cp -a $GOROOT/lib $WORK/go/lib
cp -a $GOROOT/src $WORK/go/src

View File

@ -13,15 +13,16 @@ import (
var cmdInstall = &command{
run: runInstall,
Name: "install",
Usage: "[package]",
Usage: "[-target android] [build flags] [package]",
Short: "compile android APK and iOS app and install on device",
Long: `
Install compiles and installs the app named by the import path on the
attached mobile device.
This command requires the 'adb' tool on the PATH.
Only -target android is supported. The 'adb' tool must be on the PATH.
See the build command help for common flags and common behavior.
The build flags -a, -i, -n, -x, and -tags are shared with the build command.
For documentation, see 'go help build'.
`,
}