main.go 4.6 KB

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