github.com/danbrough/mobile@v0.0.3-beta03/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/token" 12 "go/types" 13 "io" 14 "io/ioutil" 15 "os" 16 "path/filepath" 17 "strings" 18 "unicode" 19 "unicode/utf8" 20 21 "github.com/danbrough/mobile/bind" 22 "github.com/danbrough/mobile/internal/importers" 23 "github.com/danbrough/mobile/internal/importers/java" 24 "github.com/danbrough/mobile/internal/importers/objc" 25 "golang.org/x/tools/go/packages" 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+"_support.c")) 73 processErr(g.GenC()) 74 io.Copy(w, &buf) 75 closer() 76 buf.Reset() 77 w, closer = writer(filepath.Join("src", "gobind", pname+"_support.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 dir, err := packageDir("github.com/danbrough/mobile/bind") 84 if err != nil { 85 errorf(`"github.com/danbrough/mobile/bind" is not found; run go get github.com/danbrough/mobile/bind: %v`, err) 86 return 87 } 88 repo := filepath.Clean(filepath.Join(dir, "..")) // github.com/danbrough/mobile directory. 89 for _, javaFile := range []string{"Seq.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 if err != nil { 105 errorf("unable to import bind/java: %v", err) 106 return 107 } 108 javaDir, err := packageDir("github.com/danbrough/mobile/bind/java") 109 if err != nil { 110 errorf("unable to import bind/java: %v", err) 111 return 112 } 113 copyFile(filepath.Join("src", "gobind", "seq_support.c"), filepath.Join(javaDir, "seq_support.c.support")) 114 copyFile(filepath.Join("src", "gobind", "seq_support.go"), filepath.Join(javaDir, "seq_support.go.support")) 115 /* copyFile(filepath.Join("src", "gobind", "seq_linux.c"), filepath.Join(javaDir, "seq_linux.c.support")) 116 copyFile(filepath.Join("src", "gobind", "seq_linux.go"), filepath.Join(javaDir, "seq_linux.go.support"))*/ 117 copyFile(filepath.Join("src", "gobind", "seq_support.h"), filepath.Join(javaDir, "seq_support.h")) 118 } 119 case "go": 120 w, closer := writer(filepath.Join("src", "gobind", fname)) 121 conf.Writer = w 122 processErr(bind.GenGo(conf)) 123 closer() 124 w, closer = writer(filepath.Join("src", "gobind", pname+".h")) 125 genPkgH(w, pname) 126 io.Copy(w, &buf) 127 closer() 128 w, closer = writer(filepath.Join("src", "gobind", "seq.h")) 129 genPkgH(w, "seq") 130 io.Copy(w, &buf) 131 closer() 132 dir, err := packageDir("github.com/danbrough/mobile/bind") 133 if err != nil { 134 errorf("unable to import bind: %v", err) 135 return 136 } 137 copyFile(filepath.Join("src", "gobind", "seq.go"), filepath.Join(dir, "seq.go.support")) 138 case "objc": 139 g := &bind.ObjcGen{ 140 Generator: generator, 141 Prefix: *prefix, 142 } 143 g.Init(otypes) 144 w, closer := writer(filepath.Join("src", "gobind", pname+"_darwin.h")) 145 processErr(g.GenGoH()) 146 io.Copy(w, &buf) 147 closer() 148 hname := strings.Title(fname[:len(fname)-2]) + ".objc.h" 149 w, closer = writer(filepath.Join("src", "gobind", hname)) 150 processErr(g.GenH()) 151 io.Copy(w, &buf) 152 closer() 153 mname := strings.Title(fname[:len(fname)-2]) + "_darwin.m" 154 w, closer = writer(filepath.Join("src", "gobind", mname)) 155 conf.Writer = w 156 processErr(g.GenM()) 157 io.Copy(w, &buf) 158 closer() 159 if p == nil { 160 // Copy support files 161 dir, err := packageDir("github.com/danbrough/mobile/bind/objc") 162 if err != nil { 163 errorf("unable to import bind/objc: %v", err) 164 return 165 } 166 copyFile(filepath.Join("src", "gobind", "seq_darwin.m"), filepath.Join(dir, "seq_darwin.m.support")) 167 copyFile(filepath.Join("src", "gobind", "seq_darwin.go"), filepath.Join(dir, "seq_darwin.go.support")) 168 copyFile(filepath.Join("src", "gobind", "ref.h"), filepath.Join(dir, "ref.h")) 169 copyFile(filepath.Join("src", "gobind", "seq_darwin.h"), filepath.Join(dir, "seq_darwin.h")) 170 } 171 default: 172 errorf("unknown target language: %q", lang) 173 } 174 } 175 176 func genPkgH(w io.Writer, pname string) { 177 fmt.Fprintf(w, `// Code generated by gobind. DO NOT EDIT. 178 179 #ifdef __GOBIND_ANDROID__ 180 #include "%[1]s_support.h" 181 #else 182 #ifdef __GOBIND_LINUX__ 183 #include "%[1]s_support.h" 184 #endif 185 #ifdef __GOBIND_WINDOWS__ 186 #include "%[1]s_support.h" 187 #endif 188 #endif 189 #ifdef __GOBIND_DARWIN__ 190 #include "%[1]s_darwin.h" 191 #endif`, pname) 192 } 193 194 func genObjcPackages(dir string, types []*objc.Named, embedders []importers.Struct) error { 195 var buf bytes.Buffer 196 cg := &bind.ObjcWrapper{ 197 Printer: &bind.Printer{ 198 IndentEach: []byte("\t"), 199 Buf: &buf, 200 }, 201 } 202 var genNames []string 203 for _, emb := range embedders { 204 genNames = append(genNames, emb.Name) 205 } 206 cg.Init(types, genNames) 207 for i, opkg := range cg.Packages() { 208 pkgDir := filepath.Join(dir, "src", "ObjC", opkg) 209 if err := os.MkdirAll(pkgDir, 0700); err != nil { 210 return err 211 } 212 pkgFile := filepath.Join(pkgDir, "package.go") 213 buf.Reset() 214 cg.GenPackage(i) 215 if err := ioutil.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil { 216 return err 217 } 218 } 219 buf.Reset() 220 cg.GenInterfaces() 221 objcBase := filepath.Join(dir, "src", "ObjC") 222 if err := os.MkdirAll(objcBase, 0700); err != nil { 223 return err 224 } 225 if err := ioutil.WriteFile(filepath.Join(objcBase, "interfaces.go"), buf.Bytes(), 0600); err != nil { 226 return err 227 } 228 goBase := filepath.Join(dir, "src", "gobind") 229 if err := os.MkdirAll(goBase, 0700); err != nil { 230 return err 231 } 232 buf.Reset() 233 cg.GenGo() 234 if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces_darwin.go"), buf.Bytes(), 0600); err != nil { 235 return err 236 } 237 buf.Reset() 238 cg.GenH() 239 if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces.h"), buf.Bytes(), 0600); err != nil { 240 return err 241 } 242 buf.Reset() 243 cg.GenM() 244 if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces_darwin.m"), buf.Bytes(), 0600); err != nil { 245 return err 246 } 247 return nil 248 } 249 250 func genJavaPackages(dir string, classes []*java.Class, embedders []importers.Struct) error { 251 var buf bytes.Buffer 252 cg := &bind.ClassGen{ 253 JavaPkg: *javaPkg, 254 Printer: &bind.Printer{ 255 IndentEach: []byte("\t"), 256 Buf: &buf, 257 }, 258 } 259 cg.Init(classes, embedders) 260 for i, jpkg := range cg.Packages() { 261 pkgDir := filepath.Join(dir, "src", "Java", jpkg) 262 if err := os.MkdirAll(pkgDir, 0700); err != nil { 263 return err 264 } 265 pkgFile := filepath.Join(pkgDir, "package.go") 266 buf.Reset() 267 cg.GenPackage(i) 268 if err := ioutil.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil { 269 return err 270 } 271 } 272 buf.Reset() 273 cg.GenInterfaces() 274 javaBase := filepath.Join(dir, "src", "Java") 275 if err := os.MkdirAll(javaBase, 0700); err != nil { 276 return err 277 } 278 if err := ioutil.WriteFile(filepath.Join(javaBase, "interfaces.go"), buf.Bytes(), 0600); err != nil { 279 return err 280 } 281 goBase := filepath.Join(dir, "src", "gobind") 282 if err := os.MkdirAll(goBase, 0700); err != nil { 283 return err 284 } 285 buf.Reset() 286 cg.GenGo() 287 if err := ioutil.WriteFile(filepath.Join(goBase, "classes_android.go"), buf.Bytes(), 0600); err != nil { 288 return err 289 } 290 buf.Reset() 291 cg.GenH() 292 if err := ioutil.WriteFile(filepath.Join(goBase, "classes.h"), buf.Bytes(), 0600); err != nil { 293 return err 294 } 295 buf.Reset() 296 cg.GenC() 297 if err := ioutil.WriteFile(filepath.Join(goBase, "classes_android.c"), buf.Bytes(), 0600); err != nil { 298 return err 299 } 300 return nil 301 } 302 303 func processErr(err error) { 304 if err != nil { 305 if list, _ := err.(bind.ErrorList); len(list) > 0 { 306 for _, err := range list { 307 errorf("%v", err) 308 } 309 } else { 310 errorf("%v", err) 311 } 312 } 313 } 314 315 var fset = token.NewFileSet() 316 317 func writer(fname string) (w io.Writer, closer func()) { 318 if *outdir == "" { 319 return os.Stdout, func() { return } 320 } 321 322 name := filepath.Join(*outdir, fname) 323 dir := filepath.Dir(name) 324 if err := os.MkdirAll(dir, 0755); err != nil { 325 errorf("invalid output dir: %v", err) 326 os.Exit(exitStatus) 327 } 328 329 f, err := os.Create(name) 330 if err != nil { 331 errorf("invalid output dir: %v", err) 332 os.Exit(exitStatus) 333 } 334 closer = func() { 335 if err := f.Close(); err != nil { 336 errorf("error in closing output file: %v", err) 337 } 338 } 339 return f, closer 340 } 341 342 func copyFile(dst, src string) { 343 w, closer := writer(dst) 344 f, err := os.Open(src) 345 if err != nil { 346 errorf("unable to open file: %v", err) 347 closer() 348 os.Exit(exitStatus) 349 } 350 if _, err := io.Copy(w, f); err != nil { 351 errorf("unable to copy file: %v", err) 352 f.Close() 353 closer() 354 os.Exit(exitStatus) 355 } 356 f.Close() 357 closer() 358 } 359 360 func defaultFileName(lang string, pkg *types.Package) string { 361 switch lang { 362 case "java": 363 if pkg == nil { 364 return "Universe.java" 365 } 366 firstRune, size := utf8.DecodeRuneInString(pkg.Name()) 367 className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] 368 return className + ".java" 369 case "go": 370 if pkg == nil { 371 return "go_main.go" 372 } 373 return "go_" + pkg.Name() + "main.go" 374 case "objc": 375 if pkg == nil { 376 return "Universe.m" 377 } 378 firstRune, size := utf8.DecodeRuneInString(pkg.Name()) 379 className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] 380 return *prefix + className + ".m" 381 } 382 errorf("unknown target language: %q", lang) 383 os.Exit(exitStatus) 384 return "" 385 } 386 387 func packageDir(path string) (string, error) { 388 pkgs, err := packages.Load(nil, path) 389 if err != nil { 390 return "", err 391 } 392 pkg := pkgs[0] 393 if len(pkg.Errors) > 0 { 394 return "", fmt.Errorf("%v", pkg.Errors) 395 } 396 return filepath.Dir(pkg.GoFiles[0]), nil 397 }