github.com/SkycoinProject/gomobile@v0.0.0-20190312151609-d3739f865fa6/cmd/gomobile/bind.go (about)

     1  // Copyright 2015 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  	"errors"
     9  	"fmt"
    10  	"go/build"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path"
    16  	"path/filepath"
    17  )
    18  
    19  // ctx, pkg, tmpdir in build.go
    20  
    21  var cmdBind = &command{
    22  	run:   runBind,
    23  	Name:  "bind",
    24  	Usage: "[-target android|ios] [-bootclasspath <path>] [-classpath <path>] [-o output] [build flags] [package]",
    25  	Short: "build a library for Android and iOS",
    26  	Long: `
    27  Bind generates language bindings for the package named by the import
    28  path, and compiles a library for the named target system.
    29  
    30  The -target flag takes a target system name, either android (the
    31  default) or ios.
    32  
    33  For -target android, the bind command produces an AAR (Android ARchive)
    34  file that archives the precompiled Java API stub classes, the compiled
    35  shared libraries, and all asset files in the /assets subdirectory under
    36  the package directory. The output is named '<package_name>.aar' by
    37  default. This AAR file is commonly used for binary distribution of an
    38  Android library project and most Android IDEs support AAR import. For
    39  example, in Android Studio (1.2+), an AAR file can be imported using
    40  the module import wizard (File > New > New Module > Import .JAR or
    41  .AAR package), and setting it as a new dependency
    42  (File > Project Structure > Dependencies).  This requires 'javac'
    43  (version 1.7+) and Android SDK (API level 15 or newer) to build the
    44  library for Android. The environment variable ANDROID_HOME must be set
    45  to the path to Android SDK. Use the -javapkg flag to specify the Java
    46  package prefix for the generated classes.
    47  
    48  By default, -target=android builds shared libraries for all supported
    49  instruction sets (arm, arm64, 386, amd64). A subset of instruction sets
    50  can be selected by specifying target type with the architecture name. E.g.,
    51  -target=android/arm,android/386.
    52  
    53  For -target ios, gomobile must be run on an OS X machine with Xcode
    54  installed. The generated Objective-C types can be prefixed with the -prefix
    55  flag.
    56  
    57  For -target android, the -bootclasspath and -classpath flags are used to
    58  control the bootstrap classpath and the classpath for Go wrappers to Java
    59  classes.
    60  
    61  The -v flag provides verbose output, including the list of packages built.
    62  
    63  The build flags -a, -n, -x, -gcflags, -ldflags, -tags, and -work
    64  are shared with the build command. For documentation, see 'go help build'.
    65  `,
    66  }
    67  
    68  func runBind(cmd *command) error {
    69  	cleanup, err := buildEnvInit()
    70  	if err != nil {
    71  		return err
    72  	}
    73  	defer cleanup()
    74  
    75  	args := cmd.flag.Args()
    76  
    77  	targetOS, targetArchs, err := parseBuildTarget(buildTarget)
    78  	if err != nil {
    79  		return fmt.Errorf(`invalid -target=%q: %v`, buildTarget, err)
    80  	}
    81  
    82  	oldCtx := ctx
    83  	defer func() {
    84  		ctx = oldCtx
    85  	}()
    86  	ctx.GOARCH = "arm"
    87  	ctx.GOOS = targetOS
    88  
    89  	if bindJavaPkg != "" && ctx.GOOS != "android" {
    90  		return fmt.Errorf("-javapkg is supported only for android target")
    91  	}
    92  	if bindPrefix != "" && ctx.GOOS != "darwin" {
    93  		return fmt.Errorf("-prefix is supported only for ios target")
    94  	}
    95  
    96  	if ctx.GOOS == "android" {
    97  		if _, err := ndkRoot(); err != nil {
    98  			return err
    99  		}
   100  	}
   101  
   102  	if ctx.GOOS == "darwin" {
   103  		ctx.BuildTags = append(ctx.BuildTags, "ios")
   104  	}
   105  
   106  	var gobind string
   107  	if !buildN {
   108  		gobind, err = exec.LookPath("gobind")
   109  		if err != nil {
   110  			return errors.New("gobind was not found. Please run gomobile init before trying again.")
   111  		}
   112  	} else {
   113  		gobind = "gobind"
   114  	}
   115  
   116  	var pkgs []*build.Package
   117  	switch len(args) {
   118  	case 0:
   119  		pkgs = make([]*build.Package, 1)
   120  		pkgs[0], err = ctx.ImportDir(cwd, build.ImportComment)
   121  	default:
   122  		pkgs, err = importPackages(args)
   123  	}
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	// check if any of the package is main
   129  	for _, pkg := range pkgs {
   130  		if pkg.Name == "main" {
   131  			return fmt.Errorf("binding 'main' package (%s) is not supported", pkg.ImportComment)
   132  		}
   133  	}
   134  
   135  	switch targetOS {
   136  	case "android":
   137  		return goAndroidBind(gobind, pkgs, targetArchs)
   138  	case "darwin":
   139  		if !xcodeAvailable() {
   140  			return fmt.Errorf("-target=ios requires XCode")
   141  		}
   142  		return goIOSBind(gobind, pkgs, targetArchs)
   143  	default:
   144  		return fmt.Errorf(`invalid -target=%q`, buildTarget)
   145  	}
   146  }
   147  
   148  func importPackages(args []string) ([]*build.Package, error) {
   149  	pkgs := make([]*build.Package, len(args))
   150  	for i, a := range args {
   151  		a = path.Clean(a)
   152  		var err error
   153  		if pkgs[i], err = ctx.Import(a, cwd, build.ImportComment); err != nil {
   154  			return nil, fmt.Errorf("package %q: %v", a, err)
   155  		}
   156  	}
   157  	return pkgs, nil
   158  }
   159  
   160  var (
   161  	bindPrefix        string // -prefix
   162  	bindJavaPkg       string // -javapkg
   163  	bindClasspath     string // -classpath
   164  	bindBootClasspath string // -bootclasspath
   165  )
   166  
   167  func init() {
   168  	// bind command specific commands.
   169  	cmdBind.flag.StringVar(&bindJavaPkg, "javapkg", "",
   170  		"specifies custom Java package path prefix. Valid only with -target=android.")
   171  	cmdBind.flag.StringVar(&bindPrefix, "prefix", "",
   172  		"custom Objective-C name prefix. Valid only with -target=ios.")
   173  	cmdBind.flag.StringVar(&bindClasspath, "classpath", "", "The classpath for imported Java classes. Valid only with -target=android.")
   174  	cmdBind.flag.StringVar(&bindBootClasspath, "bootclasspath", "", "The bootstrap classpath for imported Java classes. Valid only with -target=android.")
   175  }
   176  
   177  func bootClasspath() (string, error) {
   178  	if bindBootClasspath != "" {
   179  		return bindBootClasspath, nil
   180  	}
   181  	apiPath, err := androidAPIPath()
   182  	if err != nil {
   183  		return "", err
   184  	}
   185  	return filepath.Join(apiPath, "android.jar"), nil
   186  }
   187  
   188  func copyFile(dst, src string) error {
   189  	if buildX {
   190  		printcmd("cp %s %s", src, dst)
   191  	}
   192  	return writeFile(dst, func(w io.Writer) error {
   193  		if buildN {
   194  			return nil
   195  		}
   196  		f, err := os.Open(src)
   197  		if err != nil {
   198  			return err
   199  		}
   200  		defer f.Close()
   201  
   202  		if _, err := io.Copy(w, f); err != nil {
   203  			return fmt.Errorf("cp %s %s failed: %v", src, dst, err)
   204  		}
   205  		return nil
   206  	})
   207  }
   208  
   209  func writeFile(filename string, generate func(io.Writer) error) error {
   210  	if buildV {
   211  		fmt.Fprintf(os.Stderr, "write %s\n", filename)
   212  	}
   213  
   214  	err := mkdir(filepath.Dir(filename))
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	if buildN {
   220  		return generate(ioutil.Discard)
   221  	}
   222  
   223  	f, err := os.Create(filename)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	defer func() {
   228  		if cerr := f.Close(); err == nil {
   229  			err = cerr
   230  		}
   231  	}()
   232  
   233  	return generate(f)
   234  }