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  }