bind: support for custom java package name and objective-c prefix.

Introduce options -javapkg and -prefix for gobind command.

The following generates java class Testpkg with package name com.example.

gobind -lang=java -javapkg=com.example testpkg

The following generates objective-c files where function and type names
are prefixed with ExampleTestpkg.

gobind -lang=objc -prefix=Example testpkg

As discussed in golang/go#9660 and golang/go#12245.

Gomobile support is not yet implemented.

Change-Id: Ib9e39997ce915580a5a2e25643c0c28373f27ee1
Reviewed-on: https://go-review.googlesource.com/13969
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Hyang-Ah (Hana) Kim
2015-08-27 12:19:47 -04:00
committed by Hyang-Ah Hana Kim
parent 4628c38e0f
commit c384607ef3
18 changed files with 192 additions and 45 deletions

View File

@ -21,12 +21,16 @@ import (
)
// GenJava generates a Java API from a Go package.
func GenJava(w io.Writer, fset *token.FileSet, pkg *types.Package) error {
func GenJava(w io.Writer, fset *token.FileSet, pkg *types.Package, javaPkg string) error {
if javaPkg == "" {
javaPkg = javaPkgName(pkg.Name())
}
buf := new(bytes.Buffer)
g := &javaGen{
printer: &printer{buf: buf, indentEach: []byte(" ")},
fset: fset,
pkg: pkg,
javaPkg: javaPkg,
}
if err := g.gen(); err != nil {
return err
@ -57,12 +61,17 @@ func GenGo(w io.Writer, fset *token.FileSet, pkg *types.Package) error {
}
// GenObjc generates the Objective-C API from a Go package.
func GenObjc(w io.Writer, fset *token.FileSet, pkg *types.Package, isHeader bool) error {
func GenObjc(w io.Writer, fset *token.FileSet, pkg *types.Package, prefix string, isHeader bool) error {
if prefix == "" {
prefix = "Go"
}
buf := new(bytes.Buffer)
g := &objcGen{
printer: &printer{buf: buf, indentEach: []byte("\t")},
fset: fset,
pkg: pkg,
prefix: prefix,
}
var err error
if isHeader {

View File

@ -7,6 +7,7 @@ import (
"go/parser"
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
@ -96,7 +97,7 @@ func TestGenObjc(t *testing.T) {
for isHeader, suffix := range suffixes {
var buf bytes.Buffer
if err := GenObjc(&buf, fset, pkg, isHeader); err != nil {
if err := GenObjc(&buf, fset, pkg, "", isHeader); err != nil {
t.Errorf("%s: %v", filename, err)
continue
}
@ -121,7 +122,7 @@ func TestGenJava(t *testing.T) {
for _, filename := range tests {
var buf bytes.Buffer
pkg := typeCheck(t, filename)
if err := GenJava(&buf, fset, pkg); err != nil {
if err := GenJava(&buf, fset, pkg, ""); err != nil {
t.Errorf("%s: %v", filename, err)
continue
}
@ -165,3 +166,47 @@ func TestGenGo(t *testing.T) {
}
}
}
func TestCustomPrefix(t *testing.T) {
const datafile = "testdata/customprefix.go"
const isHeader = true
pkg := typeCheck(t, datafile)
testCases := []struct {
golden string
gen func(w io.Writer) error
}{
{
"testdata/customprefix.java.golden",
func(w io.Writer) error { return GenJava(w, fset, pkg, "com.example") },
},
{
"testdata/customprefix.objc.h.golden",
func(w io.Writer) error { return GenObjc(w, fset, pkg, "EX", isHeader) },
},
{
"testdata/customprefix.objc.m.golden",
func(w io.Writer) error { return GenObjc(w, fset, pkg, "EX", !isHeader) },
},
}
for _, tc := range testCases {
var buf bytes.Buffer
if err := tc.gen(&buf); err != nil {
t.Errorf("generating %s: %v", tc.golden, err)
continue
}
out := writeTempFile(t, "generated", buf.Bytes())
defer os.Remove(out)
if diffstr := diff(tc.golden, out); diffstr != "" {
t.Errorf("%s: generated file does not match:\b%s", tc.golden, diffstr)
if *updateFlag {
t.Logf("Updating %s...", tc.golden)
err := exec.Command("/bin/cp", out, tc.golden).Run()
if err != nil {
t.Errorf("Update failed: %s", err)
}
}
}
}
}

View File

@ -33,9 +33,10 @@ func (list ErrorList) Error() string {
type javaGen struct {
*printer
fset *token.FileSet
pkg *types.Package
err ErrorList
fset *token.FileSet
pkg *types.Package
javaPkg string
err ErrorList
}
func (g *javaGen) genStruct(obj *types.TypeName, T *types.Struct) {
@ -545,11 +546,19 @@ func (g *javaGen) errorf(format string, args ...interface{}) {
g.err = append(g.err, fmt.Errorf(format, args...))
}
const javaPreamble = `// Java Package %s is a proxy for talking to a Go program.
// gobind -lang=java %s
func (g *javaGen) gobindOpts() string {
opts := []string{"-lang=java"}
if g.javaPkg != javaPkgName(g.pkg.Name()) {
opts = append(opts, "-javapkg="+g.javaPkg)
}
return strings.Join(opts, " ")
}
const javaPreamble = `// Java class %[1]s.%[2]s is a proxy for talking to a Go program.
// gobind %[3]s %[4]s
//
// File is generated by gobind. Do not edit.
package go.%s;
package %[1]s;
import go.Seq;
@ -560,8 +569,8 @@ var javaNameReplacer = strings.NewReplacer(
".", "_",
)
func (g *javaGen) javaPkgName() string {
s := javaNameReplacer.Replace(g.pkg.Name())
func javaPkgName(pkgName string) string {
s := javaNameReplacer.Replace(pkgName)
// Look for Java keywords that are not Go keywords, and avoid using
// them as a package name.
//
@ -579,7 +588,7 @@ func (g *javaGen) javaPkgName() string {
"void", "volatile", "while":
s += "_"
}
return s
return "go." + s
}
func (g *javaGen) className() string {
@ -587,7 +596,7 @@ func (g *javaGen) className() string {
}
func (g *javaGen) gen() error {
g.Printf(javaPreamble, g.javaPkgName(), g.pkg.Path(), g.javaPkgName())
g.Printf(javaPreamble, g.javaPkg, g.className(), g.gobindOpts(), g.pkg.Path())
g.Printf("public abstract class %s {\n", g.className())
g.Indent()

View File

@ -9,8 +9,6 @@ import (
"go/token"
"go/types"
"strings"
"unicode"
"unicode/utf8"
)
// TODO(hyangah): error code/domain propagation
@ -21,6 +19,8 @@ type objcGen struct {
pkg *types.Package
err ErrorList
prefix string // prefix arg passed by flag.
// fields set by init.
pkgName string
namePrefix string
@ -28,14 +28,9 @@ type objcGen struct {
names []*types.TypeName
}
func capitalize(n string) string {
firstRune, size := utf8.DecodeRuneInString(n)
return string(unicode.ToUpper(firstRune)) + n[size:]
}
func (g *objcGen) init() {
g.pkgName = g.pkg.Name()
g.namePrefix = "Go" + capitalize(g.pkgName)
g.namePrefix = g.prefix + strings.Title(g.pkgName)
g.funcs = nil
g.names = nil
@ -57,8 +52,8 @@ func (g *objcGen) init() {
}
}
const objcPreamble = `// Objective-C API for talking to %s Go package.
// gobind -lang=objc %s
const objcPreamble = `// Objective-C API for talking to %[1]s Go package.
// gobind %[2]s %[3]s
//
// File is generated by gobind. Do not edit.
@ -67,9 +62,9 @@ const objcPreamble = `// Objective-C API for talking to %s Go package.
func (g *objcGen) genH() error {
g.init()
g.Printf(objcPreamble, g.pkg.Path(), g.pkg.Path())
g.Printf("#ifndef __Go%s_H__\n", capitalize(g.pkgName))
g.Printf("#define __Go%s_H__\n", capitalize(g.pkgName))
g.Printf(objcPreamble, g.pkg.Path(), g.gobindOpts(), g.pkg.Path())
g.Printf("#ifndef __Go%s_H__\n", strings.Title(g.pkgName))
g.Printf("#define __Go%s_H__\n", strings.Title(g.pkgName))
g.Printf("\n")
g.Printf("#include <Foundation/Foundation.h>")
g.Printf("\n\n")
@ -115,10 +110,18 @@ func (g *objcGen) genH() error {
return nil
}
func (g *objcGen) gobindOpts() string {
opts := []string{"-lang=objc"}
if g.prefix != "Go" {
opts = append(opts, "-prefix="+g.prefix)
}
return strings.Join(opts, " ")
}
func (g *objcGen) genM() error {
g.init()
g.Printf(objcPreamble, g.pkg.Path(), g.pkg.Path())
g.Printf(objcPreamble, g.pkg.Path(), g.gobindOpts(), g.pkg.Path())
g.Printf("#include %q\n", g.namePrefix+".h")
g.Printf("#include <Foundation/Foundation.h>\n")
g.Printf("#include \"seq.h\"\n")

View File

@ -1,4 +1,4 @@
// Java Package basictypes is a proxy for talking to a Go program.
// Java class go.basictypes.Basictypes is a proxy for talking to a Go program.
// gobind -lang=java basictypes
//
// File is generated by gobind. Do not edit.

10
bind/testdata/customprefix.go vendored Normal file
View File

@ -0,0 +1,10 @@
// Copyright 2015 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.
// Data for -pkgpath and -prefix options.
package customprefix
func F() {
}

20
bind/testdata/customprefix.java.golden vendored Normal file
View File

@ -0,0 +1,20 @@
// Java class com.example.Customprefix is a proxy for talking to a Go program.
// gobind -lang=java -javapkg=com.example customprefix
//
// File is generated by gobind. Do not edit.
package com.example;
import go.Seq;
public abstract class Customprefix {
private Customprefix() {} // uninstantiable
public static void F() {
go.Seq _in = new go.Seq();
go.Seq _out = new go.Seq();
Seq.send(DESCRIPTOR, CALL_F, _in, _out);
}
private static final int CALL_F = 1;
private static final String DESCRIPTOR = "customprefix";
}

View File

@ -0,0 +1,13 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc -prefix=EX customprefix
//
// File is generated by gobind. Do not edit.
#ifndef __GoCustomprefix_H__
#define __GoCustomprefix_H__
#include <Foundation/Foundation.h>
FOUNDATION_EXPORT void EXCustomprefixF();
#endif

View File

@ -0,0 +1,27 @@
// Objective-C API for talking to customprefix Go package.
// gobind -lang=objc -prefix=EX customprefix
//
// File is generated by gobind. Do not edit.
#include "EXCustomprefix.h"
#include <Foundation/Foundation.h>
#include "seq.h"
static NSString* errDomain = @"go.customprefix";
@protocol goSeqRefInterface
-(GoSeqRef*) ref;
@end
#define _DESCRIPTOR_ "customprefix"
#define _CALL_F_ 1
void EXCustomprefixF() {
GoSeq in_ = {};
GoSeq out_ = {};
go_seq_send(_DESCRIPTOR_, _CALL_F_, &in_, &out_);
go_seq_free(&in_);
go_seq_free(&out_);
}

View File

@ -1,4 +1,4 @@
// Java Package interfaces is a proxy for talking to a Go program.
// Java class go.interfaces.Interfaces is a proxy for talking to a Go program.
// gobind -lang=java interfaces
//
// File is generated by gobind. Do not edit.

View File

@ -1,4 +1,4 @@
// Java Package issue10788 is a proxy for talking to a Go program.
// Java class go.issue10788.Issue10788 is a proxy for talking to a Go program.
// gobind -lang=java issue10788
//
// File is generated by gobind. Do not edit.

View File

@ -1,4 +1,4 @@
// Java Package issue12328 is a proxy for talking to a Go program.
// Java class go.issue12328.Issue12328 is a proxy for talking to a Go program.
// gobind -lang=java issue12328
//
// File is generated by gobind. Do not edit.

View File

@ -1,4 +1,4 @@
// Java Package structs is a proxy for talking to a Go program.
// Java class go.structs.Structs is a proxy for talking to a Go program.
// gobind -lang=java structs
//
// File is generated by gobind. Do not edit.

View File

@ -1,4 +1,4 @@
// Java Package try_ is a proxy for talking to a Go program.
// Java class go.try_.Try is a proxy for talking to a Go program.
// gobind -lang=java try
//
// File is generated by gobind. Do not edit.

View File

@ -53,7 +53,7 @@ func genPkg(pkg *build.Package) {
switch *lang {
case "java":
w, closer := writer(fname, p)
processErr(bind.GenJava(w, fset, p))
processErr(bind.GenJava(w, fset, p, *javaPkg))
closer()
case "go":
w, closer := writer(fname, p)
@ -61,15 +61,15 @@ func genPkg(pkg *build.Package) {
closer()
case "objc":
if fname == "" {
processErr(bind.GenObjc(os.Stdout, fset, p, true))
processErr(bind.GenObjc(os.Stdout, fset, p, false))
processErr(bind.GenObjc(os.Stdout, fset, p, *prefix, true))
processErr(bind.GenObjc(os.Stdout, fset, p, *prefix, false))
} else {
hname := fname[:len(fname)-2] + ".h"
w, closer := writer(hname, p)
processErr(bind.GenObjc(w, fset, p, true))
processErr(bind.GenObjc(w, fset, p, *prefix, true))
closer()
w, closer = writer(fname, p)
processErr(bind.GenObjc(w, fset, p, false))
processErr(bind.GenObjc(w, fset, p, *prefix, false))
closer()
}
default:

View File

@ -13,8 +13,10 @@ import (
)
var (
lang = flag.String("lang", "java", "target language for bindings, either java, go, or objc (experimental).")
outdir = flag.String("outdir", "", "result will be written to the directory instead of stdout.")
lang = flag.String("lang", "java", "target language for bindings, either java, go, or objc (experimental).")
outdir = flag.String("outdir", "", "result will be written to the directory instead of stdout.")
javaPkg = flag.String("javapkg", "", "custom Java package path used instead of the default 'go.<go package name>'. Valid only with -lang=java.")
prefix = flag.String("prefix", "", "custom Objective-C name prefix used instead of the default 'Go'. Valid only with -lang=objc.")
)
var usage = `The Gobind tool generates Java language bindings for Go.
@ -24,6 +26,12 @@ 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)
}
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)

View File

@ -119,14 +119,16 @@ func (b *binder) GenObjc(outdir string) error {
printcmd("gobind -lang=objc %s > %s", b.pkg.Path(), mfile)
}
const objcPrefix = "" // TODO(hyangah): -prefix
generate := func(w io.Writer) error {
return bind.GenObjc(w, b.fset, b.pkg, false)
return bind.GenObjc(w, b.fset, b.pkg, objcPrefix, false)
}
if err := writeFile(mfile, generate); err != nil {
return err
}
generate = func(w io.Writer) error {
return bind.GenObjc(w, b.fset, b.pkg, true)
return bind.GenObjc(w, b.fset, b.pkg, objcPrefix, true)
}
if err := writeFile(hfile, generate); err != nil {
return err
@ -147,8 +149,9 @@ func (b *binder) GenJava(outdir string) error {
printcmd("gobind -lang=java %s > %s", b.pkg.Path(), javaFile)
}
const javaPkg = "" // TODO(hyangah): -javapkg
generate := func(w io.Writer) error {
return bind.GenJava(w, b.fset, b.pkg)
return bind.GenJava(w, b.fset, b.pkg, javaPkg)
}
if err := writeFile(javaFile, generate); err != nil {
return err

View File

@ -126,7 +126,7 @@ mkdir -p $GOMOBILE/android-{{.NDK}}/openal
mv $WORK/openal/lib $GOMOBILE/android-{{.NDK}}/openal/lib{{if eq .GOOS "darwin"}}
go install -p={{.NumCPU}} -x golang.org/x/mobile/gl
go install -p={{.NumCPU}} -x golang.org/x/mobile/app
go install -p={{.NumCPU}} -x golang.org/x/mobile/exp/app/debug {{end}}
go install -p={{.NumCPU}} -x golang.org/x/mobile/exp/app/debug{{end}}
GOOS=android GOARCH=arm GOARM=7 CC=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-gcc{{.EXE}} CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-g++{{.EXE}} CGO_ENABLED=1 go install -p={{.NumCPU}} -pkgdir=$GOMOBILE/pkg_android_arm -x std
{{if eq .GOOS "darwin"}}GOOS=darwin GOARCH=arm GOARM=7 CC=clang-iphoneos CXX=clang-iphoneos CGO_CFLAGS=-isysroot=iphoneos -arch armv7 CGO_LDFLAGS=-isysroot=iphoneos -arch armv7 CGO_ENABLED=1 go install -p={{.NumCPU}} -pkgdir=$GOMOBILE/pkg_darwin_arm -x std
GOOS=darwin GOARCH=arm64 CC=clang-iphoneos CXX=clang-iphoneos CGO_CFLAGS=-isysroot=iphoneos -arch arm64 CGO_LDFLAGS=-isysroot=iphoneos -arch arm64 CGO_ENABLED=1 go install -p={{.NumCPU}} -pkgdir=$GOMOBILE/pkg_darwin_arm64 -x std