gen.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. "fmt"
  8. "go/ast"
  9. "go/token"
  10. "go/types"
  11. "io"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. "unicode"
  16. "unicode/utf8"
  17. "golang.org/x/mobile/bind"
  18. "golang.org/x/mobile/internal/importers"
  19. "golang.org/x/mobile/internal/importers/java"
  20. "golang.org/x/mobile/internal/importers/objc"
  21. "golang.org/x/tools/go/packages"
  22. )
  23. func genPkg(lang string, p *types.Package, astFiles []*ast.File, allPkg []*types.Package, classes []*java.Class, otypes []*objc.Named) {
  24. fname := defaultFileName(lang, p)
  25. conf := &bind.GeneratorConfig{
  26. Fset: fset,
  27. Pkg: p,
  28. AllPkg: allPkg,
  29. }
  30. var pname string
  31. if p != nil {
  32. pname = p.Name()
  33. } else {
  34. pname = "universe"
  35. }
  36. var buf bytes.Buffer
  37. generator := &bind.Generator{
  38. Printer: &bind.Printer{Buf: &buf, IndentEach: []byte("\t")},
  39. Fset: conf.Fset,
  40. AllPkg: conf.AllPkg,
  41. Pkg: conf.Pkg,
  42. Files: astFiles,
  43. }
  44. switch lang {
  45. case "java":
  46. g := &bind.JavaGen{
  47. JavaPkg: *javaPkg,
  48. Generator: generator,
  49. }
  50. g.Init(classes)
  51. pkgname := bind.JavaPkgName(*javaPkg, p)
  52. pkgDir := strings.Replace(pkgname, ".", "/", -1)
  53. buf.Reset()
  54. w, closer := writer(filepath.Join("java", pkgDir, fname))
  55. processErr(g.GenJava())
  56. io.Copy(w, &buf)
  57. closer()
  58. for i, name := range g.ClassNames() {
  59. buf.Reset()
  60. w, closer := writer(filepath.Join("java", pkgDir, name+".java"))
  61. processErr(g.GenClass(i))
  62. io.Copy(w, &buf)
  63. closer()
  64. }
  65. buf.Reset()
  66. w, closer = writer(filepath.Join("src", "gobind", pname+"_android.c"))
  67. processErr(g.GenC())
  68. io.Copy(w, &buf)
  69. closer()
  70. buf.Reset()
  71. w, closer = writer(filepath.Join("src", "gobind", pname+"_android.h"))
  72. processErr(g.GenH())
  73. io.Copy(w, &buf)
  74. closer()
  75. // Generate support files along with the universe package
  76. if p == nil {
  77. dir, err := packageDir("golang.org/x/mobile/bind")
  78. if err != nil {
  79. errorf(`"golang.org/x/mobile/bind" is not found; run go get golang.org/x/mobile/bind: %v`, err)
  80. return
  81. }
  82. repo := filepath.Clean(filepath.Join(dir, "..")) // golang.org/x/mobile directory.
  83. for _, javaFile := range []string{"Seq.java"} {
  84. src := filepath.Join(repo, "bind/java/"+javaFile)
  85. in, err := os.Open(src)
  86. if err != nil {
  87. errorf("failed to open Java support file: %v", err)
  88. }
  89. defer in.Close()
  90. w, closer := writer(filepath.Join("java", "go", javaFile))
  91. defer closer()
  92. if _, err := io.Copy(w, in); err != nil {
  93. errorf("failed to copy Java support file: %v", err)
  94. return
  95. }
  96. }
  97. // Copy support files
  98. if err != nil {
  99. errorf("unable to import bind/java: %v", err)
  100. return
  101. }
  102. javaDir, err := packageDir("golang.org/x/mobile/bind/java")
  103. if err != nil {
  104. errorf("unable to import bind/java: %v", err)
  105. return
  106. }
  107. copyFile(filepath.Join("src", "gobind", "seq_android.c"), filepath.Join(javaDir, "seq_android.c.support"))
  108. copyFile(filepath.Join("src", "gobind", "seq_android.go"), filepath.Join(javaDir, "seq_android.go.support"))
  109. copyFile(filepath.Join("src", "gobind", "seq_android.h"), filepath.Join(javaDir, "seq_android.h"))
  110. }
  111. case "go":
  112. w, closer := writer(filepath.Join("src", "gobind", fname))
  113. conf.Writer = w
  114. processErr(bind.GenGo(conf))
  115. closer()
  116. w, closer = writer(filepath.Join("src", "gobind", pname+".h"))
  117. genPkgH(w, pname)
  118. io.Copy(w, &buf)
  119. closer()
  120. w, closer = writer(filepath.Join("src", "gobind", "seq.h"))
  121. genPkgH(w, "seq")
  122. io.Copy(w, &buf)
  123. closer()
  124. dir, err := packageDir("golang.org/x/mobile/bind")
  125. if err != nil {
  126. errorf("unable to import bind: %v", err)
  127. return
  128. }
  129. copyFile(filepath.Join("src", "gobind", "seq.go"), filepath.Join(dir, "seq.go.support"))
  130. case "objc":
  131. g := &bind.ObjcGen{
  132. Generator: generator,
  133. Prefix: *prefix,
  134. }
  135. g.Init(otypes)
  136. w, closer := writer(filepath.Join("src", "gobind", pname+"_darwin.h"))
  137. processErr(g.GenGoH())
  138. io.Copy(w, &buf)
  139. closer()
  140. hname := strings.Title(fname[:len(fname)-2]) + ".objc.h"
  141. w, closer = writer(filepath.Join("src", "gobind", hname))
  142. processErr(g.GenH())
  143. io.Copy(w, &buf)
  144. closer()
  145. mname := strings.Title(fname[:len(fname)-2]) + "_darwin.m"
  146. w, closer = writer(filepath.Join("src", "gobind", mname))
  147. conf.Writer = w
  148. processErr(g.GenM())
  149. io.Copy(w, &buf)
  150. closer()
  151. if p == nil {
  152. // Copy support files
  153. dir, err := packageDir("golang.org/x/mobile/bind/objc")
  154. if err != nil {
  155. errorf("unable to import bind/objc: %v", err)
  156. return
  157. }
  158. copyFile(filepath.Join("src", "gobind", "seq_darwin.m"), filepath.Join(dir, "seq_darwin.m.support"))
  159. copyFile(filepath.Join("src", "gobind", "seq_darwin.go"), filepath.Join(dir, "seq_darwin.go.support"))
  160. copyFile(filepath.Join("src", "gobind", "ref.h"), filepath.Join(dir, "ref.h"))
  161. copyFile(filepath.Join("src", "gobind", "seq_darwin.h"), filepath.Join(dir, "seq_darwin.h"))
  162. }
  163. default:
  164. errorf("unknown target language: %q", lang)
  165. }
  166. }
  167. func genPkgH(w io.Writer, pname string) {
  168. fmt.Fprintf(w, `// Code generated by gobind. DO NOT EDIT.
  169. #ifdef __GOBIND_ANDROID__
  170. #include "%[1]s_android.h"
  171. #endif
  172. #ifdef __GOBIND_DARWIN__
  173. #include "%[1]s_darwin.h"
  174. #endif`, pname)
  175. }
  176. func genObjcPackages(dir string, types []*objc.Named, embedders []importers.Struct) error {
  177. var buf bytes.Buffer
  178. cg := &bind.ObjcWrapper{
  179. Printer: &bind.Printer{
  180. IndentEach: []byte("\t"),
  181. Buf: &buf,
  182. },
  183. }
  184. var genNames []string
  185. for _, emb := range embedders {
  186. genNames = append(genNames, emb.Name)
  187. }
  188. cg.Init(types, genNames)
  189. for i, opkg := range cg.Packages() {
  190. pkgDir := filepath.Join(dir, "src", "ObjC", opkg)
  191. if err := os.MkdirAll(pkgDir, 0700); err != nil {
  192. return err
  193. }
  194. pkgFile := filepath.Join(pkgDir, "package.go")
  195. buf.Reset()
  196. cg.GenPackage(i)
  197. if err := os.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil {
  198. return err
  199. }
  200. }
  201. buf.Reset()
  202. cg.GenInterfaces()
  203. objcBase := filepath.Join(dir, "src", "ObjC")
  204. if err := os.MkdirAll(objcBase, 0700); err != nil {
  205. return err
  206. }
  207. if err := os.WriteFile(filepath.Join(objcBase, "interfaces.go"), buf.Bytes(), 0600); err != nil {
  208. return err
  209. }
  210. goBase := filepath.Join(dir, "src", "gobind")
  211. if err := os.MkdirAll(goBase, 0700); err != nil {
  212. return err
  213. }
  214. buf.Reset()
  215. cg.GenGo()
  216. if err := os.WriteFile(filepath.Join(goBase, "interfaces_darwin.go"), buf.Bytes(), 0600); err != nil {
  217. return err
  218. }
  219. buf.Reset()
  220. cg.GenH()
  221. if err := os.WriteFile(filepath.Join(goBase, "interfaces.h"), buf.Bytes(), 0600); err != nil {
  222. return err
  223. }
  224. buf.Reset()
  225. cg.GenM()
  226. if err := os.WriteFile(filepath.Join(goBase, "interfaces_darwin.m"), buf.Bytes(), 0600); err != nil {
  227. return err
  228. }
  229. return nil
  230. }
  231. func genJavaPackages(dir string, classes []*java.Class, embedders []importers.Struct) error {
  232. var buf bytes.Buffer
  233. cg := &bind.ClassGen{
  234. JavaPkg: *javaPkg,
  235. Printer: &bind.Printer{
  236. IndentEach: []byte("\t"),
  237. Buf: &buf,
  238. },
  239. }
  240. cg.Init(classes, embedders)
  241. for i, jpkg := range cg.Packages() {
  242. pkgDir := filepath.Join(dir, "src", "Java", jpkg)
  243. if err := os.MkdirAll(pkgDir, 0700); err != nil {
  244. return err
  245. }
  246. pkgFile := filepath.Join(pkgDir, "package.go")
  247. buf.Reset()
  248. cg.GenPackage(i)
  249. if err := os.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil {
  250. return err
  251. }
  252. }
  253. buf.Reset()
  254. cg.GenInterfaces()
  255. javaBase := filepath.Join(dir, "src", "Java")
  256. if err := os.MkdirAll(javaBase, 0700); err != nil {
  257. return err
  258. }
  259. if err := os.WriteFile(filepath.Join(javaBase, "interfaces.go"), buf.Bytes(), 0600); err != nil {
  260. return err
  261. }
  262. goBase := filepath.Join(dir, "src", "gobind")
  263. if err := os.MkdirAll(goBase, 0700); err != nil {
  264. return err
  265. }
  266. buf.Reset()
  267. cg.GenGo()
  268. if err := os.WriteFile(filepath.Join(goBase, "classes_android.go"), buf.Bytes(), 0600); err != nil {
  269. return err
  270. }
  271. buf.Reset()
  272. cg.GenH()
  273. if err := os.WriteFile(filepath.Join(goBase, "classes.h"), buf.Bytes(), 0600); err != nil {
  274. return err
  275. }
  276. buf.Reset()
  277. cg.GenC()
  278. if err := os.WriteFile(filepath.Join(goBase, "classes_android.c"), buf.Bytes(), 0600); err != nil {
  279. return err
  280. }
  281. return nil
  282. }
  283. func processErr(err error) {
  284. if err != nil {
  285. if list, _ := err.(bind.ErrorList); len(list) > 0 {
  286. for _, err := range list {
  287. errorf("%v", err)
  288. }
  289. } else {
  290. errorf("%v", err)
  291. }
  292. }
  293. }
  294. var fset = token.NewFileSet()
  295. func writer(fname string) (w io.Writer, closer func()) {
  296. if *outdir == "" {
  297. return os.Stdout, func() { return }
  298. }
  299. name := filepath.Join(*outdir, fname)
  300. dir := filepath.Dir(name)
  301. if err := os.MkdirAll(dir, 0755); err != nil {
  302. errorf("invalid output dir: %v", err)
  303. os.Exit(exitStatus)
  304. }
  305. f, err := os.Create(name)
  306. if err != nil {
  307. errorf("invalid output dir: %v", err)
  308. os.Exit(exitStatus)
  309. }
  310. closer = func() {
  311. if err := f.Close(); err != nil {
  312. errorf("error in closing output file: %v", err)
  313. }
  314. }
  315. return f, closer
  316. }
  317. func copyFile(dst, src string) {
  318. w, closer := writer(dst)
  319. f, err := os.Open(src)
  320. if err != nil {
  321. errorf("unable to open file: %v", err)
  322. closer()
  323. os.Exit(exitStatus)
  324. }
  325. if _, err := io.Copy(w, f); err != nil {
  326. errorf("unable to copy file: %v", err)
  327. f.Close()
  328. closer()
  329. os.Exit(exitStatus)
  330. }
  331. f.Close()
  332. closer()
  333. }
  334. func defaultFileName(lang string, pkg *types.Package) string {
  335. switch lang {
  336. case "java":
  337. if pkg == nil {
  338. return "Universe.java"
  339. }
  340. firstRune, size := utf8.DecodeRuneInString(pkg.Name())
  341. className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:]
  342. return className + ".java"
  343. case "go":
  344. if pkg == nil {
  345. return "go_main.go"
  346. }
  347. return "go_" + pkg.Name() + "main.go"
  348. case "objc":
  349. if pkg == nil {
  350. return "Universe.m"
  351. }
  352. firstRune, size := utf8.DecodeRuneInString(pkg.Name())
  353. className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:]
  354. return *prefix + className + ".m"
  355. }
  356. errorf("unknown target language: %q", lang)
  357. os.Exit(exitStatus)
  358. return ""
  359. }
  360. func packageDir(path string) (string, error) {
  361. mode := packages.NeedFiles
  362. pkgs, err := packages.Load(&packages.Config{Mode: mode}, path)
  363. if err != nil {
  364. return "", err
  365. }
  366. if len(pkgs) == 0 || len(pkgs[0].GoFiles) == 0 {
  367. return "", fmt.Errorf("no Go package in %v", path)
  368. }
  369. pkg := pkgs[0]
  370. if len(pkg.Errors) > 0 {
  371. return "", fmt.Errorf("%v", pkg.Errors)
  372. }
  373. return filepath.Dir(pkg.GoFiles[0]), nil
  374. }