2
0
mirror of synced 2025-02-23 23:08:14 +00:00
mobile/cmd/gobind/main.go
Elias Naur 002e3c5c61 cmd/gobind: copy documentation to generated source
CL 99316 moved generation of bindings from the the gomobile command
to the gobind command. In the process, the ability to copy over
documentation from the Go source to the derived Java and ObjC was
lost. The relevant test didn't fail because it tests the generator
itself, not gobind.

Re-add support and add a gobind test for it.

Fixes golang/go#25473

Change-Id: I6eee3e79173f37d3e3e65eabc0bad59e4252da64
Reviewed-on: https://go-review.googlesource.com/114056
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2018-05-22 19:36:14 +00:00

185 lines
4.6 KiB
Go

// 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/build"
"go/importer"
"go/parser"
"go/types"
"io/ioutil"
"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"
)
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"}
}
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)
if err != nil {
log.Fatalf("package %q: %v", path, err)
}
allPkg = append(allPkg, pkg)
}
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)
}
}
// Determine GOPATH from go env GOPATH in case the default $HOME/go GOPATH
// is in effect.
if out, err := exec.Command("go", "env", "GOPATH").Output(); err != nil {
log.Fatal(err)
} else {
ctx.GOPATH = string(bytes.TrimSpace(out))
}
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(srcDir)
} else {
srcDir, err = filepath.Abs(srcDir)
if err != nil {
log.Fatal(err)
}
}
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)
}
}
}
typePkgs := make([]*types.Package, len(allPkg))
astPkgs := make([][]*ast.File, len(allPkg))
// The "source" go/importer package implicitly uses build.Default.
oldCtx := build.Default
build.Default = ctx
defer func() {
build.Default = oldCtx
}()
imp := importer.For("source", nil)
for i, pkg := range allPkg {
var err error
typePkgs[i], err = imp.Import(pkg.ImportPath)
if err != nil {
errorf("%v\n", err)
return
}
astPkgs[i], err = parse(pkg)
if err != nil {
errorf("%v\n", err)
return
}
}
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)
}
}
func parse(pkg *build.Package) ([]*ast.File, error) {
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)
}
return files, nil
}
var exitStatus = 0
func errorf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
fmt.Fprintln(os.Stderr)
exitStatus = 1
}