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 . `))