// 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 }