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  }