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  }