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