github.com/SahandAslani/gomobile@v0.0.0-20210909130135-2cb2d44c09b2/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/types"
    13  	"io/ioutil"
    14  	"log"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"strings"
    19  
    20  	"github.com/SahandAslani/gomobile/internal/importers"
    21  	"github.com/SahandAslani/gomobile/internal/importers/java"
    22  	"github.com/SahandAslani/gomobile/internal/importers/objc"
    23  	"golang.org/x/tools/go/packages"
    24  )
    25  
    26  var (
    27  	lang          = flag.String("lang", "", "target languages for bindings, either java, go, or objc. If empty, all languages are generated.")
    28  	outdir        = flag.String("outdir", "", "result will be written to the directory instead of stdout.")
    29  	javaPkg       = flag.String("javapkg", "", "custom Java package path prefix. Valid only with -lang=java.")
    30  	prefix        = flag.String("prefix", "", "custom Objective-C name prefix. Valid only with -lang=objc.")
    31  	bootclasspath = flag.String("bootclasspath", "", "Java bootstrap classpath.")
    32  	classpath     = flag.String("classpath", "", "Java classpath.")
    33  	tags          = flag.String("tags", "", "build tags.")
    34  )
    35  
    36  var usage = `The Gobind tool generates Java language bindings for Go.
    37  
    38  For usage details, see doc.go.`
    39  
    40  func main() {
    41  	flag.Parse()
    42  
    43  	run()
    44  	os.Exit(exitStatus)
    45  }
    46  
    47  func run() {
    48  	var langs []string
    49  	if *lang != "" {
    50  		langs = strings.Split(*lang, ",")
    51  	} else {
    52  		langs = []string{"go", "java", "objc"}
    53  	}
    54  
    55  	// We need to give appropriate environment variables like CC or CXX so that the returned packages no longer have errors.
    56  	// However, getting such environment variables is difficult or impossible so far.
    57  	// Gomobile can obtain such environment variables in env.go, but this logic assumes some condiitons gobind doesn't assume.
    58  	cfg := &packages.Config{
    59  		Mode: packages.NeedName | packages.NeedFiles |
    60  			packages.NeedImports | packages.NeedDeps |
    61  			packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo,
    62  		BuildFlags: []string{"-tags", strings.Join(strings.Split(*tags, ","), " ")},
    63  	}
    64  
    65  	// Call Load twice to warm the cache. There is a known issue that the result of Load
    66  	// depends on build cache state. See golang/go#33687.
    67  	packages.Load(cfg, flag.Args()...)
    68  
    69  	allPkg, err := packages.Load(cfg, flag.Args()...)
    70  	if err != nil {
    71  		log.Fatal(err)
    72  	}
    73  
    74  	jrefs, err := importers.AnalyzePackages(allPkg, "Java/")
    75  	if err != nil {
    76  		log.Fatal(err)
    77  	}
    78  	orefs, err := importers.AnalyzePackages(allPkg, "ObjC/")
    79  	if err != nil {
    80  		log.Fatal(err)
    81  	}
    82  	var classes []*java.Class
    83  	if len(jrefs.Refs) > 0 {
    84  		jimp := &java.Importer{
    85  			Bootclasspath: *bootclasspath,
    86  			Classpath:     *classpath,
    87  			JavaPkg:       *javaPkg,
    88  		}
    89  		classes, err = jimp.Import(jrefs)
    90  		if err != nil {
    91  			log.Fatal(err)
    92  		}
    93  	}
    94  	var otypes []*objc.Named
    95  	if len(orefs.Refs) > 0 {
    96  		otypes, err = objc.Import(orefs)
    97  		if err != nil {
    98  			log.Fatal(err)
    99  		}
   100  	}
   101  
   102  	if len(classes) > 0 || len(otypes) > 0 {
   103  		srcDir := *outdir
   104  		if srcDir == "" {
   105  			srcDir, err = ioutil.TempDir(os.TempDir(), "gobind-")
   106  			if err != nil {
   107  				log.Fatal(err)
   108  			}
   109  			defer os.RemoveAll(srcDir)
   110  		} else {
   111  			srcDir, err = filepath.Abs(srcDir)
   112  			if err != nil {
   113  				log.Fatal(err)
   114  			}
   115  		}
   116  		if len(classes) > 0 {
   117  			if err := genJavaPackages(srcDir, classes, jrefs.Embedders); err != nil {
   118  				log.Fatal(err)
   119  			}
   120  		}
   121  		if len(otypes) > 0 {
   122  			if err := genObjcPackages(srcDir, otypes, orefs.Embedders); err != nil {
   123  				log.Fatal(err)
   124  			}
   125  		}
   126  
   127  		// Add a new directory to GOPATH where the file for reverse bindings exist, and recreate allPkg.
   128  		// It is because the current allPkg did not solve imports for reverse bindings.
   129  		var gopath string
   130  		if out, err := exec.Command("go", "env", "GOPATH").Output(); err != nil {
   131  			log.Fatal(err)
   132  		} else {
   133  			gopath = string(bytes.TrimSpace(out))
   134  		}
   135  		if gopath != "" {
   136  			gopath = string(filepath.ListSeparator) + gopath
   137  		}
   138  		gopath = srcDir + gopath
   139  		cfg.Env = append(os.Environ(), "GOPATH="+gopath)
   140  		allPkg, err = packages.Load(cfg, flag.Args()...)
   141  		if err != nil {
   142  			log.Fatal(err)
   143  		}
   144  	}
   145  
   146  	typePkgs := make([]*types.Package, len(allPkg))
   147  	astPkgs := make([][]*ast.File, len(allPkg))
   148  	for i, pkg := range allPkg {
   149  		// Ignore pkg.Errors. pkg.Errors can exist when Cgo is used, but this should not affect the result.
   150  		// See the discussion at golang/go#36547.
   151  		typePkgs[i] = pkg.Types
   152  		astPkgs[i] = pkg.Syntax
   153  	}
   154  	for _, l := range langs {
   155  		for i, pkg := range typePkgs {
   156  			genPkg(l, pkg, astPkgs[i], typePkgs, classes, otypes)
   157  		}
   158  		// Generate the error package and support files
   159  		genPkg(l, nil, nil, typePkgs, classes, otypes)
   160  	}
   161  }
   162  
   163  var exitStatus = 0
   164  
   165  func errorf(format string, args ...interface{}) {
   166  	fmt.Fprintf(os.Stderr, format, args...)
   167  	fmt.Fprintln(os.Stderr)
   168  	exitStatus = 1
   169  }