go.mobile/cmd/gobind: language binding command line tool
LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/120140043
This commit is contained in:
parent
997628f863
commit
e8dea067be
172
cmd/gobind/doc.go
Normal file
172
cmd/gobind/doc.go
Normal file
@ -0,0 +1,172 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Gobind generates language bindings that make it possible to call Go code
|
||||
and pass objects from Java.
|
||||
|
||||
Using gobind
|
||||
|
||||
Gobind takes a Go package and generates bindings for all of the exported
|
||||
symbols. The exported symbols define the cross-language interface.
|
||||
|
||||
The gobind tool generates both an API stub in Java, and binding code in
|
||||
Go. Start with a Go package:
|
||||
|
||||
package hi
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Hello(name string) {
|
||||
fmt.Println("Hello, %s!\n", name)
|
||||
}
|
||||
|
||||
Generate a Go binding package and Java stubs:
|
||||
|
||||
go install code.google.com/p/go.mobile/cmd/gobind
|
||||
gobind -lang=go github.com/crawshaw/hi > hi/go_hi/go_hi.go
|
||||
gobind -lang=java github.com/crawshaw/hi > hi/Hi.java
|
||||
|
||||
The generated Go package, go_hi, must be linked into your Go program:
|
||||
|
||||
import _ "github.com/crawshaw/hi/go_hi"
|
||||
|
||||
Type restrictions
|
||||
|
||||
At present, only a subset of Go types are supported.
|
||||
|
||||
All exported symbols in the package must have types that are supported.
|
||||
Supported types include:
|
||||
|
||||
- Signed integer and floating point types.
|
||||
|
||||
- String and boolean types.
|
||||
|
||||
- Any function type all of whose parameters and results have
|
||||
supported types. Functions must return either no results,
|
||||
one result, or two results where the type of the second is
|
||||
the built-in 'error' type.
|
||||
|
||||
- Any interface type, all of whose exported methods have
|
||||
supported function types.
|
||||
|
||||
- Any struct type, all of whose exported methods have
|
||||
supported function types and all of whose exported fields
|
||||
have supported types.
|
||||
|
||||
Unexported symbols have no effect on the cross-language interface, and
|
||||
as such are not restricted.
|
||||
|
||||
The set of supported types will eventually be expanded to cover all Go
|
||||
types, but this is a work in progress.
|
||||
|
||||
Exceptions and panics are not yet supported. If either pass a language
|
||||
boundary, the program will exit.
|
||||
|
||||
Passing Go objects to foreign languages
|
||||
|
||||
Consider a type for counting:
|
||||
|
||||
package mypkg
|
||||
|
||||
type Counter struct {
|
||||
Value int
|
||||
}
|
||||
|
||||
func (c *Counter) Inc() { c.Value++ }
|
||||
|
||||
func New() *Counter { return &Counter{ 5 } }
|
||||
|
||||
The generated bindings enable Java programs to create and use a Counter.
|
||||
|
||||
public abstract class Mypkg {
|
||||
private Mypkg() {}
|
||||
public static final class Counter {
|
||||
public void Inc() { ... }
|
||||
public long GetValue() { ... }
|
||||
public void SetValue(long value) { ... }
|
||||
}
|
||||
public static Counter New() { ... }
|
||||
}
|
||||
|
||||
The package-level function New can be called like so:
|
||||
|
||||
Counter c = Mypkg.New()
|
||||
|
||||
returns a Java Counter, which is a proxy for a Go *Counter. Calling the Inc
|
||||
and Get methods will call the Go implementations of these methods.
|
||||
|
||||
Passing foreign language objects to Go
|
||||
|
||||
For a Go interface:
|
||||
|
||||
package myfmt
|
||||
|
||||
type Printer interface {
|
||||
Print(s string)
|
||||
}
|
||||
|
||||
func PrintHello(p Printer) {
|
||||
p.Print("Hello, World!")
|
||||
}
|
||||
|
||||
gobind generates a Java stub that can be used to implement a Printer:
|
||||
|
||||
public abstract class Myfmt {
|
||||
private Myfmt() {}
|
||||
public interface Printer {
|
||||
public void Print(String s);
|
||||
|
||||
public static abstract class Stub implements Printer {
|
||||
...
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
public static void PrintHello(Printer p) { ... }
|
||||
}
|
||||
|
||||
You can extend Myfmt.Printer.Stub to implement the Printer interface, and
|
||||
pass it to Go using the PrintHello package function:
|
||||
|
||||
public class SysPrint extends Myfmt.Printer.Stub {
|
||||
public void Print(String s) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
The Java implementation can be used like so:
|
||||
|
||||
Myfmt.Printer printer = new SysPrint();
|
||||
Myfmt.PrintHello(printer);
|
||||
|
||||
Avoid reference cycles
|
||||
|
||||
The language bindings maintain a reference to each object that has been
|
||||
proxied. When a proxy object becomes unreachable, its finalizer reports
|
||||
this fact to the object's native side, so that the reference can be
|
||||
removed, potentially allowing the object to be reclaimed by its native
|
||||
garbage collector. The mechanism is symmetric.
|
||||
|
||||
However, it is possible to create a reference cycle between Go and
|
||||
Java objects, via proxies, meaning objects cannot be collected. This
|
||||
causes a memory leak.
|
||||
|
||||
For example, if a Go object G holds a reference to the Go proxy of a
|
||||
Java object J, and J holds a reference to the Java proxy of G, then the
|
||||
language bindings on each side must keep G and J live even if they are
|
||||
otherwise unreachable.
|
||||
|
||||
We recommend that implementations of foreign interfaces do not hold
|
||||
references to proxies of objects. That is: if you extend a Stub in
|
||||
Java, do not store an instance of Seq.Object inside it.
|
||||
|
||||
Further reading
|
||||
|
||||
Examples can be found in http://code.google.com/p/go.mobile/example.
|
||||
|
||||
Design doc: http://golang.org/s/gobind
|
||||
*/
|
||||
package main
|
87
cmd/gobind/gen.go
Normal file
87
cmd/gobind/gen.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
|
||||
"code.google.com/p/go.mobile/bind"
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func genPkg(pkg *build.Package) {
|
||||
if len(pkg.CgoFiles) > 0 {
|
||||
errorf("gobind: cannot use cgo-dependent package as service definition: %s", pkg.CgoFiles[0])
|
||||
return
|
||||
}
|
||||
|
||||
files := parseFiles(pkg.Dir, pkg.GoFiles)
|
||||
if len(files) == 0 {
|
||||
return // some error has been reported
|
||||
}
|
||||
|
||||
conf := types.Config{
|
||||
Error: func(err error) {
|
||||
errorf("%v", err)
|
||||
},
|
||||
}
|
||||
p, err := conf.Check(pkg.ImportPath, fset, files, nil)
|
||||
if err != nil {
|
||||
return // printed above
|
||||
}
|
||||
|
||||
switch *lang {
|
||||
case "java":
|
||||
err = bind.GenJava(os.Stdout, fset, p)
|
||||
case "go":
|
||||
err = bind.GenGo(os.Stdout, fset, p)
|
||||
default:
|
||||
errorf("unknown target language: %q", *lang)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if list, _ := err.(bind.ErrorList); len(list) > 0 {
|
||||
for _, err := range list {
|
||||
errorf("%v", err)
|
||||
}
|
||||
} else {
|
||||
errorf("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fset = token.NewFileSet()
|
||||
|
||||
func parseFiles(dir string, filenames []string) []*ast.File {
|
||||
var files []*ast.File
|
||||
hasErr := false
|
||||
for _, filename := range filenames {
|
||||
path := filepath.Join(dir, filename)
|
||||
file, err := parser.ParseFile(fset, path, nil, parser.AllErrors)
|
||||
if err != nil {
|
||||
hasErr = true
|
||||
if list, _ := err.(scanner.ErrorList); len(list) > 0 {
|
||||
for _, err := range list {
|
||||
errorf("%v", err)
|
||||
}
|
||||
} else {
|
||||
errorf("%v", err)
|
||||
}
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
if hasErr {
|
||||
return nil
|
||||
}
|
||||
return files
|
||||
}
|
44
cmd/gobind/main.go
Normal file
44
cmd/gobind/main.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var lang = flag.String("lang", "java", "target language for bindings, either java or go")
|
||||
|
||||
var usage = `The Gobind tool generates Java language bindings for Go.
|
||||
|
||||
For usage details, see doc.go.`
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, arg := range flag.Args() {
|
||||
pkg, err := build.Import(arg, cwd, 0)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v", arg, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
genPkg(pkg)
|
||||
}
|
||||
os.Exit(exitStatus)
|
||||
}
|
||||
|
||||
var exitStatus = 0
|
||||
|
||||
func errorf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
exitStatus = 1
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user