blob: e4eeb08aa34d425ed59f0cc34b98e10fb3ccf2f0 [file] [log] [blame]
// 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/ast"
"go/build"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/mobile/internal/importers"
"golang.org/x/mobile/internal/importers/java"
)
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.")
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.")
)
var usage = `The Gobind tool generates Java language bindings for Go.
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)
}
oldCtx := build.Default
ctx := &build.Default
var allPkg []*build.Package
for _, path := range flag.Args() {
pkg, err := ctx.Import(path, ".", build.ImportComment)
if err != nil {
log.Fatalf("package %q: %v", path, err)
}
allPkg = append(allPkg, pkg)
}
var classes []*java.Class
refs, err := importers.AnalyzePackages(allPkg, "Java/")
if err != nil {
log.Fatal(err)
}
if len(refs.Refs) > 0 {
imp := &java.Importer{
Bootclasspath: *bootclasspath,
Classpath: *classpath,
JavaPkg: *javaPkg,
}
classes, err = imp.Import(refs)
if err != nil {
log.Fatal(err)
}
if len(classes) > 0 {
tmpGopath, 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 {
log.Fatal(err)
}
gopath := ctx.GOPATH
if gopath != "" {
gopath = string(filepath.ListSeparator)
}
ctx.GOPATH = gopath + tmpGopath
}
}
// Make sure the export data for any imported packages are up to date.
cmd := exec.Command("go", "install")
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)
}
}
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.
}
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)
}
tpkg, _ := conf.Check(pkg.Name, fset, files, nil)
typePkgs[i] = tpkg
}
build.Default = oldCtx
for _, pkg := range typePkgs {
genPkg(pkg, typePkgs, classes)
}
// Generate the error package and support files
genPkg(nil, typePkgs, classes)
os.Exit(exitStatus)
}
var exitStatus = 0
func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprintln(os.Stderr)
exitStatus = 1
}