gen.go 10 KB

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