1
0
Fork 0

Adding upstream version 0.0~git20250520.a1d9079+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 19:46:29 +02:00
parent 590ac7ff5f
commit 20149b7f3a
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
456 changed files with 70406 additions and 0 deletions

252
cmd/gobind/doc.go Normal file
View file

@ -0,0 +1,252 @@
// 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
functions from Java and Objective-C.
Typically gobind is not used directly. Instead, a binding is
generated and automatically packaged for Android or iOS by
`gomobile bind`. For more details on installing and using the gomobile
tool, see https://golang.org/x/mobile/cmd/gomobile.
# Binding Go
Gobind generates target language (Java or Objective-C) bindings for
each exported symbol in a Go package. The Go package you choose to
bind defines a cross-language interface.
Bindings require additional Go code be generated, so using gobind
manually requires calling it twice, first with -lang=<target>, where
target is either java or objc, and again with -lang=go. The generated
package can then be _ imported into a Go program, typically built
with -buildmode=c-archive for iOS or -buildmode=c-shared for Android.
These details are handled by the `gomobile bind` command.
# Passing Go objects to target languages
Consider a type for counting:
package mypkg
type Counter struct {
Value int
}
func (c *Counter) Inc() { c.Value++ }
func NewCounter() *Counter { return &Counter{ 5 } }
In Java, the generated bindings are,
public abstract class Mypkg {
public static native Counter newCounter();
}
and
public final class Counter {
public Counter() { ... }
public final long getValue();
public final void setValue(long v);
public void inc();
}
The package-level function newCounter can be called like so:
Counter c = Mypkg.newCounter()
For convenience, functions on the form NewT(...) *T are converted to constructors for T:
Counter c = new Counter()
Both forms returns a Java Counter, which is a proxy for a Go *Counter. Calling the inc, getValue and
setValue methods will call the Go implementations of these methods.
Similarly, the same Go package will generate the Objective-C interface
@class GoMypkgCounter;
@interface GoMypkgCounter : NSObject {
}
@property(strong, readonly) id ref;
- (void)inc;
- (int64_t)value;
- (void)setValue:(int64_t)v;
@end
FOUNDATION_EXPORT GoMypkgCounter* GoMypkgNewCounter(void);
The equivalent of calling newCounter in Go is GoMypkgNewCounter in Objective-C.
The returned GoMypkgCounter* holds a reference to an underlying Go
*Counter.
# Passing target 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 interface that can be used to implement a Printer:
public abstract class Myfmt {
public static void printHello(Printer p0);
}
and
public interface Printer {
public void print(String s);
}
You can implement Printer, and pass it to Go using the printHello
package function:
public class SysPrint implements Printer {
public void print(String s) {
System.out.println(s);
}
}
The Java implementation can be used like so:
Printer printer = new SysPrint();
Myfmt.printHello(printer);
For Objective-C binding, gobind generates a protocol that declares
methods corresponding to Go interface's methods.
@protocol GoMyfmtPrinter
- (void)Print:(NSString*)s;
@end
FOUNDATION_EXPORT void GoMyfmtPrintHello(id<GoMyfmtPrinter> p0);
Any Objective-C classes conforming to the GoMyfmtPrinter protocol can be
passed to Go using the GoMyfmtPrintHello function:
@interface SysPrint : NSObject<GoMyfmtPrinter> {
}
@end
@implementation SysPrint {
}
- (void)Print:(NSString*)s {
NSLog("%@", s);
}
@end
The Objective-C implementation can be used like so:
SysPrint* printer = [[SysPrint alloc] init];
GoMyfmtPrintHello(printer);
# 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.
- Byte slice types. Note that byte slices are passed by reference,
and support mutation.
- 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 more
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.
# Reverse bindings
Gobind also supports accessing API from Java or Objective C from Go.
Similar to how Cgo supports the magic "C" import, gobind recognizes
import statements that start with "Java/" or "ObjC/". For example,
to import java.lang.System and call the static method currentTimeMillis:
import "Java/java/lang/System"
t := System.CurrentTimeMillis()
Similarly, to import NSDate and call the static method [NSDate date]:
import "ObjC/Foundation/NSDate"
d := NSDate.Date()
Gobind also supports specifying particular classes, interfaces or
protocols a particular Go struct should extend or implement. For example,
to create an Android Activity subclass MainActivity:
import "Java/android/app/Activity"
type MainActivity struct {
app.Activity
}
Gobind also recognizes Java interfaces as well as Objective C classes and
protocols the same way.
For more details on binding the native API, see the design proposals,
https://golang.org/issues/16876 (Java) and https://golang.org/issues/17102
(Objective C).
# 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
objects in target languages, via proxies, meaning objects cannot be
collected. This causes a memory leak.
For example, in Java: 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 implement a Go
interface in Java, do not store an instance of Seq.Object inside it.
# Further reading
Examples can be found in http://golang.org/x/mobile/example.
Design doc: http://golang.org/s/gobind
*/
package main

391
cmd/gobind/gen.go Normal file
View file

@ -0,0 +1,391 @@
// 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 (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"os"
"path/filepath"
"strings"
"unicode"
"unicode/utf8"
"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"
"golang.org/x/tools/go/packages"
)
func genPkg(lang string, p *types.Package, astFiles []*ast.File, allPkg []*types.Package, classes []*java.Class, otypes []*objc.Named) {
fname := defaultFileName(lang, p)
conf := &bind.GeneratorConfig{
Fset: fset,
Pkg: p,
AllPkg: allPkg,
}
var pname string
if p != nil {
pname = p.Name()
} else {
pname = "universe"
}
var buf bytes.Buffer
generator := &bind.Generator{
Printer: &bind.Printer{Buf: &buf, IndentEach: []byte("\t")},
Fset: conf.Fset,
AllPkg: conf.AllPkg,
Pkg: conf.Pkg,
Files: astFiles,
}
switch lang {
case "java":
g := &bind.JavaGen{
JavaPkg: *javaPkg,
Generator: generator,
}
g.Init(classes)
pkgname := bind.JavaPkgName(*javaPkg, p)
pkgDir := strings.Replace(pkgname, ".", "/", -1)
buf.Reset()
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("java", pkgDir, name+".java"))
processErr(g.GenClass(i))
io.Copy(w, &buf)
closer()
}
buf.Reset()
w, closer = writer(filepath.Join("src", "gobind", pname+"_android.c"))
processErr(g.GenC())
io.Copy(w, &buf)
closer()
buf.Reset()
w, closer = writer(filepath.Join("src", "gobind", pname+"_android.h"))
processErr(g.GenH())
io.Copy(w, &buf)
closer()
// Generate support files along with the universe package
if p == nil {
dir, err := packageDir("golang.org/x/mobile/bind")
if err != nil {
errorf(`"golang.org/x/mobile/bind" is not found; run go get golang.org/x/mobile/bind: %v`, err)
return
}
repo := filepath.Clean(filepath.Join(dir, "..")) // golang.org/x/mobile directory.
for _, javaFile := range []string{"Seq.java"} {
src := filepath.Join(repo, "bind/java/"+javaFile)
in, err := os.Open(src)
if err != nil {
errorf("failed to open Java support file: %v", err)
}
defer in.Close()
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
if err != nil {
errorf("unable to import bind/java: %v", err)
return
}
javaDir, err := packageDir("golang.org/x/mobile/bind/java")
if err != nil {
errorf("unable to import bind/java: %v", err)
return
}
copyFile(filepath.Join("src", "gobind", "seq_android.c"), filepath.Join(javaDir, "seq_android.c.support"))
copyFile(filepath.Join("src", "gobind", "seq_android.go"), filepath.Join(javaDir, "seq_android.go.support"))
copyFile(filepath.Join("src", "gobind", "seq_android.h"), filepath.Join(javaDir, "seq_android.h"))
}
case "go":
w, closer := writer(filepath.Join("src", "gobind", fname))
conf.Writer = w
processErr(bind.GenGo(conf))
closer()
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()
dir, err := packageDir("golang.org/x/mobile/bind")
if err != nil {
errorf("unable to import bind: %v", err)
return
}
copyFile(filepath.Join("src", "gobind", "seq.go"), filepath.Join(dir, "seq.go.support"))
case "objc":
g := &bind.ObjcGen{
Generator: generator,
Prefix: *prefix,
}
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(filepath.Join("src", "gobind", hname))
processErr(g.GenH())
io.Copy(w, &buf)
closer()
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()
if p == nil {
// Copy support files
dir, err := packageDir("golang.org/x/mobile/bind/objc")
if err != nil {
errorf("unable to import bind/objc: %v", err)
return
}
copyFile(filepath.Join("src", "gobind", "seq_darwin.m"), filepath.Join(dir, "seq_darwin.m.support"))
copyFile(filepath.Join("src", "gobind", "seq_darwin.go"), filepath.Join(dir, "seq_darwin.go.support"))
copyFile(filepath.Join("src", "gobind", "ref.h"), filepath.Join(dir, "ref.h"))
copyFile(filepath.Join("src", "gobind", "seq_darwin.h"), filepath.Join(dir, "seq_darwin.h"))
}
default:
errorf("unknown target language: %q", lang)
}
}
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.ObjcWrapper{
Printer: &bind.Printer{
IndentEach: []byte("\t"),
Buf: &buf,
},
}
var genNames []string
for _, emb := range embedders {
genNames = append(genNames, emb.Name)
}
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
}
pkgFile := filepath.Join(pkgDir, "package.go")
buf.Reset()
cg.GenPackage(i)
if err := os.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil {
return err
}
}
buf.Reset()
cg.GenInterfaces()
objcBase := filepath.Join(dir, "src", "ObjC")
if err := os.MkdirAll(objcBase, 0700); err != nil {
return err
}
if err := os.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 := os.WriteFile(filepath.Join(goBase, "interfaces_darwin.go"), buf.Bytes(), 0600); err != nil {
return err
}
buf.Reset()
cg.GenH()
if err := os.WriteFile(filepath.Join(goBase, "interfaces.h"), buf.Bytes(), 0600); err != nil {
return err
}
buf.Reset()
cg.GenM()
if err := os.WriteFile(filepath.Join(goBase, "interfaces_darwin.m"), buf.Bytes(), 0600); err != nil {
return err
}
return nil
}
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 := os.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 := os.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 := os.WriteFile(filepath.Join(goBase, "classes_android.go"), buf.Bytes(), 0600); err != nil {
return err
}
buf.Reset()
cg.GenH()
if err := os.WriteFile(filepath.Join(goBase, "classes.h"), buf.Bytes(), 0600); err != nil {
return err
}
buf.Reset()
cg.GenC()
if err := os.WriteFile(filepath.Join(goBase, "classes_android.c"), buf.Bytes(), 0600); err != nil {
return err
}
return nil
}
func processErr(err error) {
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 writer(fname string) (w io.Writer, closer func()) {
if *outdir == "" {
return os.Stdout, func() { return }
}
name := filepath.Join(*outdir, fname)
dir := filepath.Dir(name)
if err := os.MkdirAll(dir, 0755); err != nil {
errorf("invalid output dir: %v", err)
os.Exit(exitStatus)
}
f, err := os.Create(name)
if err != nil {
errorf("invalid output dir: %v", err)
os.Exit(exitStatus)
}
closer = func() {
if err := f.Close(); err != nil {
errorf("error in closing output file: %v", err)
}
}
return f, closer
}
func copyFile(dst, src string) {
w, closer := writer(dst)
f, err := os.Open(src)
if err != nil {
errorf("unable to open file: %v", err)
closer()
os.Exit(exitStatus)
}
if _, err := io.Copy(w, f); err != nil {
errorf("unable to copy file: %v", err)
f.Close()
closer()
os.Exit(exitStatus)
}
f.Close()
closer()
}
func defaultFileName(lang string, pkg *types.Package) string {
switch lang {
case "java":
if pkg == nil {
return "Universe.java"
}
firstRune, size := utf8.DecodeRuneInString(pkg.Name())
className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:]
return className + ".java"
case "go":
if pkg == nil {
return "go_main.go"
}
return "go_" + pkg.Name() + "main.go"
case "objc":
if pkg == nil {
return "Universe.m"
}
firstRune, size := utf8.DecodeRuneInString(pkg.Name())
className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:]
return *prefix + className + ".m"
}
errorf("unknown target language: %q", lang)
os.Exit(exitStatus)
return ""
}
func packageDir(path string) (string, error) {
mode := packages.NeedFiles
pkgs, err := packages.Load(&packages.Config{Mode: mode}, path)
if err != nil {
return "", err
}
if len(pkgs) == 0 || len(pkgs[0].GoFiles) == 0 {
return "", fmt.Errorf("no Go package in %v", path)
}
pkg := pkgs[0]
if len(pkg.Errors) > 0 {
return "", fmt.Errorf("%v", pkg.Errors)
}
return filepath.Dir(pkg.GoFiles[0]), nil
}

218
cmd/gobind/gobind_test.go Normal file
View file

@ -0,0 +1,218 @@
// Copyright 2016 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 (
"bytes"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"testing"
"golang.org/x/tools/go/packages/packagestest"
)
func TestMain(m *testing.M) {
// To avoid recompiling the gobind command (and to support compiler options
// like -race and -coverage), allow the test binary itself to re-exec itself
// as the gobind command by setting an environment variable.
if os.Getenv("GOBIND_TEST_IS_GOBIND") != "" {
main()
os.Exit(0)
}
os.Setenv("GOBIND_TEST_IS_GOBIND", "1")
os.Exit(m.Run())
}
var tests = []struct {
name string
lang string
pkg string
goos string
// reverse is true if the test needs to generate reverse bindings using
// external tools such as javap.
reverse bool
}{
{
name: "ObjC-Testpkg",
lang: "objc",
pkg: "golang.org/x/mobile/bind/testdata/testpkg",
},
{
name: "Java-Testpkg",
lang: "java",
pkg: "golang.org/x/mobile/bind/testdata/testpkg",
},
{
name: "Go-Testpkg",
lang: "go",
pkg: "golang.org/x/mobile/bind/testdata/testpkg",
},
{
name: "Java-Javapkg",
lang: "java",
pkg: "golang.org/x/mobile/bind/testdata/testpkg/javapkg",
goos: "android",
reverse: true,
},
{
name: "Go-Javapkg",
lang: "go",
pkg: "golang.org/x/mobile/bind/testdata/testpkg/javapkg",
goos: "android",
reverse: true,
},
{
name: "Go-Cgopkg",
lang: "go,java,objc",
pkg: "golang.org/x/mobile/bind/testdata/cgopkg",
goos: "android",
},
}
func mustHaveBindTestdata(t testing.TB) {
switch runtime.GOOS {
case "android", "ios":
t.Skipf("skipping: test cannot access ../../bind/testdata on %s/%s", runtime.GOOS, runtime.GOARCH)
}
}
func gobindBin(t testing.TB) string {
switch runtime.GOOS {
case "js", "ios":
t.Skipf("skipping: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
}
p, err := os.Executable()
if err != nil {
t.Fatal(err)
}
return p
}
func runGobind(t testing.TB, lang, pkg, goos string, exported *packagestest.Exported) error {
cmd := exec.Command(gobindBin(t), "-lang", lang, pkg)
cmd.Dir = exported.Config.Dir
cmd.Env = exported.Config.Env
if goos != "" {
// Add CGO_ENABLED=1 explicitly since Cgo is disabled when GOOS is different from host OS.
cmd.Env = append(cmd.Env, "GOOS="+goos, "CGO_ENABLED=1")
}
stderr := new(strings.Builder)
cmd.Stderr = stderr
stdout := new(strings.Builder)
cmd.Stdout = stdout
err := cmd.Run()
if testing.Verbose() && stdout.Len() > 0 {
t.Logf("stdout (%v):\n%s", cmd, stderr)
}
if stderr.Len() > 0 {
t.Logf("stderr (%v):\n%s", cmd, stderr)
}
if err != nil {
return fmt.Errorf("%v: %w", cmd, err)
}
return nil
}
func TestGobind(t *testing.T) { packagestest.TestAll(t, testGobind) }
func testGobind(t *testing.T, exporter packagestest.Exporter) {
mustHaveBindTestdata(t)
_, javapErr := exec.LookPath("javap")
exported := packagestest.Export(t, exporter, []packagestest.Module{{
Name: "golang.org/x/mobile",
Files: packagestest.MustCopyFileTree("../.."),
}})
defer exported.Cleanup()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if exporter == packagestest.Modules && test.reverse {
t.Skip("reverse binding does't work with Go modules")
}
if test.reverse && javapErr != nil {
t.Skip("reverse bind test requires javap which is not available")
}
if err := runGobind(t, test.lang, test.pkg, test.goos, exported); err != nil {
t.Error(err)
}
})
}
}
func TestDocs(t *testing.T) { packagestest.TestAll(t, testDocs) }
func testDocs(t *testing.T, exporter packagestest.Exporter) {
mustHaveBindTestdata(t)
const docsrc = `
package doctest
// This is a comment.
type Struct struct{
}`
exported := packagestest.Export(t, exporter, []packagestest.Module{
{
Name: "example.com/doctest",
Files: map[string]interface{}{
"doc.go": docsrc,
},
},
{
// gobind requires golang.org/x/mobile to generate code for reverse bindings.
Name: "golang.org/x/mobile",
Files: packagestest.MustCopyFileTree("../.."),
},
})
defer exported.Cleanup()
const comment = "This is a comment."
for _, lang := range []string{"java", "objc"} {
cmd := exec.Command(gobindBin(t), "-lang", lang, "example.com/doctest")
cmd.Dir = exported.Config.Dir
cmd.Env = exported.Config.Env
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("gobind -lang %s failed: %v: %s", lang, err, out)
continue
}
if bytes.Index(out, []byte(comment)) == -1 {
t.Errorf("gobind output for language %s did not contain the comment %q", lang, comment)
}
}
}
func BenchmarkGobind(b *testing.B) {
packagestest.BenchmarkAll(b, benchmarkGobind)
}
func benchmarkGobind(b *testing.B, exporter packagestest.Exporter) {
_, javapErr := exec.LookPath("javap")
exported := packagestest.Export(b, exporter, []packagestest.Module{{
Name: "golang.org/x/mobile",
Files: packagestest.MustCopyFileTree("../.."),
}})
defer exported.Cleanup()
for _, test := range tests {
b.Run(test.name, func(b *testing.B) {
if exporter == packagestest.Modules && test.reverse {
b.Skip("reverse binding does't work with Go modules")
}
if test.reverse && javapErr != nil {
b.Skip("reverse bind test requires javap which is not available")
}
for i := 0; i < b.N; i++ {
if err := runGobind(b, test.lang, test.pkg, test.goos, exported); err != nil {
b.Error(err)
}
}
})
}
}

11
cmd/gobind/implicit.go Normal file
View file

@ -0,0 +1,11 @@
// This file imports implicit dependencies required by generated code.
//go:build mobile_implicit
package main
import (
_ "golang.org/x/mobile/bind"
_ "golang.org/x/mobile/bind/java"
_ "golang.org/x/mobile/bind/objc"
)

168
cmd/gobind/main.go Normal file
View file

@ -0,0 +1,168 @@
// 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 (
"bytes"
"flag"
"fmt"
"go/ast"
"go/types"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/mobile/internal/importers"
"golang.org/x/mobile/internal/importers/java"
"golang.org/x/mobile/internal/importers/objc"
"golang.org/x/tools/go/packages"
)
var (
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.
For usage details, see doc.go.`
func main() {
flag.Parse()
run()
os.Exit(exitStatus)
}
func run() {
var langs []string
if *lang != "" {
langs = strings.Split(*lang, ",")
} else {
langs = []string{"go", "java", "objc"}
}
// We need to give appropriate environment variables like CC or CXX so that the returned packages no longer have errors.
// However, getting such environment variables is difficult or impossible so far.
// Gomobile can obtain such environment variables in env.go, but this logic assumes some condiitons gobind doesn't assume.
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedFiles |
packages.NeedImports | packages.NeedDeps |
packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo,
BuildFlags: []string{"-tags", strings.Join(strings.Split(*tags, ","), " ")},
}
// Call Load twice to warm the cache. There is a known issue that the result of Load
// depends on build cache state. See golang/go#33687.
packages.Load(cfg, flag.Args()...)
allPkg, err := packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatal(err)
}
jrefs, err := importers.AnalyzePackages(allPkg, "Java/")
if err != nil {
log.Fatal(err)
}
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 = jimp.Import(jrefs)
if err != nil {
log.Fatal(err)
}
}
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 {
srcDir := *outdir
if srcDir == "" {
srcDir, err = os.MkdirTemp(os.TempDir(), "gobind-")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(srcDir)
} else {
srcDir, err = filepath.Abs(srcDir)
if err != nil {
log.Fatal(err)
}
}
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)
}
}
// Add a new directory to GOPATH where the file for reverse bindings exist, and recreate allPkg.
// It is because the current allPkg did not solve imports for reverse bindings.
var gopath string
if out, err := exec.Command("go", "env", "GOPATH").Output(); err != nil {
log.Fatal(err)
} else {
gopath = string(bytes.TrimSpace(out))
}
if gopath != "" {
gopath = string(filepath.ListSeparator) + gopath
}
gopath = srcDir + gopath
cfg.Env = append(os.Environ(), "GOPATH="+gopath)
allPkg, err = packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatal(err)
}
}
typePkgs := make([]*types.Package, len(allPkg))
astPkgs := make([][]*ast.File, len(allPkg))
for i, pkg := range allPkg {
// Ignore pkg.Errors. pkg.Errors can exist when Cgo is used, but this should not affect the result.
// See the discussion at golang/go#36547.
typePkgs[i] = pkg.Types
astPkgs[i] = pkg.Syntax
}
for _, l := range langs {
for i, pkg := range typePkgs {
genPkg(l, pkg, astPkgs[i], typePkgs, classes, otypes)
}
// Generate the error package and support files
genPkg(l, nil, nil, typePkgs, classes, otypes)
}
}
var exitStatus = 0
func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprintln(os.Stderr)
exitStatus = 1
}