github.com/acrespo/mobile@v0.0.0-20190107162257-dc0771356504/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" && !hasNDK() { 97 return errors.New("no Android NDK path is set. Please run gomobile init with the ndk-bundle installed through the Android SDK manager or with the -ndk flag set.") 98 } 99 100 if ctx.GOOS == "darwin" { 101 ctx.BuildTags = append(ctx.BuildTags, "ios") 102 } 103 104 var gobind string 105 if !buildN { 106 gobind, err = exec.LookPath("gobind") 107 if err != nil { 108 return errors.New("gobind was not found. Please run gomobile init before trying again.") 109 } 110 } else { 111 gobind = "gobind" 112 } 113 114 var pkgs []*build.Package 115 switch len(args) { 116 case 0: 117 pkgs = make([]*build.Package, 1) 118 pkgs[0], err = ctx.ImportDir(cwd, build.ImportComment) 119 default: 120 pkgs, err = importPackages(args) 121 } 122 if err != nil { 123 return err 124 } 125 126 // check if any of the package is main 127 for _, pkg := range pkgs { 128 if pkg.Name == "main" { 129 return fmt.Errorf("binding 'main' package (%s) is not supported", pkg.ImportComment) 130 } 131 } 132 133 switch targetOS { 134 case "android": 135 return goAndroidBind(gobind, pkgs, targetArchs) 136 case "darwin": 137 if !xcodeAvailable() { 138 return fmt.Errorf("-target=ios requires XCode") 139 } 140 return goIOSBind(gobind, pkgs, targetArchs) 141 default: 142 return fmt.Errorf(`invalid -target=%q`, buildTarget) 143 } 144 } 145 146 func importPackages(args []string) ([]*build.Package, error) { 147 pkgs := make([]*build.Package, len(args)) 148 for i, a := range args { 149 a = path.Clean(a) 150 var err error 151 if pkgs[i], err = ctx.Import(a, cwd, build.ImportComment); err != nil { 152 return nil, fmt.Errorf("package %q: %v", a, err) 153 } 154 } 155 return pkgs, nil 156 } 157 158 var ( 159 bindPrefix string // -prefix 160 bindJavaPkg string // -javapkg 161 bindClasspath string // -classpath 162 bindBootClasspath string // -bootclasspath 163 ) 164 165 func init() { 166 // bind command specific commands. 167 cmdBind.flag.StringVar(&bindJavaPkg, "javapkg", "", 168 "specifies custom Java package path prefix. Valid only with -target=android.") 169 cmdBind.flag.StringVar(&bindPrefix, "prefix", "", 170 "custom Objective-C name prefix. Valid only with -target=ios.") 171 cmdBind.flag.StringVar(&bindClasspath, "classpath", "", "The classpath for imported Java classes. Valid only with -target=android.") 172 cmdBind.flag.StringVar(&bindBootClasspath, "bootclasspath", "", "The bootstrap classpath for imported Java classes. Valid only with -target=android.") 173 } 174 175 func bootClasspath() (string, error) { 176 if bindBootClasspath != "" { 177 return bindBootClasspath, nil 178 } 179 apiPath, err := androidAPIPath() 180 if err != nil { 181 return "", err 182 } 183 return filepath.Join(apiPath, "android.jar"), nil 184 } 185 186 func copyFile(dst, src string) error { 187 if buildX { 188 printcmd("cp %s %s", src, dst) 189 } 190 return writeFile(dst, func(w io.Writer) error { 191 if buildN { 192 return nil 193 } 194 f, err := os.Open(src) 195 if err != nil { 196 return err 197 } 198 defer f.Close() 199 200 if _, err := io.Copy(w, f); err != nil { 201 return fmt.Errorf("cp %s %s failed: %v", src, dst, err) 202 } 203 return nil 204 }) 205 } 206 207 func writeFile(filename string, generate func(io.Writer) error) error { 208 if buildV { 209 fmt.Fprintf(os.Stderr, "write %s\n", filename) 210 } 211 212 err := mkdir(filepath.Dir(filename)) 213 if err != nil { 214 return err 215 } 216 217 if buildN { 218 return generate(ioutil.Discard) 219 } 220 221 f, err := os.Create(filename) 222 if err != nil { 223 return err 224 } 225 defer func() { 226 if cerr := f.Close(); err == nil { 227 err = cerr 228 } 229 }() 230 231 return generate(f) 232 }