// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bytes" "fmt" "go/build" "go/token" "go/types" "io" "io/ioutil" "os" "path/filepath" "strings" "unicode" "unicode/utf8" "golang.org/x/mobile/bind" "golang.org/x/mobile/internal/importers" "golang.org/x/mobile/internal/importers/java" "golang.org/x/mobile/internal/importers/objc" ) func genPkg(lang string, p *types.Package, allPkg []*types.Package, classes []*java.Class, otypes []*objc.Named) { fname := defaultFileName(lang, p) conf := &bind.GeneratorConfig{ Fset: fset, Pkg: p, AllPkg: allPkg, } var pname string if p != nil { pname = p.Name() } else { pname = "universe" } switch lang { case "java": var buf bytes.Buffer g := &bind.JavaGen{ JavaPkg: *javaPkg, Generator: &bind.Generator{ Printer: &bind.Printer{Buf: &buf, IndentEach: []byte(" ")}, Fset: conf.Fset, AllPkg: conf.AllPkg, Pkg: conf.Pkg, }, } g.Init(classes) pkgname := bind.JavaPkgName(*javaPkg, p) pkgDir := strings.Replace(pkgname, ".", "/", -1) buf.Reset() w, closer := writer(filepath.Join("java", pkgDir, fname)) processErr(g.GenJava()) io.Copy(w, &buf) closer() for i, name := range g.ClassNames() { buf.Reset() w, closer := writer(filepath.Join("java", pkgDir, name+".java")) processErr(g.GenClass(i)) io.Copy(w, &buf) closer() } buf.Reset() w, closer = writer(filepath.Join("src", "gobind", pname+"_android.c")) processErr(g.GenC()) io.Copy(w, &buf) closer() buf.Reset() w, closer = writer(filepath.Join("src", "gobind", pname+"_android.h")) processErr(g.GenH()) io.Copy(w, &buf) closer() // Generate support files along with the universe package if p == nil { p, err := build.Default.Import("golang.org/x/mobile/bind", ".", build.ImportComment) if err != nil { errorf(`"golang.org/x/mobile/bind" is not found; run go get golang.org/x/mobile/bind: %v`, err) return } repo := filepath.Clean(filepath.Join(p.Dir, "..")) // golang.org/x/mobile directory. for _, javaFile := range []string{"Seq.java", "LoadJNI.java"} { src := filepath.Join(repo, "bind/java/"+javaFile) in, err := os.Open(src) if err != nil { errorf("failed to open Java support file: %v", err) } defer in.Close() w, closer := writer(filepath.Join("java", "go", javaFile)) defer closer() if _, err := io.Copy(w, in); err != nil { errorf("failed to copy Java support file: %v", err) return } } // Copy support files javaPkg, err := build.Default.Import("golang.org/x/mobile/bind/java", "", build.FindOnly) if err != nil { errorf("unable to import bind/java: %v", err) return } copyFile(filepath.Join("src", "gobind", "seq_android.c"), filepath.Join(javaPkg.Dir, "seq_android.c.support")) copyFile(filepath.Join("src", "gobind", "seq_android.go"), filepath.Join(javaPkg.Dir, "seq_android.go.support")) copyFile(filepath.Join("src", "gobind", "seq_android.h"), filepath.Join(javaPkg.Dir, "seq_android.h")) } case "go": w, closer := writer(filepath.Join("src", "gobind", fname)) conf.Writer = w processErr(bind.GenGo(conf)) closer() var buf bytes.Buffer w, closer = writer(filepath.Join("src", "gobind", pname+".h")) genPkgH(w, pname) io.Copy(w, &buf) closer() w, closer = writer(filepath.Join("src", "gobind", "seq.h")) genPkgH(w, "seq") io.Copy(w, &buf) closer() bindPkg, err := build.Default.Import("golang.org/x/mobile/bind", "", build.FindOnly) if err != nil { errorf("unable to import bind: %v", err) return } copyFile(filepath.Join("src", "gobind", "seq.go"), filepath.Join(bindPkg.Dir, "seq.go.support")) case "objc": var buf bytes.Buffer g := &bind.ObjcGen{ Generator: &bind.Generator{ Printer: &bind.Printer{Buf: &buf, IndentEach: []byte("\t")}, Fset: conf.Fset, AllPkg: conf.AllPkg, Pkg: conf.Pkg, }, Prefix: *prefix, } g.Init(otypes) w, closer := writer(filepath.Join("src", "gobind", pname+"_darwin.h")) processErr(g.GenGoH()) io.Copy(w, &buf) closer() hname := strings.Title(fname[:len(fname)-2]) + ".objc.h" w, closer = writer(filepath.Join("src", "gobind", hname)) processErr(g.GenH()) io.Copy(w, &buf) closer() mname := strings.Title(fname[:len(fname)-2]) + "_darwin.m" w, closer = writer(filepath.Join("src", "gobind", mname)) conf.Writer = w processErr(g.GenM()) io.Copy(w, &buf) closer() if p == nil { // Copy support files objcPkg, err := build.Default.Import("golang.org/x/mobile/bind/objc", "", build.FindOnly) if err != nil { errorf("unable to import bind/objc: %v", err) return } copyFile(filepath.Join("src", "gobind", "seq_darwin.m"), filepath.Join(objcPkg.Dir, "seq_darwin.m.support")) copyFile(filepath.Join("src", "gobind", "seq_darwin.go"), filepath.Join(objcPkg.Dir, "seq_darwin.go.support")) copyFile(filepath.Join("src", "gobind", "ref.h"), filepath.Join(objcPkg.Dir, "ref.h")) copyFile(filepath.Join("src", "gobind", "seq_darwin.h"), filepath.Join(objcPkg.Dir, "seq_darwin.h")) } default: errorf("unknown target language: %q", lang) } } func genPkgH(w io.Writer, pname string) { fmt.Fprintf(w, `// Code generated by gobind. DO NOT EDIT. #ifdef __GOBIND_ANDROID__ #include "%[1]s_android.h" #endif #ifdef __GOBIND_DARWIN__ #include "%[1]s_darwin.h" #endif`, pname) } func genObjcPackages(dir string, types []*objc.Named, embedders []importers.Struct) error { var buf bytes.Buffer cg := &bind.ObjcWrapper{ Printer: &bind.Printer{ IndentEach: []byte("\t"), Buf: &buf, }, } var genNames []string for _, emb := range embedders { genNames = append(genNames, emb.Name) } cg.Init(types, genNames) for i, opkg := range cg.Packages() { pkgDir := filepath.Join(dir, "src", "ObjC", opkg) if err := os.MkdirAll(pkgDir, 0700); err != nil { return err } pkgFile := filepath.Join(pkgDir, "package.go") buf.Reset() cg.GenPackage(i) if err := ioutil.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil { return err } } buf.Reset() cg.GenInterfaces() objcBase := filepath.Join(dir, "src", "ObjC") if err := os.MkdirAll(objcBase, 0700); err != nil { return err } if err := ioutil.WriteFile(filepath.Join(objcBase, "interfaces.go"), buf.Bytes(), 0600); err != nil { return err } goBase := filepath.Join(dir, "src", "gobind") if err := os.MkdirAll(goBase, 0700); err != nil { return err } buf.Reset() cg.GenGo() if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces_darwin.go"), buf.Bytes(), 0600); err != nil { return err } buf.Reset() cg.GenH() if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces.h"), buf.Bytes(), 0600); err != nil { return err } buf.Reset() cg.GenM() if err := ioutil.WriteFile(filepath.Join(goBase, "interfaces_darwin.m"), buf.Bytes(), 0600); err != nil { return err } return nil } func genJavaPackages(dir string, classes []*java.Class, embedders []importers.Struct) error { var buf bytes.Buffer cg := &bind.ClassGen{ JavaPkg: *javaPkg, Printer: &bind.Printer{ IndentEach: []byte("\t"), Buf: &buf, }, } cg.Init(classes, embedders) for i, jpkg := range cg.Packages() { pkgDir := filepath.Join(dir, "src", "Java", jpkg) if err := os.MkdirAll(pkgDir, 0700); err != nil { return err } pkgFile := filepath.Join(pkgDir, "package.go") buf.Reset() cg.GenPackage(i) if err := ioutil.WriteFile(pkgFile, buf.Bytes(), 0600); err != nil { return err } } buf.Reset() cg.GenInterfaces() javaBase := filepath.Join(dir, "src", "Java") if err := os.MkdirAll(javaBase, 0700); err != nil { return err } if err := ioutil.WriteFile(filepath.Join(javaBase, "interfaces.go"), buf.Bytes(), 0600); err != nil { return err } goBase := filepath.Join(dir, "src", "gobind") if err := os.MkdirAll(goBase, 0700); err != nil { return err } buf.Reset() cg.GenGo() if err := ioutil.WriteFile(filepath.Join(goBase, "classes_android.go"), buf.Bytes(), 0600); err != nil { return err } buf.Reset() cg.GenH() if err := ioutil.WriteFile(filepath.Join(goBase, "classes.h"), buf.Bytes(), 0600); err != nil { return err } buf.Reset() cg.GenC() if err := ioutil.WriteFile(filepath.Join(goBase, "classes_android.c"), buf.Bytes(), 0600); err != nil { return err } return nil } func processErr(err error) { if err != nil { if list, _ := err.(bind.ErrorList); len(list) > 0 { for _, err := range list { errorf("%v", err) } } else { errorf("%v", err) } } } var fset = token.NewFileSet() func writer(fname string) (w io.Writer, closer func()) { if *outdir == "" { return os.Stdout, func() { return } } name := filepath.Join(*outdir, fname) dir := filepath.Dir(name) if err := os.MkdirAll(dir, 0755); err != nil { errorf("invalid output dir: %v", err) os.Exit(exitStatus) } f, err := os.Create(name) if err != nil { errorf("invalid output dir: %v", err) os.Exit(exitStatus) } closer = func() { if err := f.Close(); err != nil { errorf("error in closing output file: %v", err) } } return f, closer } func copyFile(dst, src string) { w, closer := writer(dst) f, err := os.Open(src) if err != nil { errorf("unable to open file: %v", err) closer() os.Exit(exitStatus) } if _, err := io.Copy(w, f); err != nil { errorf("unable to copy file: %v", err) f.Close() closer() os.Exit(exitStatus) } f.Close() closer() } func defaultFileName(lang string, pkg *types.Package) string { switch lang { case "java": if pkg == nil { return "Universe.java" } firstRune, size := utf8.DecodeRuneInString(pkg.Name()) className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] return className + ".java" case "go": if pkg == nil { return "go_main.go" } return "go_" + pkg.Name() + "main.go" case "objc": if pkg == nil { return "Universe.m" } firstRune, size := utf8.DecodeRuneInString(pkg.Name()) className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] return className + ".m" } errorf("unknown target language: %q", lang) os.Exit(exitStatus) return "" }