From 4600df55ca9f03689ade154095e4daf53a00e223 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 7 Mar 2018 22:51:37 +0100 Subject: [PATCH] bind, cmd: generate complete standalone bindings from gobind The gobind and gomobile bind tools have historically overlapped: gobind outputs generated bindings, and gomobile bind will generate bindings before building them. However, the gobind bindings were never used for building and thus allowed to not be complete. To simplify version control, debugging, instrumentation and build system flexibility, this CL upgrades the gobind tool to be the canonical binding generator and change gomobile bind to use gobind instead of its own generator code. This greatly simplifies gomobile bind, but also paves the way to skip gomobile bind entirely. For example: $ gobind -outdir=$GOPATH golang.org/x/mobile/example/bind/hello $ GOOS=android GOARCH=arm64 CC=/bin/clang go build -buildmode=c-shared -o libgobind.so gobind $ ls libgobind.* libgobind.h libgobind.so The same applies to iOS, although the go build command line is more involved. By skipping gomobile it is possible to freely customize the Android or iOS SDK level or any other flags not supported by gomobile bind. By checking in the generated source code, the cost of supporting gomobile in a custom build system is also decreased. Change-Id: I59c14a77d625ac1377c23b3213672e0d83a48c85 Reviewed-on: https://go-review.googlesource.com/99316 Reviewed-by: Hyang-Ah Hana Kim --- bind/genclasses.go | 2 +- bind/gengo.go | 4 +- bind/genobjcw.go | 3 +- bind/java/context_android.c | 2 +- bind/java/seq_android.c.support | 4 + bind/java/seq_android.go.support | 8 +- bind/java/{seq.h => seq_android.h} | 6 +- bind/objc/seq_darwin.go.support | 4 +- bind/objc/{seq.h => seq_darwin.h} | 6 +- bind/seq.go.support | 11 +- bind/testdata/basictypes.go.golden | 4 +- bind/testdata/classes.go.golden | 6 +- bind/testdata/doc.go.golden | 4 +- bind/testdata/ignore.go.golden | 4 +- bind/testdata/interfaces.go.golden | 4 +- bind/testdata/issue10788.go.golden | 4 +- bind/testdata/issue12328.go.golden | 4 +- bind/testdata/issue12403.go.golden | 4 +- bind/testdata/java.go.golden | 6 +- bind/testdata/keywords.go.golden | 4 +- bind/testdata/objc.go.golden | 7 +- bind/testdata/objcw.go.golden | 7 +- bind/testdata/structs.go.golden | 4 +- bind/testdata/try.go.golden | 4 +- bind/testdata/universe.golden | 4 +- bind/testdata/vars.go.golden | 4 +- cmd/gobind/gen.go | 214 ++++++++---- cmd/gobind/main.go | 125 ++++--- cmd/gomobile/bind.go | 540 ----------------------------- cmd/gomobile/bind_androidapp.go | 157 ++------- cmd/gomobile/bind_iosapp.go | 101 ++---- cmd/gomobile/bind_test.go | 49 +-- 32 files changed, 352 insertions(+), 958 deletions(-) rename bind/java/{seq.h => seq_android.h} (96%) rename bind/objc/{seq.h => seq_darwin.h} (95%) diff --git a/bind/genclasses.go b/bind/genclasses.go index 234bb4b..a606f10 100644 --- a/bind/genclasses.go +++ b/bind/genclasses.go @@ -930,7 +930,7 @@ extern void init_proxies(); classesGoHeader = `// File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include // for free() diff --git a/bind/gengo.go b/bind/gengo.go index 1d0ff9f..4b6a22e 100644 --- a/bind/gengo.go +++ b/bind/gengo.go @@ -32,11 +32,11 @@ type goGen struct { } const ( - goPreamble = `// Package gomobile_bind is an autogenerated binder stub for package %[1]s. + goPreamble = `// Package main is an autogenerated binder stub for package %[1]s. // gobind -lang=go %[2]s // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/genobjcw.go b/bind/genobjcw.go index 6b4b0b0..94152c6 100644 --- a/bind/genobjcw.go +++ b/bind/genobjcw.go @@ -315,8 +315,7 @@ func (g *ObjcWrapper) genCFuncDecl(prefix, name string, f *objc.Func) { func (g *ObjcWrapper) GenGo() { g.Printf("// File is generated by gobind. Do not edit.\n\n") - g.Printf("package gomobile_bind\n\n") - g.Printf("// #cgo CFLAGS: -fobjc-arc -fmodules -fblocks\n") + g.Printf("package main\n\n") g.Printf("// #include \"interfaces.h\"\n") g.Printf("import \"C\"\n\n") g.Printf("import \"ObjC\"\n") diff --git a/bind/java/context_android.c b/bind/java/context_android.c index 1e3a59c..66ce569 100644 --- a/bind/java/context_android.c +++ b/bind/java/context_android.c @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. #include -#include "seq.h" +#include "seq_android.h" #include "_cgo_export.h" JNIEXPORT void JNICALL diff --git a/bind/java/seq_android.c.support b/bind/java/seq_android.c.support index 7c0f483..b3aafad 100644 --- a/bind/java/seq_android.c.support +++ b/bind/java/seq_android.c.support @@ -19,6 +19,10 @@ #define NULL_REFNUM 41 +// initClasses are only exported from Go if reverse bindings are used. +// If they're not, weakly define a no-op function. +__attribute__((weak)) void initClasses(void) { } + static JavaVM *jvm; // jnienvs holds the per-thread JNIEnv* for Go threads where we called AttachCurrentThread. // A pthread key destructor is supplied to call DetachCurrentThread on exit. This trick is diff --git a/bind/java/seq_android.go.support b/bind/java/seq_android.go.support index f586e86..a832292 100644 --- a/bind/java/seq_android.go.support +++ b/bind/java/seq_android.go.support @@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gomobile_bind +package main // Go support functions for bindings. This file is copied into the -// generated gomobile_bind package and compiled along with the -// generated binding files. +// generated main package and compiled along with the generated binding +// files. //#cgo CFLAGS: -Werror //#cgo LDFLAGS: -llog //#include //#include //#include -//#include "seq.h" +//#include "seq_android.h" import "C" import ( "unsafe" diff --git a/bind/java/seq.h b/bind/java/seq_android.h similarity index 96% rename from bind/java/seq.h rename to bind/java/seq_android.h index 84c1dbb..26e9025 100644 --- a/bind/java/seq.h +++ b/bind/java/seq_android.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#ifndef __GO_SEQ_HDR__ -#define __GO_SEQ_HDR__ +#ifndef __GO_SEQ_ANDROID_HDR__ +#define __GO_SEQ_ANDROID_HDR__ #include #include @@ -64,4 +64,4 @@ extern jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, con extern jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig); extern int go_seq_isinstanceof(jint refnum, jclass clazz); -#endif // __GO_SEQ_HDR__ +#endif // __GO_SEQ_ANDROID_HDR__ diff --git a/bind/objc/seq_darwin.go.support b/bind/objc/seq_darwin.go.support index 38d6406..0b4e5de 100644 --- a/bind/objc/seq_darwin.go.support +++ b/bind/objc/seq_darwin.go.support @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gomobile_bind +package main // Go support functions for Objective-C. Note that this // file is copied into and compiled with the generated // bindings. /* -#cgo CFLAGS: -x objective-c -fobjc-arc -Werror +#cgo CFLAGS: -x objective-c -fobjc-arc -fmodules -fblocks -Werror #cgo LDFLAGS: -framework Foundation #include diff --git a/bind/objc/seq.h b/bind/objc/seq_darwin.h similarity index 95% rename from bind/objc/seq.h rename to bind/objc/seq_darwin.h index 57e3665..1aeec4a 100644 --- a/bind/objc/seq.h +++ b/bind/objc/seq_darwin.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#ifndef __GO_SEQ_HDR__ -#define __GO_SEQ_HDR__ +#ifndef __GO_SEQ_DARWIN_HDR__ +#define __GO_SEQ_DARWIN_HDR__ #include #include "ref.h" @@ -60,4 +60,4 @@ extern nstring go_seq_from_objc_string(NSString *s); extern NSData *go_seq_to_objc_bytearray(nbyteslice, int copy); extern NSString *go_seq_to_objc_string(nstring str); -#endif // __GO_SEQ_HDR__ +#endif // __GO_SEQ_DARWIN_HDR__ diff --git a/bind/seq.go.support b/bind/seq.go.support index 1167b6c..392ec09 100644 --- a/bind/seq.go.support +++ b/bind/seq.go.support @@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package gomobile_bind +package main // Go support functions for generated Go bindings. This file is -// copied into the generated package, gomobile_bind, and compiled -// along with the bindings. +// copied into the generated main package, and compiled along +// with the bindings. +// #cgo android CFLAGS: -D__GOBIND_ANDROID__ +// #cgo darwin CFLAGS: -D__GOBIND_DARWIN__ // #include // #include "seq.h" import "C" @@ -18,6 +20,7 @@ import ( "os/signal" "syscall" + _ "golang.org/x/mobile/bind/java" _seq "golang.org/x/mobile/bind/seq" ) @@ -45,3 +48,5 @@ func init() { func IncGoRef(refnum C.int32_t) { _seq.Inc(int32(refnum)) } + +func main() {} diff --git a/bind/testdata/basictypes.go.golden b/bind/testdata/basictypes.go.golden index 128aade..b96b9f4 100644 --- a/bind/testdata/basictypes.go.golden +++ b/bind/testdata/basictypes.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package basictypes. +// Package main is an autogenerated binder stub for package basictypes. // gobind -lang=go basictypes // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/classes.go.golden b/bind/testdata/classes.go.golden index 9227ab6..c90663d 100644 --- a/bind/testdata/classes.go.golden +++ b/bind/testdata/classes.go.golden @@ -544,7 +544,7 @@ type Java_io_Console interface { // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include // for free() @@ -1865,11 +1865,11 @@ func (p *proxy_class_java_io_Console) ToString() string { return _res } -// Package gomobile_bind is an autogenerated binder stub for package java. +// Package main is an autogenerated binder stub for package java. // gobind -lang=go classes // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/doc.go.golden b/bind/testdata/doc.go.golden index 41da999..043122d 100644 --- a/bind/testdata/doc.go.golden +++ b/bind/testdata/doc.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package doc. +// Package main is an autogenerated binder stub for package doc. // gobind -lang=go doc // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/ignore.go.golden b/bind/testdata/ignore.go.golden index 4299931..ed73288 100644 --- a/bind/testdata/ignore.go.golden +++ b/bind/testdata/ignore.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package ignore. +// Package main is an autogenerated binder stub for package ignore. // gobind -lang=go ignore // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/interfaces.go.golden b/bind/testdata/interfaces.go.golden index a4700d6..60cf2e4 100644 --- a/bind/testdata/interfaces.go.golden +++ b/bind/testdata/interfaces.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package interfaces. +// Package main is an autogenerated binder stub for package interfaces. // gobind -lang=go interfaces // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/issue10788.go.golden b/bind/testdata/issue10788.go.golden index f8d1145..165bb70 100644 --- a/bind/testdata/issue10788.go.golden +++ b/bind/testdata/issue10788.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package issue10788. +// Package main is an autogenerated binder stub for package issue10788. // gobind -lang=go issue10788 // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/issue12328.go.golden b/bind/testdata/issue12328.go.golden index c4804f9..38f4ccd 100644 --- a/bind/testdata/issue12328.go.golden +++ b/bind/testdata/issue12328.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package issue12328. +// Package main is an autogenerated binder stub for package issue12328. // gobind -lang=go issue12328 // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/issue12403.go.golden b/bind/testdata/issue12403.go.golden index eb83831..2eecfa9 100644 --- a/bind/testdata/issue12403.go.golden +++ b/bind/testdata/issue12403.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package issue12403. +// Package main is an autogenerated binder stub for package issue12403. // gobind -lang=go issue12403 // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/java.go.golden b/bind/testdata/java.go.golden index 48eefad..1bcd9a8 100644 --- a/bind/testdata/java.go.golden +++ b/bind/testdata/java.go.golden @@ -220,7 +220,7 @@ type Java_lang_Character_Subset interface { // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include // for free() @@ -495,11 +495,11 @@ func (p *proxy_class_java_lang_Character_Subset) ToString() string { return _res } -// Package gomobile_bind is an autogenerated binder stub for package java. +// Package main is an autogenerated binder stub for package java. // gobind -lang=go java // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/keywords.go.golden b/bind/testdata/keywords.go.golden index d9fba9b..4ad33bc 100644 --- a/bind/testdata/keywords.go.golden +++ b/bind/testdata/keywords.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package keywords. +// Package main is an autogenerated binder stub for package keywords. // gobind -lang=go keywords // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/objc.go.golden b/bind/testdata/objc.go.golden index c03ecf1..114aefc 100644 --- a/bind/testdata/objc.go.golden +++ b/bind/testdata/objc.go.golden @@ -16,9 +16,8 @@ type Foundation_NSObjectC interface { // File is generated by gobind. Do not edit. -package gomobile_bind +package main -// #cgo CFLAGS: -fobjc-arc -fmodules -fblocks // #include "interfaces.h" import "C" @@ -53,11 +52,11 @@ type proxy_class_NSObjectC _seq.Ref func (p *proxy_class_NSObjectC) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() } -// Package gomobile_bind is an autogenerated binder stub for package objc. +// Package main is an autogenerated binder stub for package objc. // gobind -lang=go objc // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/objcw.go.golden b/bind/testdata/objcw.go.golden index f0f237b..dc5ba55 100644 --- a/bind/testdata/objcw.go.golden +++ b/bind/testdata/objcw.go.golden @@ -55,9 +55,8 @@ type Objc_GoUIResponder interface { // File is generated by gobind. Do not edit. -package gomobile_bind +package main -// #cgo CFLAGS: -fobjc-arc -fmodules -fblocks // #include "interfaces.h" import "C" @@ -296,11 +295,11 @@ func (p *super_GoUIResponder) Description() (string) { return _res } -// Package gomobile_bind is an autogenerated binder stub for package objc. +// Package main is an autogenerated binder stub for package objc. // gobind -lang=go objcw // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/structs.go.golden b/bind/testdata/structs.go.golden index 817ec5d..aab0a35 100644 --- a/bind/testdata/structs.go.golden +++ b/bind/testdata/structs.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package structs. +// Package main is an autogenerated binder stub for package structs. // gobind -lang=go structs // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/try.go.golden b/bind/testdata/try.go.golden index 2dfe8e4..f9ef1e4 100644 --- a/bind/testdata/try.go.golden +++ b/bind/testdata/try.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package try. +// Package main is an autogenerated binder stub for package try. // gobind -lang=go try // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/universe.golden b/bind/testdata/universe.golden index 5c172ff..12c0507 100644 --- a/bind/testdata/universe.golden +++ b/bind/testdata/universe.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package universe. +// Package main is an autogenerated binder stub for package universe. // gobind -lang=go // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/bind/testdata/vars.go.golden b/bind/testdata/vars.go.golden index 6158e20..7098222 100644 --- a/bind/testdata/vars.go.golden +++ b/bind/testdata/vars.go.golden @@ -1,8 +1,8 @@ -// Package gomobile_bind is an autogenerated binder stub for package vars. +// Package main is an autogenerated binder stub for package vars. // gobind -lang=go vars // // File is generated by gobind. Do not edit. -package gomobile_bind +package main /* #include diff --git a/cmd/gobind/gen.go b/cmd/gobind/gen.go index ddfe76a..cb2d3cc 100644 --- a/cmd/gobind/gen.go +++ b/cmd/gobind/gen.go @@ -13,7 +13,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path/filepath" "strings" "unicode" @@ -22,16 +21,23 @@ import ( "golang.org/x/mobile/bind" "golang.org/x/mobile/internal/importers" "golang.org/x/mobile/internal/importers/java" + "golang.org/x/mobile/internal/importers/objc" ) -func genPkg(p *types.Package, allPkg []*types.Package, classes []*java.Class) { - fname := defaultFileName(*lang, p) +func genPkg(lang string, p *types.Package, allPkg []*types.Package, classes []*java.Class, otypes []*objc.Named) { + fname := defaultFileName(lang, p) conf := &bind.GeneratorConfig{ Fset: fset, Pkg: p, AllPkg: allPkg, } - switch *lang { + var pname string + if p != nil { + pname = p.Name() + } else { + pname = "universe" + } + switch lang { case "java": var buf bytes.Buffer g := &bind.JavaGen{ @@ -48,30 +54,24 @@ func genPkg(p *types.Package, allPkg []*types.Package, classes []*java.Class) { pkgname := bind.JavaPkgName(*javaPkg, p) pkgDir := strings.Replace(pkgname, ".", "/", -1) buf.Reset() - w, closer := writer(filepath.Join(pkgDir, fname)) + w, closer := writer(filepath.Join("java", pkgDir, fname)) processErr(g.GenJava()) io.Copy(w, &buf) closer() for i, name := range g.ClassNames() { buf.Reset() - w, closer := writer(filepath.Join(pkgDir, name+".java")) + w, closer := writer(filepath.Join("java", pkgDir, name+".java")) processErr(g.GenClass(i)) io.Copy(w, &buf) closer() } buf.Reset() - pn := "universe" - if p != nil { - pn = p.Name() - } - cname := "java_" + pn + ".c" - w, closer = writer(cname) + w, closer = writer(filepath.Join("src", "gobind", pname+"_android.c")) processErr(g.GenC()) io.Copy(w, &buf) closer() buf.Reset() - hname := pn + ".h" - w, closer = writer(hname) + w, closer = writer(filepath.Join("src", "gobind", pname+"_android.h")) processErr(g.GenH()) io.Copy(w, &buf) closer() @@ -90,41 +90,44 @@ func genPkg(p *types.Package, allPkg []*types.Package, classes []*java.Class) { errorf("failed to open Java support file: %v", err) } defer in.Close() - w, closer := writer(filepath.Join("go", javaFile)) + w, closer := writer(filepath.Join("java", "go", javaFile)) defer closer() if _, err := io.Copy(w, in); err != nil { errorf("failed to copy Java support file: %v", err) return } } + // Copy support files + javaPkg, err := build.Default.Import("golang.org/x/mobile/bind/java", "", build.FindOnly) + if err != nil { + errorf("unable to import bind/java: %v", err) + return + } + copyFile(filepath.Join("src", "gobind", "seq_android.c"), filepath.Join(javaPkg.Dir, "seq_android.c.support")) + copyFile(filepath.Join("src", "gobind", "seq_android.go"), filepath.Join(javaPkg.Dir, "seq_android.go.support")) + copyFile(filepath.Join("src", "gobind", "seq_android.h"), filepath.Join(javaPkg.Dir, "seq_android.h")) } - // Copy support files - javaPkg, err := build.Default.Import("golang.org/x/mobile/bind/java", "", build.FindOnly) - if err != nil { - errorf("unable to import bind/java: %v", err) - return - } - copyFile("seq_android.c", filepath.Join(javaPkg.Dir, "seq_android.c.support")) - copyFile("seq_android.go", filepath.Join(javaPkg.Dir, "seq_android.go.support")) - copyFile("seq.h", filepath.Join(javaPkg.Dir, "seq.h")) case "go": - w, closer := writer(fname) + w, closer := writer(filepath.Join("src", "gobind", fname)) conf.Writer = w processErr(bind.GenGo(conf)) closer() + var buf bytes.Buffer + w, closer = writer(filepath.Join("src", "gobind", pname+".h")) + genPkgH(w, pname) + io.Copy(w, &buf) + closer() + w, closer = writer(filepath.Join("src", "gobind", "seq.h")) + genPkgH(w, "seq") + io.Copy(w, &buf) + closer() bindPkg, err := build.Default.Import("golang.org/x/mobile/bind", "", build.FindOnly) if err != nil { errorf("unable to import bind: %v", err) return } - copyFile("seq.go", filepath.Join(bindPkg.Dir, "seq.go.support")) + copyFile(filepath.Join("src", "gobind", "seq.go"), filepath.Join(bindPkg.Dir, "seq.go.support")) case "objc": - var gohname string - if p != nil { - gohname = p.Name() + ".h" - } else { - gohname = "universe.h" - } var buf bytes.Buffer g := &bind.ObjcGen{ Generator: &bind.Generator{ @@ -135,53 +138,65 @@ func genPkg(p *types.Package, allPkg []*types.Package, classes []*java.Class) { }, Prefix: *prefix, } - g.Init(nil) - - w, closer := writer(gohname) + g.Init(otypes) + w, closer := writer(filepath.Join("src", "gobind", pname+"_darwin.h")) processErr(g.GenGoH()) io.Copy(w, &buf) closer() hname := strings.Title(fname[:len(fname)-2]) + ".objc.h" - w, closer = writer(hname) + w, closer = writer(filepath.Join("src", "gobind", hname)) processErr(g.GenH()) io.Copy(w, &buf) closer() - w, closer = writer(fname) + mname := strings.Title(fname[:len(fname)-2]) + "_darwin.m" + w, closer = writer(filepath.Join("src", "gobind", mname)) conf.Writer = w processErr(g.GenM()) io.Copy(w, &buf) closer() - // Copy support files - objcPkg, err := build.Default.Import("golang.org/x/mobile/bind/objc", "", build.FindOnly) - if err != nil { - errorf("unable to import bind/objc: %v", err) - return + if p == nil { + // Copy support files + objcPkg, err := build.Default.Import("golang.org/x/mobile/bind/objc", "", build.FindOnly) + if err != nil { + errorf("unable to import bind/objc: %v", err) + return + } + copyFile(filepath.Join("src", "gobind", "seq_darwin.m"), filepath.Join(objcPkg.Dir, "seq_darwin.m.support")) + copyFile(filepath.Join("src", "gobind", "seq_darwin.go"), filepath.Join(objcPkg.Dir, "seq_darwin.go.support")) + copyFile(filepath.Join("src", "gobind", "ref.h"), filepath.Join(objcPkg.Dir, "ref.h")) + copyFile(filepath.Join("src", "gobind", "seq_darwin.h"), filepath.Join(objcPkg.Dir, "seq_darwin.h")) } - copyFile("seq_darwin.m", filepath.Join(objcPkg.Dir, "seq_darwin.m.support")) - copyFile("seq_darwin.go", filepath.Join(objcPkg.Dir, "seq_darwin.go.support")) - copyFile("ref.h", filepath.Join(objcPkg.Dir, "ref.h")) - copyFile("seq.h", filepath.Join(objcPkg.Dir, "seq.h")) default: - errorf("unknown target language: %q", *lang) + errorf("unknown target language: %q", lang) } } -func genJavaPackages(ctx *build.Context, dir string, classes []*java.Class, embedders []importers.Struct) error { +func genPkgH(w io.Writer, pname string) { + fmt.Fprintf(w, `// Code generated by gobind. DO NOT EDIT. + +#ifdef __GOBIND_ANDROID__ +#include "%[1]s_android.h" +#endif +#ifdef __GOBIND_DARWIN__ +#include "%[1]s_darwin.h" +#endif`, pname) +} + +func genObjcPackages(dir string, types []*objc.Named, embedders []importers.Struct) error { var buf bytes.Buffer - cg := &bind.ClassGen{ - JavaPkg: *javaPkg, + cg := &bind.ObjcWrapper{ Printer: &bind.Printer{ IndentEach: []byte("\t"), Buf: &buf, }, } - cg.Init(classes, embedders) - pkgBase := filepath.Join(dir, "src", "Java") - if err := os.MkdirAll(pkgBase, 0700); err != nil { - return err + var genNames []string + for _, emb := range embedders { + genNames = append(genNames, emb.Name) } - for i, jpkg := range cg.Packages() { - pkgDir := filepath.Join(pkgBase, jpkg) + cg.Init(types, genNames) + for i, opkg := range cg.Packages() { + pkgDir := filepath.Join(dir, "src", "ObjC", opkg) if err := os.MkdirAll(pkgDir, 0700); err != nil { return err } @@ -194,21 +209,84 @@ func genJavaPackages(ctx *build.Context, dir string, classes []*java.Class, embe } buf.Reset() cg.GenInterfaces() - clsFile := filepath.Join(pkgBase, "interfaces.go") - if err := ioutil.WriteFile(clsFile, buf.Bytes(), 0600); err != nil { + objcBase := filepath.Join(dir, "src", "ObjC") + if err := os.MkdirAll(objcBase, 0700); err != nil { return err } + if err := ioutil.WriteFile(filepath.Join(objcBase, "interfaces.go"), buf.Bytes(), 0600); err != nil { + return err + } + goBase := filepath.Join(dir, "src", "gobind") + if err := os.MkdirAll(goBase, 0700); err != nil { + return err + } + buf.Reset() + cg.GenGo() + if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces_darwin.go"), buf.Bytes(), 0600); err != nil { + return err + } + buf.Reset() + cg.GenH() + if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces.h"), buf.Bytes(), 0600); err != nil { + return err + } + buf.Reset() + cg.GenM() + if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces_darwin.m"), buf.Bytes(), 0600); err != nil { + return err + } + return nil +} - cmd := exec.Command( - "go", - "install", - "-pkgdir="+filepath.Join(dir, "pkg", ctx.GOOS+"_"+ctx.GOARCH), - "Java/...", - ) - cmd.Env = append(os.Environ(), "GOPATH="+dir) - cmd.Env = append(cmd.Env, "GOROOT="+ctx.GOROOT) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("failed to go install the generated Java wrappers: %v: %s", err, string(out)) +func genJavaPackages(dir string, classes []*java.Class, embedders []importers.Struct) error { + var buf bytes.Buffer + cg := &bind.ClassGen{ + JavaPkg: *javaPkg, + Printer: &bind.Printer{ + IndentEach: []byte("\t"), + Buf: &buf, + }, + } + cg.Init(classes, embedders) + for i, jpkg := range cg.Packages() { + pkgDir := filepath.Join(dir, "src", "Java", jpkg) + if err := os.MkdirAll(pkgDir, 0700); err != nil { + return err + } + pkgFile := filepath.Join(pkgDir, "package.go") + buf.Reset() + cg.GenPackage(i) + if err := ioutil.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil { + return err + } + } + buf.Reset() + cg.GenInterfaces() + javaBase := filepath.Join(dir, "src", "Java") + if err := os.MkdirAll(javaBase, 0700); err != nil { + return err + } + if err := ioutil.WriteFile(filepath.Join(javaBase, "interfaces.go"), buf.Bytes(), 0600); err != nil { + return err + } + goBase := filepath.Join(dir, "src", "gobind") + if err := os.MkdirAll(goBase, 0700); err != nil { + return err + } + buf.Reset() + cg.GenGo() + if err := ioutil.WriteFile(filepath.Join(goBase, "classes_android.go"), buf.Bytes(), 0600); err != nil { + return err + } + buf.Reset() + cg.GenH() + if err := ioutil.WriteFile(filepath.Join(goBase, "classes.h"), buf.Bytes(), 0600); err != nil { + return err + } + buf.Reset() + cg.GenC() + if err := ioutil.WriteFile(filepath.Join(goBase, "classes_android.c"), buf.Bytes(), 0600); err != nil { + return err } return nil } diff --git a/cmd/gobind/main.go b/cmd/gobind/main.go index 7fe5b67..03f8059 100644 --- a/cmd/gobind/main.go +++ b/cmd/gobind/main.go @@ -7,11 +7,8 @@ package main import ( "flag" "fmt" - "go/ast" "go/build" "go/importer" - "go/parser" - "go/token" "go/types" "io/ioutil" "log" @@ -22,15 +19,17 @@ import ( "golang.org/x/mobile/internal/importers" "golang.org/x/mobile/internal/importers/java" + "golang.org/x/mobile/internal/importers/objc" ) var ( - lang = flag.String("lang", "java", "target language for bindings, either java, go, or objc (experimental).") + lang = flag.String("lang", "", "target languages for bindings, either java, go, or objc. If empty, all languages are generated.") outdir = flag.String("outdir", "", "result will be written to the directory instead of stdout.") javaPkg = flag.String("javapkg", "", "custom Java package path prefix. Valid only with -lang=java.") prefix = flag.String("prefix", "", "custom Objective-C name prefix. Valid only with -lang=objc.") bootclasspath = flag.String("bootclasspath", "", "Java bootstrap classpath.") classpath = flag.String("classpath", "", "Java classpath.") + tags = flag.String("tags", "", "build tags.") ) var usage = `The Gobind tool generates Java language bindings for Go. @@ -40,14 +39,22 @@ For usage details, see doc.go.` func main() { flag.Parse() - if *lang != "java" && *javaPkg != "" { - log.Fatalf("Invalid option -javapkg for gobind -lang=%s", *lang) - } else if *lang != "objc" && *prefix != "" { - log.Fatalf("Invalid option -prefix for gobind -lang=%s", *lang) - } + run() + os.Exit(exitStatus) +} +func run() { + var langs []string + if *lang != "" { + langs = strings.Split(*lang, ",") + } else { + langs = []string{"go", "java", "objc"} + } oldCtx := build.Default ctx := &build.Default + if *tags != "" { + ctx.BuildTags = append(ctx.BuildTags, strings.Split(*tags, ",")...) + } var allPkg []*build.Package for _, path := range flag.Args() { pkg, err := ctx.Import(path, ".", build.ImportComment) @@ -56,80 +63,94 @@ func main() { } allPkg = append(allPkg, pkg) } - var classes []*java.Class - refs, err := importers.AnalyzePackages(allPkg, "Java/") + jrefs, err := importers.AnalyzePackages(allPkg, "Java/") if err != nil { log.Fatal(err) } - if len(refs.Refs) > 0 { - imp := &java.Importer{ + orefs, err := importers.AnalyzePackages(allPkg, "ObjC/") + if err != nil { + log.Fatal(err) + } + var classes []*java.Class + if len(jrefs.Refs) > 0 { + jimp := &java.Importer{ Bootclasspath: *bootclasspath, Classpath: *classpath, JavaPkg: *javaPkg, } - classes, err = imp.Import(refs) + classes, err = jimp.Import(jrefs) if err != nil { log.Fatal(err) } - if len(classes) > 0 { - tmpGopath, err := ioutil.TempDir(os.TempDir(), "gobind-") + } + var otypes []*objc.Named + if len(orefs.Refs) > 0 { + otypes, err = objc.Import(orefs) + if err != nil { + log.Fatal(err) + } + } + if len(classes) > 0 || len(otypes) > 0 { + // After generation, reverse bindings needs to be in the GOPATH + // for user packages to build. + srcDir := *outdir + if srcDir == "" { + srcDir, err = ioutil.TempDir(os.TempDir(), "gobind-") if err != nil { log.Fatal(err) } - defer os.RemoveAll(tmpGopath) - if err := genJavaPackages(ctx, tmpGopath, classes, refs.Embedders); err != nil { + defer os.RemoveAll(srcDir) + } else { + srcDir, err = filepath.Abs(srcDir) + if err != nil { log.Fatal(err) } - gopath := ctx.GOPATH - if gopath != "" { - gopath = string(filepath.ListSeparator) + } + if ctx.GOPATH != "" { + ctx.GOPATH = string(filepath.ListSeparator) + ctx.GOPATH + } + ctx.GOPATH = srcDir + ctx.GOPATH + if len(classes) > 0 { + if err := genJavaPackages(srcDir, classes, jrefs.Embedders); err != nil { + log.Fatal(err) + } + } + if len(otypes) > 0 { + if err := genObjcPackages(srcDir, otypes, orefs.Embedders); err != nil { + log.Fatal(err) } - ctx.GOPATH = gopath + tmpGopath } } // Make sure the export data for any imported packages are up to date. - cmd := exec.Command("go", "install") + cmd := exec.Command("go", "install", "-tags", strings.Join(ctx.BuildTags, " ")) cmd.Args = append(cmd.Args, flag.Args()...) cmd.Env = append(os.Environ(), "GOPATH="+ctx.GOPATH) cmd.Env = append(cmd.Env, "GOROOT="+ctx.GOROOT) - if err := cmd.Run(); err != nil { - // Only report I/O errors. Errors from go install is expected for as-yet - // undefined Java wrappers. - if _, ok := err.(*exec.ExitError); !ok { - fmt.Fprintf(os.Stderr, "%s failed: %v", strings.Join(cmd.Args, " "), err) - os.Exit(1) - } + if out, err := cmd.CombinedOutput(); err != nil { + fmt.Fprintf(os.Stderr, "%s", out) + exitStatus = 1 + return } typePkgs := make([]*types.Package, len(allPkg)) - fset := token.NewFileSet() - conf := &types.Config{ - Importer: importer.Default(), - } - conf.Error = func(err error) { - // Ignore errors. They're probably caused by as-yet undefined - // Java wrappers. - } + imp := importer.Default() for i, pkg := range allPkg { - var files []*ast.File - for _, name := range pkg.GoFiles { - f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, name), nil, 0) - if err != nil { - log.Fatalf("Failed to parse Go file %s: %v", name, err) - } - files = append(files, f) + var err error + typePkgs[i], err = imp.Import(pkg.ImportPath) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return } - tpkg, _ := conf.Check(pkg.ImportPath, fset, files, nil) - typePkgs[i] = tpkg } build.Default = oldCtx - for _, pkg := range typePkgs { - genPkg(pkg, typePkgs, classes) + for _, l := range langs { + for _, pkg := range typePkgs { + genPkg(l, pkg, typePkgs, classes, otypes) + } + // Generate the error package and support files + genPkg(l, nil, typePkgs, classes, otypes) } - // Generate the error package and support files - genPkg(nil, typePkgs, classes) - os.Exit(exitStatus) } var exitStatus = 0 diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go index 1c47cda..6024a2e 100644 --- a/cmd/gomobile/bind.go +++ b/cmd/gomobile/bind.go @@ -5,26 +5,14 @@ package main import ( - "bytes" "errors" "fmt" - "go/ast" "go/build" - "go/importer" - "go/parser" - "go/token" - "go/types" "io" "io/ioutil" "os" "path" "path/filepath" - "strings" - - "golang.org/x/mobile/bind" - "golang.org/x/mobile/internal/importers" - "golang.org/x/mobile/internal/importers/java" - "golang.org/x/mobile/internal/importers/objc" ) // ctx, pkg, tmpdir in build.go @@ -167,135 +155,6 @@ func init() { cmdBind.flag.StringVar(&bindBootClasspath, "bootclasspath", "", "The bootstrap classpath for imported Java classes. Valid only with -target=android.") } -type binder struct { - files []*ast.File - fset *token.FileSet - pkgs []*types.Package -} - -func (b *binder) GenGoSupport(outdir string) error { - bindPkg, err := ctx.Import("golang.org/x/mobile/bind", "", build.FindOnly) - if err != nil { - return err - } - return copyFile(filepath.Join(outdir, "seq.go"), filepath.Join(bindPkg.Dir, "seq.go.support")) -} - -func (b *binder) GenObjcSupport(outdir string) error { - objcPkg, err := ctx.Import("golang.org/x/mobile/bind/objc", "", build.FindOnly) - if err != nil { - return err - } - if err := copyFile(filepath.Join(outdir, "seq_darwin.m"), filepath.Join(objcPkg.Dir, "seq_darwin.m.support")); err != nil { - return err - } - if err := copyFile(filepath.Join(outdir, "seq_darwin.go"), filepath.Join(objcPkg.Dir, "seq_darwin.go.support")); err != nil { - return err - } - if err := copyFile(filepath.Join(outdir, "ref.h"), filepath.Join(objcPkg.Dir, "ref.h")); err != nil { - return err - } - return copyFile(filepath.Join(outdir, "seq.h"), filepath.Join(objcPkg.Dir, "seq.h")) -} - -func (b *binder) GenObjc(pkg *types.Package, files []*ast.File, allPkg []*types.Package, outdir string, wrappers []*objc.Named) (string, error) { - if pkg == nil { - bindPrefix = "" - } - pkgName := "" - pkgPath := "" - if pkg != nil { - pkgName = pkg.Name() - pkgPath = pkg.Path() - } else { - pkgName = "universe" - } - bindOption := "-lang=objc" - if bindPrefix != "" { - bindOption += fmt.Sprintf(" -prefix=%q", bindPrefix) - } - - fileBase := bindPrefix + strings.Title(pkgName) - mfile := filepath.Join(outdir, fileBase+".m") - hfile := filepath.Join(outdir, fileBase+".objc.h") - gohfile := filepath.Join(outdir, pkgName+".h") - - var buf bytes.Buffer - g := &bind.ObjcGen{ - Generator: &bind.Generator{ - Printer: &bind.Printer{Buf: &buf, IndentEach: []byte("\t")}, - Fset: b.fset, - AllPkg: allPkg, - Pkg: pkg, - Files: files, - }, - Prefix: bindPrefix, - } - g.Init(wrappers) - - generate := func(w io.Writer) error { - if buildX { - printcmd("gobind %s -outdir=%s %s", bindOption, outdir, pkgPath) - } - if buildN { - return nil - } - buf.Reset() - if err := g.GenM(); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(mfile, generate); err != nil { - return "", err - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - if err := g.GenH(); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(hfile, generate); err != nil { - return "", err - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - if err := g.GenGoH(); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(gohfile, generate); err != nil { - return "", err - } - - return fileBase, nil -} - -func (b *binder) GenJavaSupport(outdir string) error { - javaPkg, err := ctx.Import("golang.org/x/mobile/bind/java", "", build.FindOnly) - if err != nil { - return err - } - if err := copyFile(filepath.Join(outdir, "seq_android.go"), filepath.Join(javaPkg.Dir, "seq_android.go.support")); err != nil { - return err - } - if err := copyFile(filepath.Join(outdir, "seq_android.c"), filepath.Join(javaPkg.Dir, "seq_android.c.support")); err != nil { - return err - } - return copyFile(filepath.Join(outdir, "seq.h"), filepath.Join(javaPkg.Dir, "seq.h")) -} - func bootClasspath() (string, error) { if bindBootClasspath != "" { return bindBootClasspath, nil @@ -307,321 +166,6 @@ func bootClasspath() (string, error) { return filepath.Join(apiPath, "android.jar"), nil } -func GenObjcWrappers(pkgs []*build.Package, srcDir, pkgGen string) ([]*objc.Named, error) { - refs, err := importers.AnalyzePackages(pkgs, "ObjC/") - if err != nil { - return nil, err - } - types, err := objc.Import(refs) - if err != nil { - return nil, err - } - var buf bytes.Buffer - g := &bind.ObjcWrapper{ - Printer: &bind.Printer{ - IndentEach: []byte("\t"), - Buf: &buf, - }, - } - var genNames []string - for _, emb := range refs.Embedders { - genNames = append(genNames, emb.Name) - } - g.Init(types, genNames) - for i, name := range g.Packages() { - pkgDir := filepath.Join(pkgGen, "src", "ObjC", name) - if err := os.MkdirAll(pkgDir, 0700); err != nil { - return nil, err - } - pkgFile := filepath.Join(pkgDir, "package.go") - generate := func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenPackage(i) - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(pkgFile, generate); err != nil { - return nil, fmt.Errorf("failed to create the ObjC wrapper package %s: %v", name, err) - } - } - generate := func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenGo() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(srcDir, "interfaces.go"), generate); err != nil { - return nil, fmt.Errorf("failed to create the ObjC wrapper Go file: %v", err) - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenInterfaces() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(pkgGen, "src", "ObjC", "interfaces.go"), generate); err != nil { - return nil, fmt.Errorf("failed to create the ObjC wrapper Go file: %v", err) - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenH() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(srcDir, "interfaces.h"), generate); err != nil { - return nil, fmt.Errorf("failed to create the ObjC wrapper header file: %v", err) - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenM() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(srcDir, "interfaces.m"), generate); err != nil { - return nil, fmt.Errorf("failed to create the Java classes ObjC file: %v", err) - } - return types, nil -} - -func GenClasses(pkgs []*build.Package, srcDir, jpkgSrc string) ([]*java.Class, error) { - refs, err := importers.AnalyzePackages(pkgs, "Java/") - if err != nil { - return nil, err - } - bClspath, err := bootClasspath() - if err != nil { - return nil, err - } - imp := &java.Importer{ - Bootclasspath: bClspath, - Classpath: bindClasspath, - JavaPkg: bindJavaPkg, - } - classes, err := imp.Import(refs) - if err != nil { - return nil, err - } - var buf bytes.Buffer - g := &bind.ClassGen{ - JavaPkg: bindJavaPkg, - Printer: &bind.Printer{ - IndentEach: []byte("\t"), - Buf: &buf, - }, - } - g.Init(classes, refs.Embedders) - for i, jpkg := range g.Packages() { - pkgDir := filepath.Join(jpkgSrc, "src", "Java", jpkg) - if err := os.MkdirAll(pkgDir, 0700); err != nil { - return nil, err - } - pkgFile := filepath.Join(pkgDir, "package.go") - generate := func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenPackage(i) - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(pkgFile, generate); err != nil { - return nil, fmt.Errorf("failed to create the Java wrapper package %s: %v", jpkg, err) - } - } - generate := func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenGo() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(srcDir, "classes.go"), generate); err != nil { - return nil, fmt.Errorf("failed to create the Java classes Go file: %v", err) - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenH() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(srcDir, "classes.h"), generate); err != nil { - return nil, fmt.Errorf("failed to create the Java classes header file: %v", err) - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenC() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(srcDir, "classes.c"), generate); err != nil { - return nil, fmt.Errorf("failed to create the Java classes C file: %v", err) - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - g.GenInterfaces() - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(filepath.Join(jpkgSrc, "src", "Java", "interfaces.go"), generate); err != nil { - return nil, fmt.Errorf("failed to create the Java classes interfaces file: %v", err) - } - return classes, nil -} - -func (b *binder) GenJava(pkg *types.Package, files []*ast.File, allPkg []*types.Package, classes []*java.Class, outdir, androidDir string) error { - jpkgname := bind.JavaPkgName(bindJavaPkg, pkg) - javadir := filepath.Join(androidDir, strings.Replace(jpkgname, ".", "/", -1)) - var className string - pkgName := "" - pkgPath := "" - if pkg != nil { - className = strings.Title(pkg.Name()) - pkgName = pkg.Name() - pkgPath = pkg.Path() - } else { - pkgName = "universe" - className = "Universe" - } - javaFile := filepath.Join(javadir, className+".java") - cFile := filepath.Join(outdir, "java_"+pkgName+".c") - hFile := filepath.Join(outdir, pkgName+".h") - bindOption := "-lang=java" - if bindJavaPkg != "" { - bindOption += " -javapkg=" + bindJavaPkg - } - - var buf bytes.Buffer - g := &bind.JavaGen{ - JavaPkg: bindJavaPkg, - Generator: &bind.Generator{ - Printer: &bind.Printer{Buf: &buf, IndentEach: []byte(" ")}, - Fset: b.fset, - AllPkg: allPkg, - Pkg: pkg, - Files: files, - }, - } - g.Init(classes) - - generate := func(w io.Writer) error { - if buildX { - printcmd("gobind %s -outdir=%s %s", bindOption, javadir, pkgPath) - } - if buildN { - return nil - } - buf.Reset() - if err := g.GenJava(); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(javaFile, generate); err != nil { - return err - } - for i, name := range g.ClassNames() { - generate := func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - if err := g.GenClass(i); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - classFile := filepath.Join(javadir, name+".java") - if err := writeFile(classFile, generate); err != nil { - return err - } - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - if err := g.GenC(); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - if err := writeFile(cFile, generate); err != nil { - return err - } - generate = func(w io.Writer) error { - if buildN { - return nil - } - buf.Reset() - if err := g.GenH(); err != nil { - return err - } - _, err := io.Copy(w, &buf) - return err - } - return writeFile(hFile, generate) -} - -func (b *binder) GenGo(pkg *types.Package, allPkg []*types.Package, outdir string) error { - pkgName := "go_" - pkgPath := "" - if pkg != nil { - pkgName += pkg.Name() - pkgPath = pkg.Path() - } - goFile := filepath.Join(outdir, pkgName+"main.go") - - generate := func(w io.Writer) error { - if buildX { - printcmd("gobind -lang=go -outdir=%s %s", outdir, pkgPath) - } - if buildN { - return nil - } - conf := &bind.GeneratorConfig{ - Writer: w, - Fset: b.fset, - Pkg: pkg, - AllPkg: allPkg, - } - return bind.GenGo(conf) - } - if err := writeFile(goFile, generate); err != nil { - return err - } - return nil -} - func copyFile(dst, src string) error { if buildX { printcmd("cp %s %s", src, dst) @@ -669,87 +213,3 @@ func writeFile(filename string, generate func(io.Writer) error) error { return generate(f) } - -func loadExportData(pkgs []*build.Package, env []string, args ...string) ([]*types.Package, error) { - // Compile the package. This will produce good errors if the package - // doesn't typecheck for some reason, and is a necessary step to - // building the final output anyway. - paths := make([]string, len(pkgs)) - for i, p := range pkgs { - paths[i] = p.ImportPath - } - if err := goInstall(paths, env, args...); err != nil { - return nil, err - } - - goos, goarch := getenv(env, "GOOS"), getenv(env, "GOARCH") - - // Assemble a fake GOPATH and trick go/importer into using it. - // Ideally the importer package would let us provide this to - // it somehow, but this works with what's in Go 1.5 today and - // gives us access to the gcimporter package without us having - // to make a copy of it. - fakegopath := filepath.Join(tmpdir, "fakegopath") - if err := removeAll(fakegopath); err != nil { - return nil, err - } - if err := mkdir(filepath.Join(fakegopath, "pkg")); err != nil { - return nil, err - } - typePkgs := make([]*types.Package, len(pkgs)) - imp := importer.Default() - for i, p := range pkgs { - importPath := p.ImportPath - src := filepath.Join(pkgdir(env), importPath+".a") - dst := filepath.Join(fakegopath, "pkg/"+goos+"_"+goarch+"/"+importPath+".a") - if err := copyFile(dst, src); err != nil { - return nil, err - } - if buildN { - typePkgs[i] = types.NewPackage(importPath, path.Base(importPath)) - continue - } - oldDefault := build.Default - build.Default = ctx // copy - build.Default.GOARCH = goarch - build.Default.GOPATH = fakegopath - p, err := imp.Import(importPath) - build.Default = oldDefault - if err != nil { - return nil, err - } - typePkgs[i] = p - } - return typePkgs, nil -} - -func parse(pkgs []*build.Package) ([][]*ast.File, error) { - fset := token.NewFileSet() - var astPkgs [][]*ast.File - for _, pkg := range pkgs { - fileNames := append(append([]string{}, pkg.GoFiles...), pkg.CgoFiles...) - var files []*ast.File - for _, name := range fileNames { - f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, name), nil, parser.ParseComments) - if err != nil { - return nil, err - } - files = append(files, f) - } - astPkgs = append(astPkgs, files) - } - return astPkgs, nil -} - -func newBinder(pkgs []*types.Package) (*binder, error) { - for _, pkg := range pkgs { - if pkg.Name() == "main" { - return nil, fmt.Errorf("package %q (%q): can only bind a library package", pkg.Name(), pkg.Path()) - } - } - b := &binder{ - fset: token.NewFileSet(), - pkgs: pkgs, - } - return b, nil -} diff --git a/cmd/gomobile/bind_androidapp.go b/cmd/gomobile/bind_androidapp.go index db37139..ee24c95 100644 --- a/cmd/gomobile/bind_androidapp.go +++ b/cmd/gomobile/bind_androidapp.go @@ -21,114 +21,44 @@ func goAndroidBind(pkgs []*build.Package, androidArchs []string) error { if sdkDir := os.Getenv("ANDROID_HOME"); sdkDir == "" { return fmt.Errorf("this command requires ANDROID_HOME environment variable (path to the Android SDK)") } - // Ideally this would be -buildmode=c-shared. - // https://golang.org/issue/13234. - androidArgs := []string{"-gcflags=-shared", "-ldflags=-shared"} - paths := make([]string, len(pkgs)) - for i, p := range pkgs { - paths[i] = p.ImportPath + // Run gobind to generate the bindings + cmd := exec.Command( + "gobind", + "-lang=go,java", + "-outdir="+tmpdir, + ) + if len(ctx.BuildTags) > 0 { + cmd.Args = append(cmd.Args, "-tags="+strings.Join(ctx.BuildTags, ",")) + } + if bindJavaPkg != "" { + cmd.Args = append(cmd.Args, "-javapkg="+bindJavaPkg) + } + if bindClasspath != "" { + cmd.Args = append(cmd.Args, "-classpath="+bindClasspath) + } + if bindBootClasspath != "" { + cmd.Args = append(cmd.Args, "-bootclasspath="+bindBootClasspath) + } + for _, p := range pkgs { + cmd.Args = append(cmd.Args, p.ImportPath) + } + if err := runCmd(cmd); err != nil { + return err } androidDir := filepath.Join(tmpdir, "android") - mainFile := filepath.Join(tmpdir, "androidlib/main.go") - jpkgSrc := filepath.Join(tmpdir, "gen") // Generate binding code and java source code only when processing the first package. - first := true for _, arch := range androidArchs { env := androidEnv[arch] - // Add the generated Java class wrapper packages to GOPATH - gopath := fmt.Sprintf("GOPATH=%s%c%s", jpkgSrc, filepath.ListSeparator, os.Getenv("GOPATH")) + // Add the generated packages to GOPATH + gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, os.Getenv("GOPATH")) env = append(env, gopath) toolchain := ndk.Toolchain(arch) - if !first { - err := goBuild( - mainFile, - env, - "-buildmode=c-shared", - "-o="+filepath.Join(androidDir, "src/main/jniLibs/"+toolchain.abi+"/libgojni.so"), - ) - if err != nil { - return err - } - - continue - } - first = false - - srcDir := filepath.Join(tmpdir, "gomobile_bind") - if err := mkdir(srcDir); err != nil { - return err - } - - classes, err := GenClasses(pkgs, srcDir, jpkgSrc) - if err != nil { - return err - } - - typesPkgs, err := loadExportData(pkgs, env, androidArgs...) - if err != nil { - return fmt.Errorf("loadExportData failed: %v", err) - } - - astPkgs, err := parse(pkgs) - if err != nil { - return fmt.Errorf("parseAST failed: %v", err) - } - - binder, err := newBinder(typesPkgs) - if err != nil { - return err - } - - for _, pkg := range binder.pkgs { - if err := binder.GenGo(pkg, binder.pkgs, srcDir); err != nil { - return err - } - } - // Generate the error type. - if err := binder.GenGo(nil, binder.pkgs, srcDir); err != nil { - return err - } - - err = writeFile(mainFile, func(w io.Writer) error { - _, err := w.Write(androidMainFile) - return err - }) - if err != nil { - return fmt.Errorf("failed to create the main package for android: %v", err) - } - - p, err := ctx.Import("golang.org/x/mobile/bind", cwd, build.ImportComment) - if err != nil { - return fmt.Errorf(`"golang.org/x/mobile/bind" is not found; run go get golang.org/x/mobile/bind`) - } - repo := filepath.Clean(filepath.Join(p.Dir, "..")) // golang.org/x/mobile directory. - - jclsDir := filepath.Join(androidDir, "src", "main", "java") - for i, pkg := range binder.pkgs { - if err := binder.GenJava(pkg, astPkgs[i], binder.pkgs, classes, srcDir, jclsDir); err != nil { - return err - } - } - if err := binder.GenJava(nil, nil, binder.pkgs, classes, srcDir, jclsDir); err != nil { - return err - } - if err := binder.GenJavaSupport(srcDir); err != nil { - return err - } - if err := binder.GenGoSupport(srcDir); err != nil { - return err - } - - javaDir := filepath.Join(androidDir, "src/main/java/go") - if err := mkdir(javaDir); err != nil { - return err - } - err = goBuild( - mainFile, + err := goBuild( + "gobind", env, "-buildmode=c-shared", "-o="+filepath.Join(androidDir, "src/main/jniLibs/"+toolchain.abi+"/libgojni.so"), @@ -136,35 +66,16 @@ func goAndroidBind(pkgs []*build.Package, androidArchs []string) error { if err != nil { return err } - - for _, javaFile := range []string{"Seq.java", "LoadJNI.java"} { - src := filepath.Join(repo, "bind/java/"+javaFile) - dst := filepath.Join(javaDir, javaFile) - rm(dst) - if err := symlink(src, dst); err != nil { - return err - } - } } - if err := buildAAR(androidDir, pkgs, androidArchs); err != nil { + jsrc := filepath.Join(tmpdir, "java") + if err := buildAAR(jsrc, androidDir, pkgs, androidArchs); err != nil { return err } - return buildSrcJar(androidDir) + return buildSrcJar(jsrc) } -var androidMainFile = []byte(` -package main - -import ( - _ "golang.org/x/mobile/bind/java" - _ "../gomobile_bind" -) - -func main() {} -`) - -func buildSrcJar(androidDir string) error { +func buildSrcJar(src string) error { var out io.Writer = ioutil.Discard if !buildN { ext := filepath.Ext(buildO) @@ -180,7 +91,6 @@ func buildSrcJar(androidDir string) error { out = f } - src := filepath.Join(androidDir, "src/main/java") return writeJar(out, src) } @@ -202,7 +112,7 @@ func buildSrcJar(androidDir string) error { // aidl (optional, not relevant) // // javac and jar commands are needed to build classes.jar. -func buildAAR(androidDir string, pkgs []*build.Package, androidArchs []string) (err error) { +func buildAAR(srcDir, androidDir string, pkgs []*build.Package, androidArchs []string) (err error) { var out io.Writer = ioutil.Discard if buildO == "" { buildO = pkgs[0].Name + ".aar" @@ -248,8 +158,7 @@ func buildAAR(androidDir string, pkgs []*build.Package, androidArchs []string) ( if err != nil { return err } - src := filepath.Join(androidDir, "src/main/java") - if err := buildJar(w, src); err != nil { + if err := buildJar(w, srcDir); err != nil { return err } diff --git a/cmd/gomobile/bind_iosapp.go b/cmd/gomobile/bind_iosapp.go index a808fd3..c2b9499 100644 --- a/cmd/gomobile/bind_iosapp.go +++ b/cmd/gomobile/bind_iosapp.go @@ -17,30 +17,29 @@ import ( ) func goIOSBind(pkgs []*build.Package) error { - srcDir := filepath.Join(tmpdir, "src", "gomobile_bind") - genDir := filepath.Join(tmpdir, "gen") - wrappers, err := GenObjcWrappers(pkgs, srcDir, genDir) - if err != nil { - return err + // Run gobind to generate the bindings + cmd := exec.Command( + "gobind", + "-lang=go,objc", + "-outdir="+tmpdir, + ) + if len(ctx.BuildTags) > 0 { + cmd.Args = append(cmd.Args, "-tags="+strings.Join(ctx.BuildTags, ",")) } - env := darwinArmEnv - gopath := fmt.Sprintf("GOPATH=%s%c%s", genDir, filepath.ListSeparator, os.Getenv("GOPATH")) - env = append(env, gopath) - typesPkgs, err := loadExportData(pkgs, env) - if err != nil { + if bindPrefix != "" { + cmd.Args = append(cmd.Args, "-prefix="+bindPrefix) + } + for _, p := range pkgs { + cmd.Args = append(cmd.Args, p.ImportPath) + } + if err := runCmd(cmd); err != nil { return err } - astPkgs, err := parse(pkgs) - if err != nil { - return err - } + srcDir := filepath.Join(tmpdir, "src", "gobind") + gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, os.Getenv("GOPATH")) - binder, err := newBinder(typesPkgs) - if err != nil { - return err - } - name := binder.pkgs[0].Name() + name := pkgs[0].Name title := strings.Title(name) if buildO != "" && !strings.HasSuffix(buildO, ".framework") { @@ -50,46 +49,18 @@ func goIOSBind(pkgs []*build.Package) error { buildO = title + ".framework" } - for _, pkg := range binder.pkgs { - if err := binder.GenGo(pkg, binder.pkgs, srcDir); err != nil { - return err - } - } - // Generate the error type. - if err := binder.GenGo(nil, binder.pkgs, srcDir); err != nil { - return err - } - mainFile := filepath.Join(tmpdir, "src/iosbin/main.go") - err = writeFile(mainFile, func(w io.Writer) error { - _, err := w.Write(iosBindFile) - return err - }) - if err != nil { - return fmt.Errorf("failed to create the binding package for iOS: %v", err) + fileBases := make([]string, len(pkgs)+1) + for i, pkg := range pkgs { + fileBases[i] = bindPrefix + strings.Title(pkg.Name) } + fileBases[len(fileBases)-1] = "universe" - fileBases := make([]string, len(typesPkgs)+1) - for i, pkg := range binder.pkgs { - if fileBases[i], err = binder.GenObjc(pkg, astPkgs[i], binder.pkgs, srcDir, wrappers); err != nil { - return err - } - } - if fileBases[len(fileBases)-1], err = binder.GenObjc(nil, nil, binder.pkgs, srcDir, wrappers); err != nil { - return err - } - if err := binder.GenObjcSupport(srcDir); err != nil { - return err - } - if err := binder.GenGoSupport(srcDir); err != nil { - return err - } - - cmd := exec.Command("xcrun", "lipo", "-create") + cmd = exec.Command("xcrun", "lipo", "-create") for _, env := range [][]string{darwinArmEnv, darwinArm64Env, darwinAmd64Env} { env = append(env, gopath) arch := archClang(getenv(env, "GOARCH")) - path, err := goIOSBindArchive(name, mainFile, env, fileBases) + path, err := goIOSBindArchive(name, env) if err != nil { return fmt.Errorf("darwin-%s: %v", arch, err) } @@ -123,7 +94,7 @@ func goIOSBind(pkgs []*build.Package) error { headerFiles := make([]string, len(fileBases)) if len(fileBases) == 1 { headerFiles[0] = title + ".h" - err = copyFile( + err := copyFile( headers+"/"+title+".h", srcDir+"/"+bindPrefix+title+".objc.h", ) @@ -133,14 +104,14 @@ func goIOSBind(pkgs []*build.Package) error { } else { for i, fileBase := range fileBases { headerFiles[i] = fileBase + ".objc.h" - err = copyFile( + err := copyFile( headers+"/"+fileBase+".objc.h", srcDir+"/"+fileBase+".objc.h") if err != nil { return err } } - err = copyFile( + err := copyFile( headers+"/ref.h", srcDir+"/ref.h") if err != nil { @@ -175,7 +146,7 @@ func goIOSBind(pkgs []*build.Package) error { Module: title, Headers: headerFiles, } - err = writeFile(buildO+"/Versions/A/Modules/module.modulemap", func(w io.Writer) error { + err := writeFile(buildO+"/Versions/A/Modules/module.modulemap", func(w io.Writer) error { return iosModuleMapTmpl.Execute(w, mmVals) }) if err != nil { @@ -199,10 +170,10 @@ var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework mo export * }`)) -func goIOSBindArchive(name, path string, env, fileBases []string) (string, error) { +func goIOSBindArchive(name string, env []string) (string, error) { arch := getenv(env, "GOARCH") archive := filepath.Join(tmpdir, name+"-"+arch+".a") - err := goBuild(path, env, "-buildmode=c-archive", "-o", archive) + err := goBuild("gobind", env, "-buildmode=c-archive", "-o", archive) if err != nil { return "", err } @@ -210,18 +181,6 @@ func goIOSBindArchive(name, path string, env, fileBases []string) (string, error return archive, nil } -var iosBindFile = []byte(` -package main - -import ( - _ "../gomobile_bind" -) - -import "C" - -func main() {} -`) - var iosBindHeaderTmpl = template.Must(template.New("ios.h").Parse(` // Objective-C API for talking to the following Go packages // diff --git a/cmd/gomobile/bind_test.go b/cmd/gomobile/bind_test.go index b1d1abf..38744c6 100644 --- a/cmd/gomobile/bind_test.go +++ b/cmd/gomobile/bind_test.go @@ -55,16 +55,13 @@ func TestBindAndroid(t *testing.T) { tests := []struct { javaPkg string wantGobind string - wantPkgDir string }{ { wantGobind: "gobind -lang=java", - wantPkgDir: "asset", }, { javaPkg: "com.example.foo", wantGobind: "gobind -lang=java -javapkg=com.example.foo", - wantPkgDir: "com/example/foo/asset", }, } for _, tc := range tests { @@ -88,12 +85,12 @@ func TestBindAndroid(t *testing.T) { outputData AndroidPlatform string GobindJavaCmd string - JavaPkgDir string + JavaPkg string }{ outputData: defaultOutputData(), AndroidPlatform: platform, GobindJavaCmd: tc.wantGobind, - JavaPkgDir: tc.wantPkgDir, + JavaPkg: tc.javaPkg, } wantBuf := new(bytes.Buffer) @@ -115,44 +112,8 @@ func TestBindAndroid(t *testing.T) { var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile WORK=$WORK -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/gen/src/Java -GOOS=android GOARCH=arm CC=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/sysroot -isystem /NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/sysroot -isystem /NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/platforms/android-15/arch-arm -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 GOPATH=$WORK/gen:$GOPATH go install -pkgdir=$GOMOBILE/pkg_android_arm -x -gcflags=-shared -ldflags=-shared golang.org/x/mobile/asset -rm -r -f "$WORK/fakegopath" -mkdir -p $WORK/fakegopath/pkg -cp $GOMOBILE/pkg_android_arm/golang.org/x/mobile/asset.a $WORK/fakegopath/pkg/android_arm/golang.org/x/mobile/asset.a -mkdir -p $WORK/fakegopath/pkg/android_arm/golang.org/x/mobile -mkdir -p $WORK/gomobile_bind -gobind -lang=go -outdir=$WORK/gomobile_bind golang.org/x/mobile/asset -mkdir -p $WORK/gomobile_bind -gobind -lang=go -outdir=$WORK/gomobile_bind -mkdir -p $WORK/androidlib -mkdir -p $WORK/android/src/main/java/{{.JavaPkgDir}} -{{.GobindJavaCmd}} -outdir=$WORK/android/src/main/java/{{.JavaPkgDir}} golang.org/x/mobile/asset -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/android/src/main/java/go -{{.GobindJavaCmd}} -outdir=$WORK/android/src/main/java/go -mkdir -p $WORK/android/src/main/java/go -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/gomobile_bind -cp $GOPATH/src/golang.org/x/mobile/bind/java/seq_android.go.support $WORK/gomobile_bind/seq_android.go -mkdir -p $WORK/gomobile_bind -cp $GOPATH/src/golang.org/x/mobile/bind/java/seq_android.c.support $WORK/gomobile_bind/seq_android.c -mkdir -p $WORK/gomobile_bind -cp $GOPATH/src/golang.org/x/mobile/bind/java/seq.h $WORK/gomobile_bind/seq.h -mkdir -p $WORK/gomobile_bind -cp $GOPATH/src/golang.org/x/mobile/bind/seq.go.support $WORK/gomobile_bind/seq.go -mkdir -p $WORK/gomobile_bind -mkdir -p $WORK/android/src/main/java/go -GOOS=android GOARCH=arm CC=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/sysroot -isystem /NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/sysroot -isystem /NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/platforms/android-15/arch-arm -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 GOPATH=$WORK/gen:$GOPATH go build -pkgdir=$GOMOBILE/pkg_android_arm -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so $WORK/androidlib/main.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 -rm $WORK/android/src/main/java/go/LoadJNI.java -ln -s $GOPATH/src/golang.org/x/mobile/bind/java/LoadJNI.java $WORK/android/src/main/java/go/LoadJNI.java -PWD=$WORK/android/src/main/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java +gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} golang.org/x/mobile/asset +GOOS=android GOARCH=arm CC=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/sysroot -isystem /NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/sysroot -isystem /NDK/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=15 -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} --sysroot /NDK/platforms/android-15/arch-arm -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go build -pkgdir=$GOMOBILE/pkg_android_arm -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so gobind +PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java jar c -C $WORK/javac-output . `))