github.com/champo/mobile@v0.0.0-20190107162257-dc0771356504/cmd/gobind/main.go (about)

     1  // Copyright 2014 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/build"
    13  	"go/importer"
    14  	"go/parser"
    15  	"go/types"
    16  	"io/ioutil"
    17  	"log"
    18  	"os"
    19  	"os/exec"
    20  	"path/filepath"
    21  	"strings"
    22  
    23  	"golang.org/x/mobile/internal/importers"
    24  	"golang.org/x/mobile/internal/importers/java"
    25  	"golang.org/x/mobile/internal/importers/objc"
    26  )
    27  
    28  var (
    29  	lang          = flag.String("lang", "", "target languages for bindings, either java, go, or objc. If empty, all languages are generated.")
    30  	outdir        = flag.String("outdir", "", "result will be written to the directory instead of stdout.")
    31  	javaPkg       = flag.String("javapkg", "", "custom Java package path prefix. Valid only with -lang=java.")
    32  	prefix        = flag.String("prefix", "", "custom Objective-C name prefix. Valid only with -lang=objc.")
    33  	bootclasspath = flag.String("bootclasspath", "", "Java bootstrap classpath.")
    34  	classpath     = flag.String("classpath", "", "Java classpath.")
    35  	tags          = flag.String("tags", "", "build tags.")
    36  )
    37  
    38  var usage = `The Gobind tool generates Java language bindings for Go.
    39  
    40  For usage details, see doc.go.`
    41  
    42  func main() {
    43  	flag.Parse()
    44  
    45  	run()
    46  	os.Exit(exitStatus)
    47  }
    48  
    49  func run() {
    50  	var langs []string
    51  	if *lang != "" {
    52  		langs = strings.Split(*lang, ",")
    53  	} else {
    54  		langs = []string{"go", "java", "objc"}
    55  	}
    56  	ctx := build.Default
    57  	if *tags != "" {
    58  		ctx.BuildTags = append(ctx.BuildTags, strings.Split(*tags, ",")...)
    59  	}
    60  	var allPkg []*build.Package
    61  	for _, path := range flag.Args() {
    62  		pkg, err := ctx.Import(path, ".", build.ImportComment)
    63  		if err != nil {
    64  			log.Fatalf("package %q: %v", path, err)
    65  		}
    66  		allPkg = append(allPkg, pkg)
    67  	}
    68  	jrefs, err := importers.AnalyzePackages(allPkg, "Java/")
    69  	if err != nil {
    70  		log.Fatal(err)
    71  	}
    72  	orefs, err := importers.AnalyzePackages(allPkg, "ObjC/")
    73  	if err != nil {
    74  		log.Fatal(err)
    75  	}
    76  	var classes []*java.Class
    77  	if len(jrefs.Refs) > 0 {
    78  		jimp := &java.Importer{
    79  			Bootclasspath: *bootclasspath,
    80  			Classpath:     *classpath,
    81  			JavaPkg:       *javaPkg,
    82  		}
    83  		classes, err = jimp.Import(jrefs)
    84  		if err != nil {
    85  			log.Fatal(err)
    86  		}
    87  	}
    88  	var otypes []*objc.Named
    89  	if len(orefs.Refs) > 0 {
    90  		otypes, err = objc.Import(orefs)
    91  		if err != nil {
    92  			log.Fatal(err)
    93  		}
    94  	}
    95  	// Determine GOPATH from go env GOPATH in case the default $HOME/go GOPATH
    96  	// is in effect.
    97  	if out, err := exec.Command("go", "env", "GOPATH").Output(); err != nil {
    98  		log.Fatal(err)
    99  	} else {
   100  		ctx.GOPATH = string(bytes.TrimSpace(out))
   101  	}
   102  	if len(classes) > 0 || len(otypes) > 0 {
   103  		// After generation, reverse bindings needs to be in the GOPATH
   104  		// for user packages to build.
   105  		srcDir := *outdir
   106  		if srcDir == "" {
   107  			srcDir, err = ioutil.TempDir(os.TempDir(), "gobind-")
   108  			if err != nil {
   109  				log.Fatal(err)
   110  			}
   111  			defer os.RemoveAll(srcDir)
   112  		} else {
   113  			srcDir, err = filepath.Abs(srcDir)
   114  			if err != nil {
   115  				log.Fatal(err)
   116  			}
   117  		}
   118  		if ctx.GOPATH != "" {
   119  			ctx.GOPATH = string(filepath.ListSeparator) + ctx.GOPATH
   120  		}
   121  		ctx.GOPATH = srcDir + ctx.GOPATH
   122  		if len(classes) > 0 {
   123  			if err := genJavaPackages(srcDir, classes, jrefs.Embedders); err != nil {
   124  				log.Fatal(err)
   125  			}
   126  		}
   127  		if len(otypes) > 0 {
   128  			if err := genObjcPackages(srcDir, otypes, orefs.Embedders); err != nil {
   129  				log.Fatal(err)
   130  			}
   131  		}
   132  	}
   133  
   134  	typePkgs := make([]*types.Package, len(allPkg))
   135  	astPkgs := make([][]*ast.File, len(allPkg))
   136  	// The "source" go/importer package implicitly uses build.Default.
   137  	oldCtx := build.Default
   138  	build.Default = ctx
   139  	defer func() {
   140  		build.Default = oldCtx
   141  	}()
   142  	imp := importer.For("source", nil)
   143  	for i, pkg := range allPkg {
   144  		var err error
   145  		typePkgs[i], err = imp.Import(pkg.ImportPath)
   146  		if err != nil {
   147  			errorf("%v\n", err)
   148  			return
   149  		}
   150  		astPkgs[i], err = parse(pkg)
   151  		if err != nil {
   152  			errorf("%v\n", err)
   153  			return
   154  		}
   155  	}
   156  	for _, l := range langs {
   157  		for i, pkg := range typePkgs {
   158  			genPkg(l, pkg, astPkgs[i], typePkgs, classes, otypes)
   159  		}
   160  		// Generate the error package and support files
   161  		genPkg(l, nil, nil, typePkgs, classes, otypes)
   162  	}
   163  }
   164  
   165  func parse(pkg *build.Package) ([]*ast.File, error) {
   166  	fileNames := append(append([]string{}, pkg.GoFiles...), pkg.CgoFiles...)
   167  	var files []*ast.File
   168  	for _, name := range fileNames {
   169  		f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, name), nil, parser.ParseComments)
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		files = append(files, f)
   174  	}
   175  	return files, nil
   176  }
   177  
   178  var exitStatus = 0
   179  
   180  func errorf(format string, args ...interface{}) {
   181  	fmt.Fprintf(os.Stderr, format, args...)
   182  	fmt.Fprintln(os.Stderr)
   183  	exitStatus = 1
   184  }