2
0
mirror of synced 2025-02-22 14:28:14 +00:00

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=<ndk-toolchain>/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 <hyangah@gmail.com>
This commit is contained in:
Elias Naur 2018-03-07 22:51:37 +01:00
parent f16143114e
commit 4600df55ca
32 changed files with 352 additions and 958 deletions

View File

@ -930,7 +930,7 @@ extern void init_proxies();
classesGoHeader = `// File is generated by gobind. Do not edit.
package gomobile_bind
package main
/*
#include <stdlib.h> // for free()

View File

@ -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 <stdlib.h>

View File

@ -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")

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include <jni.h>
#include "seq.h"
#include "seq_android.h"
#include "_cgo_export.h"
JNIEXPORT void JNICALL

View File

@ -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

View File

@ -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 <jni.h>
//#include <stdint.h>
//#include <stdlib.h>
//#include "seq.h"
//#include "seq_android.h"
import "C"
import (
"unsafe"

View File

@ -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 <stdint.h>
#include <android/log.h>
@ -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__

View File

@ -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 <stdint.h>

View File

@ -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 <Foundation/Foundation.h>
#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__

View File

@ -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 <stdlib.h>
// #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() {}

View File

@ -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 <stdlib.h>

View File

@ -544,7 +544,7 @@ type Java_io_Console interface {
// File is generated by gobind. Do not edit.
package gomobile_bind
package main
/*
#include <stdlib.h> // 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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -220,7 +220,7 @@ type Java_lang_Character_Subset interface {
// File is generated by gobind. Do not edit.
package gomobile_bind
package main
/*
#include <stdlib.h> // 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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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 <stdlib.h>

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
//

View File

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