2015-02-11 15:23:09 -05:00
|
|
|
// Copyright 2015 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 (
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/build"
|
2015-11-13 13:57:15 -05:00
|
|
|
"go/importer"
|
2015-02-11 15:23:09 -05:00
|
|
|
"go/token"
|
2015-07-25 09:45:10 -04:00
|
|
|
"go/types"
|
2015-02-11 15:23:09 -05:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2015-11-20 11:26:00 -05:00
|
|
|
"path"
|
2015-02-11 15:23:09 -05:00
|
|
|
"path/filepath"
|
2015-07-14 18:09:25 -04:00
|
|
|
"strings"
|
2015-02-11 15:23:09 -05:00
|
|
|
|
|
|
|
"golang.org/x/mobile/bind"
|
|
|
|
)
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
// ctx, pkg, tmpdir in build.go
|
2015-02-11 15:23:09 -05:00
|
|
|
|
|
|
|
var cmdBind = &command{
|
|
|
|
run: runBind,
|
|
|
|
Name: "bind",
|
2015-06-19 12:27:15 -04:00
|
|
|
Usage: "[-target android|ios] [-o output] [build flags] [package]",
|
2015-08-29 16:27:39 -07:00
|
|
|
Short: "build a library for Android and iOS",
|
2015-02-11 15:23:09 -05:00
|
|
|
Long: `
|
2015-06-19 12:27:15 -04:00
|
|
|
Bind generates language bindings for the package named by the import
|
|
|
|
path, and compiles a library for the named target system.
|
|
|
|
|
|
|
|
The -target flag takes a target system name, either android (the
|
|
|
|
default) or ios.
|
|
|
|
|
|
|
|
For -target android, the bind command produces an AAR (Android ARchive)
|
|
|
|
file that archives the precompiled Java API stub classes, the compiled
|
|
|
|
shared libraries, and all asset files in the /assets subdirectory under
|
|
|
|
the package directory. The output is named '<package_name>.aar' by
|
|
|
|
default. This AAR file is commonly used for binary distribution of an
|
|
|
|
Android library project and most Android IDEs support AAR import. For
|
|
|
|
example, in Android Studio (1.2+), an AAR file can be imported using
|
|
|
|
the module import wizard (File > New > New Module > Import .JAR or
|
|
|
|
.AAR package), and setting it as a new dependency
|
|
|
|
(File > Project Structure > Dependencies). This requires 'javac'
|
|
|
|
(version 1.7+) and Android SDK (API level 9 or newer) to build the
|
|
|
|
library for Android. The environment variable ANDROID_HOME must be set
|
2015-08-28 13:39:30 -04:00
|
|
|
to the path to Android SDK. The generated Java class is in the java
|
|
|
|
package 'go.<package_name>' unless -javapkg flag is specified.
|
2015-06-19 12:27:15 -04:00
|
|
|
|
|
|
|
For -target ios, gomobile must be run on an OS X machine with Xcode
|
2015-08-28 13:39:30 -04:00
|
|
|
installed. Support is not complete. The generated Objective-C types
|
|
|
|
are prefixed with 'Go' unless the -prefix flag is provided.
|
2015-02-11 15:23:09 -05:00
|
|
|
|
|
|
|
The -v flag provides verbose output, including the list of packages built.
|
|
|
|
|
2015-11-30 14:01:47 -05:00
|
|
|
The build flags -a, -n, -x, -gcflags, -ldflags, -tags, and -work
|
2015-07-27 16:33:08 -04:00
|
|
|
are shared with the build command. For documentation, see 'go help build'.
|
2015-02-11 15:23:09 -05:00
|
|
|
`,
|
|
|
|
}
|
|
|
|
|
|
|
|
func runBind(cmd *command) error {
|
2015-07-16 13:32:51 -04:00
|
|
|
cleanup, err := buildEnvInit()
|
2015-02-11 15:23:09 -05:00
|
|
|
if err != nil {
|
2015-07-10 16:47:46 -06:00
|
|
|
return err
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
defer cleanup()
|
|
|
|
|
2015-02-11 15:23:09 -05:00
|
|
|
args := cmd.flag.Args()
|
|
|
|
|
2015-07-24 07:04:40 -04:00
|
|
|
ctx.GOARCH = "arm"
|
|
|
|
switch buildTarget {
|
|
|
|
case "android":
|
|
|
|
ctx.GOOS = "android"
|
|
|
|
case "ios":
|
|
|
|
ctx.GOOS = "darwin"
|
|
|
|
default:
|
|
|
|
return fmt.Errorf(`unknown -target, %q.`, buildTarget)
|
|
|
|
}
|
|
|
|
|
2015-08-28 13:39:30 -04:00
|
|
|
if bindJavaPkg != "" && ctx.GOOS != "android" {
|
|
|
|
return fmt.Errorf("-javapkg is supported only for android target")
|
|
|
|
}
|
|
|
|
if bindPrefix != "" && ctx.GOOS != "darwin" {
|
|
|
|
return fmt.Errorf("-prefix is supported only for ios target")
|
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
var pkgs []*build.Package
|
2015-02-11 15:23:09 -05:00
|
|
|
switch len(args) {
|
|
|
|
case 0:
|
2015-11-19 10:51:29 +05:30
|
|
|
pkgs = make([]*build.Package, 1)
|
|
|
|
pkgs[0], err = ctx.ImportDir(cwd, build.ImportComment)
|
2015-02-11 15:23:09 -05:00
|
|
|
default:
|
2015-11-19 10:51:29 +05:30
|
|
|
pkgs, err = importPackages(args)
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-06-24 15:15:51 -07:00
|
|
|
switch buildTarget {
|
2015-06-19 12:27:15 -04:00
|
|
|
case "android":
|
2015-11-19 10:51:29 +05:30
|
|
|
return goAndroidBind(pkgs)
|
2015-06-19 12:27:15 -04:00
|
|
|
case "ios":
|
2015-11-19 10:51:29 +05:30
|
|
|
return goIOSBind(pkgs)
|
2015-07-24 16:02:39 -04:00
|
|
|
default:
|
|
|
|
return fmt.Errorf(`unknown -target, %q.`, buildTarget)
|
2015-06-19 12:27:15 -04:00
|
|
|
}
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func importPackages(args []string) ([]*build.Package, error) {
|
|
|
|
pkgs := make([]*build.Package, len(args))
|
|
|
|
for i, path := range args {
|
|
|
|
var err error
|
|
|
|
if pkgs[i], err = ctx.Import(path, cwd, build.ImportComment); err != nil {
|
|
|
|
return nil, fmt.Errorf("package %q: %v", path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pkgs, nil
|
|
|
|
}
|
|
|
|
|
2015-08-28 13:39:30 -04:00
|
|
|
var (
|
|
|
|
bindPrefix string // -prefix
|
|
|
|
bindJavaPkg string // -javapkg
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// bind command specific commands.
|
|
|
|
cmdBind.flag.StringVar(&bindJavaPkg, "javapkg", "",
|
|
|
|
"specifies custom Java package path used instead of the default 'go.<go package name>'. Valid only with -target=android.")
|
|
|
|
cmdBind.flag.StringVar(&bindPrefix, "prefix", "",
|
|
|
|
"custom Objective-C name prefix used instead of the default 'Go'. Valid only with -lang=ios.")
|
|
|
|
}
|
|
|
|
|
2015-02-11 15:23:09 -05:00
|
|
|
type binder struct {
|
|
|
|
files []*ast.File
|
|
|
|
fset *token.FileSet
|
2015-11-19 10:51:29 +05:30
|
|
|
pkgs []*types.Package
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
|
2015-12-01 09:37:56 +05:30
|
|
|
func (b *binder) GenObjc(pkg *types.Package, outdir string) (string, error) {
|
2015-10-18 21:37:57 -07:00
|
|
|
const bindPrefixDefault = "Go"
|
|
|
|
if bindPrefix == "" {
|
|
|
|
bindPrefix = bindPrefixDefault
|
|
|
|
}
|
2015-12-01 09:37:56 +05:30
|
|
|
name := strings.Title(pkg.Name())
|
2015-08-28 13:39:30 -04:00
|
|
|
bindOption := "-lang=objc"
|
2015-10-18 21:37:57 -07:00
|
|
|
if bindPrefix != bindPrefixDefault {
|
2015-08-28 13:39:30 -04:00
|
|
|
bindOption += " -prefix=" + bindPrefix
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
|
|
|
|
2015-12-01 09:37:56 +05:30
|
|
|
fileBase := bindPrefix + name
|
|
|
|
mfile := filepath.Join(outdir, fileBase+".m")
|
|
|
|
hfile := filepath.Join(outdir, fileBase+".h")
|
2015-08-27 12:19:47 -04:00
|
|
|
|
2015-07-14 18:09:25 -04:00
|
|
|
generate := func(w io.Writer) error {
|
2015-08-28 13:39:30 -04:00
|
|
|
if buildX {
|
2015-12-01 09:37:56 +05:30
|
|
|
printcmd("gobind %s -outdir=%s %s", bindOption, outdir, pkg.Path())
|
2015-08-28 13:39:30 -04:00
|
|
|
}
|
2015-11-20 11:26:00 -05:00
|
|
|
if buildN {
|
|
|
|
return nil
|
|
|
|
}
|
2015-12-01 09:37:56 +05:30
|
|
|
return bind.GenObjc(w, b.fset, pkg, bindPrefix, false)
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
|
|
|
if err := writeFile(mfile, generate); err != nil {
|
2015-12-01 09:37:56 +05:30
|
|
|
return "", err
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
|
|
|
generate = func(w io.Writer) error {
|
2015-11-20 11:26:00 -05:00
|
|
|
if buildN {
|
|
|
|
return nil
|
|
|
|
}
|
2015-12-01 09:37:56 +05:30
|
|
|
return bind.GenObjc(w, b.fset, pkg, bindPrefix, true)
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
|
|
|
if err := writeFile(hfile, generate); err != nil {
|
2015-12-01 09:37:56 +05:30
|
|
|
return "", err
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
objcPkg, err := ctx.Import("golang.org/x/mobile/bind/objc", "", build.FindOnly)
|
|
|
|
if err != nil {
|
2015-12-01 09:37:56 +05:30
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if err := copyFile(filepath.Join(outdir, "seq.h"), filepath.Join(objcPkg.Dir, "seq.h")); err != nil {
|
|
|
|
return "", err
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
2015-12-01 09:37:56 +05:30
|
|
|
return fileBase, nil
|
2015-07-14 18:09:25 -04:00
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func (b *binder) GenJava(pkg *types.Package, outdir string) error {
|
|
|
|
className := strings.Title(pkg.Name())
|
2015-02-11 15:23:09 -05:00
|
|
|
javaFile := filepath.Join(outdir, className+".java")
|
2015-08-28 13:39:30 -04:00
|
|
|
bindOption := "-lang=java"
|
|
|
|
if bindJavaPkg != "" {
|
|
|
|
bindOption += " -javapkg=" + bindJavaPkg
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
generate := func(w io.Writer) error {
|
2015-08-28 13:39:30 -04:00
|
|
|
if buildX {
|
2015-11-19 10:51:29 +05:30
|
|
|
printcmd("gobind %s -outdir=%s %s", bindOption, outdir, pkg.Path())
|
2015-08-28 13:39:30 -04:00
|
|
|
}
|
2015-11-20 11:26:00 -05:00
|
|
|
if buildN {
|
|
|
|
return nil
|
|
|
|
}
|
2015-11-19 10:51:29 +05:30
|
|
|
return bind.GenJava(w, b.fset, pkg, bindJavaPkg)
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
if err := writeFile(javaFile, generate); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func (b *binder) GenGo(pkg *types.Package, outdir string) error {
|
|
|
|
pkgName := "go_" + pkg.Name()
|
2015-08-28 13:39:30 -04:00
|
|
|
outdir = filepath.Join(outdir, pkgName)
|
|
|
|
goFile := filepath.Join(outdir, pkgName+"main.go")
|
2015-02-11 15:23:09 -05:00
|
|
|
|
|
|
|
generate := func(w io.Writer) error {
|
2015-08-28 13:39:30 -04:00
|
|
|
if buildX {
|
2015-11-19 10:51:29 +05:30
|
|
|
printcmd("gobind -lang=go -outdir=%s %s", outdir, pkg.Path())
|
2015-08-28 13:39:30 -04:00
|
|
|
}
|
2015-11-20 11:26:00 -05:00
|
|
|
if buildN {
|
|
|
|
return nil
|
|
|
|
}
|
2015-11-19 10:51:29 +05:30
|
|
|
return bind.GenGo(w, b.fset, pkg)
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
if err := writeFile(goFile, generate); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-07-14 18:09:25 -04:00
|
|
|
func copyFile(dst, src string) error {
|
|
|
|
if buildX {
|
|
|
|
printcmd("cp %s %s", src, dst)
|
|
|
|
}
|
|
|
|
return writeFile(dst, func(w io.Writer) error {
|
2015-07-19 21:29:35 -04:00
|
|
|
if buildN {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
f, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if _, err := io.Copy(w, f); err != nil {
|
|
|
|
return fmt.Errorf("cp %s %s failed: %v", src, dst, err)
|
|
|
|
}
|
|
|
|
return nil
|
2015-07-14 18:09:25 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-02-11 15:23:09 -05:00
|
|
|
func writeFile(filename string, generate func(io.Writer) error) error {
|
|
|
|
if buildV {
|
|
|
|
fmt.Fprintf(os.Stderr, "write %s\n", filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := mkdir(filepath.Dir(filename))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if buildN {
|
|
|
|
return generate(ioutil.Discard)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if cerr := f.Close(); err == nil {
|
|
|
|
err = cerr
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return generate(f)
|
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func loadExportData(pkgs []*build.Package, env []string, args ...string) ([]*types.Package, error) {
|
2015-11-13 13:57:15 -05:00
|
|
|
// Compile the package. This will produce good errors if the package
|
|
|
|
// doesn't typecheck for some reason, and is a necessary step to
|
|
|
|
// building the final output anyway.
|
2015-11-19 10:51:29 +05:30
|
|
|
paths := make([]string, len(pkgs))
|
|
|
|
for i, p := range pkgs {
|
|
|
|
paths[i] = p.ImportPath
|
|
|
|
}
|
|
|
|
if err := goInstall(paths, env, args...); err != nil {
|
2015-11-13 13:57:15 -05:00
|
|
|
return nil, err
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
|
2015-11-13 13:57:15 -05:00
|
|
|
// Assemble a fake GOPATH and trick go/importer into using it.
|
|
|
|
// Ideally the importer package would let us provide this to
|
|
|
|
// it somehow, but this works with what's in Go 1.5 today and
|
|
|
|
// gives us access to the gcimporter package without us having
|
|
|
|
// to make a copy of it.
|
|
|
|
fakegopath := filepath.Join(tmpdir, "fakegopath")
|
|
|
|
if err := removeAll(fakegopath); err != nil {
|
|
|
|
return nil, err
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
2015-11-13 13:57:15 -05:00
|
|
|
if err := mkdir(filepath.Join(fakegopath, "pkg")); err != nil {
|
|
|
|
return nil, err
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
2015-11-19 10:51:29 +05:30
|
|
|
typePkgs := make([]*types.Package, len(pkgs))
|
|
|
|
for i, p := range pkgs {
|
|
|
|
importPath := p.ImportPath
|
|
|
|
src := filepath.Join(pkgdir(env), importPath+".a")
|
|
|
|
dst := filepath.Join(fakegopath, "pkg/"+getenv(env, "GOOS")+"_"+getenv(env, "GOARCH")+"/"+importPath+".a")
|
|
|
|
if err := copyFile(dst, src); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if buildN {
|
|
|
|
typePkgs[i] = types.NewPackage(importPath, path.Base(importPath))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
oldDefault := build.Default
|
|
|
|
build.Default = ctx // copy
|
|
|
|
build.Default.GOPATH = fakegopath
|
|
|
|
p, err := importer.Default().Import(importPath)
|
|
|
|
build.Default = oldDefault
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
typePkgs[i] = p
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
2015-11-19 10:51:29 +05:30
|
|
|
return typePkgs, nil
|
2015-11-13 13:57:15 -05:00
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func newBinder(pkgs []*types.Package) (*binder, error) {
|
|
|
|
for _, pkg := range pkgs {
|
|
|
|
if pkg.Name() == "main" {
|
|
|
|
return nil, fmt.Errorf("package %q (%q): can only bind a library package", pkg.Name(), pkg.Path())
|
|
|
|
}
|
2015-11-13 13:57:15 -05:00
|
|
|
}
|
2015-02-11 15:23:09 -05:00
|
|
|
b := &binder{
|
2015-11-13 13:57:15 -05:00
|
|
|
fset: token.NewFileSet(),
|
2015-11-19 10:51:29 +05:30
|
|
|
pkgs: pkgs,
|
2015-02-11 15:23:09 -05:00
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|