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 }