cmd/gomobile: bind allows custom package path/prefix.
New option -javapkg for -target=android, and -prefix for -target=ios. Fixes golang/go#9660. Change-Id: I9143f30672672527876524b38f450629452a3161 Reviewed-on: https://go-review.googlesource.com/14023 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
00865a642f
commit
4a6caa5fd7
@ -49,10 +49,12 @@ the module import wizard (File > New > New Module > Import .JAR or
|
||||
(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.
|
||||
to the path to Android SDK. The generated Java class is in the java
|
||||
package 'go.<package_name>' unless -javapkg flag is specified.
|
||||
|
||||
For -target ios, gomobile must be run on an OS X machine with Xcode
|
||||
installed. Support is not complete.
|
||||
installed. Support is not complete. The generated Objective-C types
|
||||
are prefixed with 'Go' unless the -prefix flag is provided.
|
||||
|
||||
The -v flag provides verbose output, including the list of packages built.
|
||||
|
||||
@ -80,6 +82,13 @@ func runBind(cmd *command) error {
|
||||
return fmt.Errorf(`unknown -target, %q.`, buildTarget)
|
||||
}
|
||||
|
||||
if bindJavaPkg != "" && ctx.GOOS != "android" {
|
||||
return fmt.Errorf("-javapkg is supported only for android target")
|
||||
}
|
||||
if bindPrefix != "" && ctx.GOOS != "darwin" {
|
||||
return fmt.Errorf("-prefix is supported only for ios target")
|
||||
}
|
||||
|
||||
var pkg *build.Package
|
||||
switch len(args) {
|
||||
case 0:
|
||||
@ -104,6 +113,19 @@ func runBind(cmd *command) error {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
bindPrefix string // -prefix
|
||||
bindJavaPkg string // -javapkg
|
||||
)
|
||||
|
||||
func init() {
|
||||
// bind command specific commands.
|
||||
cmdBind.flag.StringVar(&bindJavaPkg, "javapkg", "",
|
||||
"specifies custom Java package path used instead of the default 'go.<go package name>'. Valid only with -target=android.")
|
||||
cmdBind.flag.StringVar(&bindPrefix, "prefix", "",
|
||||
"custom Objective-C name prefix used instead of the default 'Go'. Valid only with -lang=ios.")
|
||||
}
|
||||
|
||||
type binder struct {
|
||||
files []*ast.File
|
||||
fset *token.FileSet
|
||||
@ -112,23 +134,27 @@ type binder struct {
|
||||
|
||||
func (b *binder) GenObjc(outdir string) error {
|
||||
name := strings.Title(b.pkg.Name())
|
||||
mfile := filepath.Join(outdir, "Go"+name+".m")
|
||||
hfile := filepath.Join(outdir, "Go"+name+".h")
|
||||
|
||||
if buildX {
|
||||
printcmd("gobind -lang=objc %s > %s", b.pkg.Path(), mfile)
|
||||
bindOption := "-lang=objc"
|
||||
prefix := "Go"
|
||||
if bindPrefix != "" {
|
||||
prefix = bindPrefix
|
||||
bindOption += " -prefix=" + bindPrefix
|
||||
}
|
||||
|
||||
const objcPrefix = "" // TODO(hyangah): -prefix
|
||||
mfile := filepath.Join(outdir, prefix+name+".m")
|
||||
hfile := filepath.Join(outdir, prefix+name+".h")
|
||||
|
||||
generate := func(w io.Writer) error {
|
||||
return bind.GenObjc(w, b.fset, b.pkg, objcPrefix, false)
|
||||
if buildX {
|
||||
printcmd("gobind %s -outdir=%s %s", bindOption, outdir, b.pkg.Path())
|
||||
}
|
||||
return bind.GenObjc(w, b.fset, b.pkg, prefix, false)
|
||||
}
|
||||
if err := writeFile(mfile, generate); err != nil {
|
||||
return err
|
||||
}
|
||||
generate = func(w io.Writer) error {
|
||||
return bind.GenObjc(w, b.fset, b.pkg, objcPrefix, true)
|
||||
return bind.GenObjc(w, b.fset, b.pkg, prefix, true)
|
||||
}
|
||||
if err := writeFile(hfile, generate); err != nil {
|
||||
return err
|
||||
@ -144,14 +170,16 @@ func (b *binder) GenObjc(outdir string) error {
|
||||
func (b *binder) GenJava(outdir string) error {
|
||||
className := strings.Title(b.pkg.Name())
|
||||
javaFile := filepath.Join(outdir, className+".java")
|
||||
|
||||
if buildX {
|
||||
printcmd("gobind -lang=java %s > %s", b.pkg.Path(), javaFile)
|
||||
bindOption := "-lang=java"
|
||||
if bindJavaPkg != "" {
|
||||
bindOption += " -javapkg=" + bindJavaPkg
|
||||
}
|
||||
|
||||
const javaPkg = "" // TODO(hyangah): -javapkg
|
||||
generate := func(w io.Writer) error {
|
||||
return bind.GenJava(w, b.fset, b.pkg, javaPkg)
|
||||
if buildX {
|
||||
printcmd("gobind %s -outdir=%s %s", bindOption, outdir, b.pkg.Path())
|
||||
}
|
||||
return bind.GenJava(w, b.fset, b.pkg, bindJavaPkg)
|
||||
}
|
||||
if err := writeFile(javaFile, generate); err != nil {
|
||||
return err
|
||||
@ -161,13 +189,13 @@ func (b *binder) GenJava(outdir string) error {
|
||||
|
||||
func (b *binder) GenGo(outdir string) error {
|
||||
pkgName := "go_" + b.pkg.Name()
|
||||
goFile := filepath.Join(outdir, pkgName, pkgName+"main.go")
|
||||
|
||||
if buildX {
|
||||
printcmd("gobind -lang=go %s > %s", b.pkg.Path(), goFile)
|
||||
}
|
||||
outdir = filepath.Join(outdir, pkgName)
|
||||
goFile := filepath.Join(outdir, pkgName+"main.go")
|
||||
|
||||
generate := func(w io.Writer) error {
|
||||
if buildX {
|
||||
printcmd("gobind -lang=go -outdir=%s %s", outdir, b.pkg.Path())
|
||||
}
|
||||
return bind.GenGo(w, b.fset, b.pkg)
|
||||
}
|
||||
if err := writeFile(goFile, generate); err != nil {
|
||||
|
@ -58,8 +58,11 @@ func goAndroidBind(pkg *build.Package) error {
|
||||
}
|
||||
repo := filepath.Clean(filepath.Join(p.Dir, "..")) // golang.org/x/mobile directory.
|
||||
|
||||
// TODO(crawshaw): use a better package path derived from the go package.
|
||||
if err := binder.GenJava(filepath.Join(androidDir, "src/main/java/go/"+binder.pkg.Name())); err != nil {
|
||||
pkgpath := strings.Replace(bindJavaPkg, ".", "/", -1)
|
||||
if bindJavaPkg == "" {
|
||||
pkgpath = "go/" + binder.pkg.Name()
|
||||
}
|
||||
if err := binder.GenJava(filepath.Join(androidDir, "src/main/java/"+pkgpath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"text/template"
|
||||
)
|
||||
@ -15,52 +16,100 @@ import (
|
||||
// TODO(crawshaw): TestBindIOS
|
||||
|
||||
func TestBindAndroid(t *testing.T) {
|
||||
if os.Getenv("ANDROID_HOME") == "" {
|
||||
androidHome := os.Getenv("ANDROID_HOME")
|
||||
if androidHome == "" {
|
||||
t.Skip("ANDROID_HOME not found, skipping bind")
|
||||
}
|
||||
platform, err := androidAPIPath()
|
||||
if err != nil {
|
||||
t.Skip("No android API platform found in $ANDROID_HOME, skipping bind")
|
||||
}
|
||||
platform = strings.Replace(platform, androidHome, "$ANDROID_HOME", -1)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
defer func() {
|
||||
xout = os.Stderr
|
||||
buildN = false
|
||||
buildX = false
|
||||
buildO = ""
|
||||
buildTarget = ""
|
||||
}()
|
||||
xout = buf
|
||||
buildN = true
|
||||
buildX = true
|
||||
buildO = "asset.aar"
|
||||
buildTarget = "android"
|
||||
gopath = filepath.SplitList(os.Getenv("GOPATH"))[0]
|
||||
if goos == "windows" {
|
||||
os.Setenv("HOMEDRIVE", "C:")
|
||||
}
|
||||
cmdBind.flag.Parse([]string{"golang.org/x/mobile/asset"})
|
||||
err := runBind(cmdBind)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diff, err := diffOutput(buf.String(), bindAndroidTmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("computing diff failed: %v", err)
|
||||
tests := []struct {
|
||||
javaPkg string
|
||||
wantGobind string
|
||||
wantPkgDir string
|
||||
}{
|
||||
{
|
||||
wantGobind: "gobind -lang=java",
|
||||
wantPkgDir: "go/asset",
|
||||
},
|
||||
{
|
||||
javaPkg: "com.example.foo",
|
||||
wantGobind: "gobind -lang=java -javapkg=com.example.foo",
|
||||
wantPkgDir: "com/example/foo",
|
||||
},
|
||||
}
|
||||
if diff != "" {
|
||||
t.Errorf("unexpected output:\n%s", diff)
|
||||
for _, tc := range tests {
|
||||
bindJavaPkg = tc.javaPkg
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
xout = buf
|
||||
gopath = filepath.SplitList(os.Getenv("GOPATH"))[0]
|
||||
if goos == "windows" {
|
||||
os.Setenv("HOMEDRIVE", "C:")
|
||||
}
|
||||
cmdBind.flag.Parse([]string{"golang.org/x/mobile/asset"})
|
||||
err := runBind(cmdBind)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := filepath.ToSlash(buf.String())
|
||||
|
||||
data := struct {
|
||||
outputData
|
||||
AndroidPlatform string
|
||||
GobindJavaCmd string
|
||||
JavaPkgDir string
|
||||
}{
|
||||
outputData: defaultOutputData(),
|
||||
AndroidPlatform: platform,
|
||||
GobindJavaCmd: tc.wantGobind,
|
||||
JavaPkgDir: tc.wantPkgDir,
|
||||
}
|
||||
|
||||
wantBuf := new(bytes.Buffer)
|
||||
if err := bindAndroidTmpl.Execute(wantBuf, data); err != nil {
|
||||
t.Errorf("%+v: computing diff failed: %v", tc, err)
|
||||
continue
|
||||
}
|
||||
|
||||
diff, err := diff(got, wantBuf.String())
|
||||
if err != nil {
|
||||
t.Errorf("%+v: computing diff failed: %v", tc, err)
|
||||
continue
|
||||
}
|
||||
if diff != "" {
|
||||
t.Errorf("%+v: unexpected output:\n%s", tc, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
|
||||
WORK=$WORK
|
||||
gobind -lang=go golang.org/x/mobile/asset > $WORK/go_asset/go_assetmain.go
|
||||
mkdir -p $WORK/go_asset
|
||||
gobind -lang=go -outdir=$WORK/go_asset golang.org/x/mobile/asset
|
||||
mkdir -p $WORK/androidlib
|
||||
GOOS=android GOARCH=arm GOARM=7 CC=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-gcc{{.EXE}} CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-g++{{.EXE}} CGO_ENABLED=1 go build -p={{.NumCPU}} -pkgdir=$GOMOBILE/pkg_android_arm -tags="" -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so $WORK/androidlib/main.go
|
||||
gobind -lang=java golang.org/x/mobile/asset > $WORK/android/src/main/java/go/asset/Asset.java
|
||||
mkdir -p $WORK/android/src/main/java/go/asset
|
||||
mkdir -p $WORK/android/src/main/java/{{.JavaPkgDir}}
|
||||
{{.GobindJavaCmd}} -outdir=$WORK/android/src/main/java/{{.JavaPkgDir}} golang.org/x/mobile/asset
|
||||
mkdir -p $WORK/android/src/main/java/go
|
||||
rm $WORK/android/src/main/java/go/Seq.java
|
||||
ln -s $GOPATH/src/golang.org/x/mobile/bind/java/Seq.java $WORK/android/src/main/java/go/Seq.java
|
||||
PWD=$WORK/android/src/main/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath $ANDROID_HOME/platforms/android-22/android.jar *.java
|
||||
PWD=$WORK/android/src/main/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java
|
||||
jar c -C $WORK/javac-output .
|
||||
`))
|
||||
|
@ -64,20 +64,7 @@ func diffOutput(got string, wantTmpl *template.Template) (string, error) {
|
||||
got = filepath.ToSlash(got)
|
||||
|
||||
wantBuf := new(bytes.Buffer)
|
||||
data := outputData{
|
||||
NDK: ndkVersion,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
GOPATH: gopath,
|
||||
NDKARCH: ndkarch,
|
||||
Xproj: projPbxproj,
|
||||
Xcontents: contentsJSON,
|
||||
Xinfo: infoplistTmplData{BundleID: "org.golang.todo.basic", Name: "Basic"},
|
||||
NumCPU: strconv.Itoa(runtime.NumCPU()),
|
||||
}
|
||||
if goos == "windows" {
|
||||
data.EXE = ".exe"
|
||||
}
|
||||
data := defaultOutputData()
|
||||
if err := wantTmpl.Execute(wantBuf, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -101,6 +88,24 @@ type outputData struct {
|
||||
NumCPU string
|
||||
}
|
||||
|
||||
func defaultOutputData() outputData {
|
||||
data := outputData{
|
||||
NDK: ndkVersion,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
GOPATH: gopath,
|
||||
NDKARCH: ndkarch,
|
||||
Xproj: projPbxproj,
|
||||
Xcontents: contentsJSON,
|
||||
Xinfo: infoplistTmplData{BundleID: "org.golang.todo.basic", Name: "Basic"},
|
||||
NumCPU: strconv.Itoa(runtime.NumCPU()),
|
||||
}
|
||||
if goos == "windows" {
|
||||
data.EXE = ".exe"
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
var initTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile
|
||||
mkdir -p $GOMOBILE/android-{{.NDK}}
|
||||
WORK={{.GOPATH}}/pkg/gomobile/work
|
||||
|
Loading…
x
Reference in New Issue
Block a user