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