2015-07-10 16:47:46 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2015-07-16 13:32:51 -04:00
|
|
|
"os/exec"
|
2015-07-10 16:47:46 -06:00
|
|
|
"path/filepath"
|
2015-07-16 13:32:51 -04:00
|
|
|
"runtime"
|
2015-07-13 21:02:58 -04:00
|
|
|
"strings"
|
2015-07-10 16:47:46 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// General mobile build environment. Initialized by envInit.
|
|
|
|
var (
|
|
|
|
cwd string
|
|
|
|
gomobilepath string // $GOPATH/pkg/gomobile
|
|
|
|
|
2015-12-10 15:52:40 -05:00
|
|
|
androidEnv map[string][]string // android arch -> []string
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
darwinArmEnv []string
|
|
|
|
darwinArm64Env []string
|
2015-07-16 13:32:51 -04:00
|
|
|
darwin386Env []string
|
|
|
|
darwinAmd64Env []string
|
2015-07-24 16:47:16 -04:00
|
|
|
|
|
|
|
androidArmNM string
|
|
|
|
darwinArmNM string
|
2015-07-10 16:47:46 -06:00
|
|
|
)
|
|
|
|
|
2015-07-16 13:32:51 -04:00
|
|
|
func buildEnvInit() (cleanup func(), err error) {
|
2015-07-10 16:47:46 -06:00
|
|
|
// Find gomobilepath.
|
|
|
|
gopath := goEnv("GOPATH")
|
|
|
|
for _, p := range filepath.SplitList(gopath) {
|
|
|
|
gomobilepath = filepath.Join(p, "pkg", "gomobile")
|
2015-07-16 20:36:27 -04:00
|
|
|
if _, err := os.Stat(gomobilepath); buildN || err == nil {
|
2015-07-10 16:47:46 -06:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2015-07-16 20:36:27 -04:00
|
|
|
|
|
|
|
if err := envInit(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
if buildX {
|
|
|
|
fmt.Fprintln(xout, "GOMOBILE="+gomobilepath)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the toolchain is in a good state.
|
2015-07-16 20:36:27 -04:00
|
|
|
// Pick a temporary directory for assembling an apk/app.
|
2015-07-10 16:47:46 -06:00
|
|
|
if gomobilepath == "" {
|
|
|
|
return nil, errors.New("toolchain not installed, run `gomobile init`")
|
|
|
|
}
|
2015-07-27 16:33:08 -04:00
|
|
|
cleanupFn := func() {
|
|
|
|
if buildWork {
|
|
|
|
fmt.Printf("WORK=%s\n", tmpdir)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
removeAll(tmpdir)
|
|
|
|
}
|
2015-07-16 13:32:51 -04:00
|
|
|
if buildN {
|
|
|
|
tmpdir = "$WORK"
|
2015-07-16 20:36:27 -04:00
|
|
|
cleanupFn = func() {}
|
2015-07-16 13:32:51 -04:00
|
|
|
} else {
|
2015-07-16 20:36:27 -04:00
|
|
|
verpath := filepath.Join(gomobilepath, "version")
|
|
|
|
installedVersion, err := ioutil.ReadFile(verpath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.New("toolchain partially installed, run `gomobile init`")
|
|
|
|
}
|
2015-11-17 13:24:48 -05:00
|
|
|
if !bytes.Equal(installedVersion, goVersionOut) {
|
2015-07-16 20:36:27 -04:00
|
|
|
return nil, errors.New("toolchain out of date, run `gomobile init`")
|
|
|
|
}
|
|
|
|
|
2015-07-16 13:32:51 -04:00
|
|
|
tmpdir, err = ioutil.TempDir("", "gomobile-work-")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if buildX {
|
|
|
|
fmt.Fprintln(xout, "WORK="+tmpdir)
|
|
|
|
}
|
|
|
|
|
2015-07-16 20:36:27 -04:00
|
|
|
return cleanupFn, nil
|
2015-07-16 13:32:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func envInit() (err error) {
|
|
|
|
// TODO(crawshaw): cwd only used by ctx.Import, which can take "."
|
|
|
|
cwd, err = os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
// Setup the cross-compiler environments.
|
|
|
|
|
2015-12-10 15:52:40 -05:00
|
|
|
androidEnv = make(map[string][]string)
|
|
|
|
for arch, toolchain := range ndk {
|
|
|
|
if goVersion < toolchain.minGoVer {
|
|
|
|
continue
|
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
|
2016-06-25 00:50:10 +02:00
|
|
|
// Emulate the flags in the clang wrapper scripts generated
|
|
|
|
// by make_standalone_toolchain.py
|
|
|
|
s := strings.SplitN(toolchain.toolPrefix, "-", 3)
|
|
|
|
a, os, env := s[0], s[1], s[2]
|
|
|
|
if a == "arm" {
|
|
|
|
a = "armv7a"
|
|
|
|
}
|
|
|
|
target := strings.Join([]string{a, "none", os, env}, "-")
|
|
|
|
sysroot := filepath.Join(ndk.Root(), toolchain.arch, "sysroot")
|
|
|
|
flags := fmt.Sprintf("-target %s --sysroot %s", target, sysroot)
|
2015-12-10 15:52:40 -05:00
|
|
|
androidEnv[arch] = []string{
|
|
|
|
"GOOS=android",
|
|
|
|
"GOARCH=" + arch,
|
2016-06-25 00:50:10 +02:00
|
|
|
"CC=" + toolchain.Path("clang"),
|
|
|
|
"CXX=" + toolchain.Path("clang++"),
|
|
|
|
"CGO_CFLAGS=" + flags,
|
|
|
|
"CGO_CPPFLAGS=" + flags,
|
|
|
|
"CGO_LDFLAGS=" + flags,
|
2015-12-10 15:52:40 -05:00
|
|
|
"CGO_ENABLED=1",
|
|
|
|
}
|
|
|
|
if arch == "arm" {
|
|
|
|
androidEnv[arch] = append(androidEnv[arch], "GOARM=7")
|
|
|
|
}
|
2015-07-16 13:32:51 -04:00
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
|
2015-07-16 13:32:51 -04:00
|
|
|
if runtime.GOOS != "darwin" {
|
|
|
|
return nil
|
2015-07-10 16:47:46 -06:00
|
|
|
}
|
2015-07-16 13:32:51 -04:00
|
|
|
|
|
|
|
clang, cflags, err := envClang("iphoneos")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
darwinArmEnv = []string{
|
2015-07-10 16:47:46 -06:00
|
|
|
"GOOS=darwin",
|
|
|
|
"GOARCH=arm",
|
2015-07-16 13:32:51 -04:00
|
|
|
"GOARM=7",
|
|
|
|
"CC=" + clang,
|
|
|
|
"CXX=" + clang,
|
2016-03-25 01:05:13 +01:00
|
|
|
"CGO_CFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm"),
|
|
|
|
"CGO_LDFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm"),
|
2015-07-16 13:32:51 -04:00
|
|
|
"CGO_ENABLED=1",
|
|
|
|
}
|
2015-07-24 16:47:16 -04:00
|
|
|
darwinArmNM = "nm"
|
2015-07-16 13:32:51 -04:00
|
|
|
darwinArm64Env = []string{
|
2015-07-10 16:47:46 -06:00
|
|
|
"GOOS=darwin",
|
|
|
|
"GOARCH=arm64",
|
2015-07-16 13:32:51 -04:00
|
|
|
"CC=" + clang,
|
|
|
|
"CXX=" + clang,
|
2016-03-25 01:05:13 +01:00
|
|
|
"CGO_CFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm64"),
|
|
|
|
"CGO_LDFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm64"),
|
2015-07-16 13:32:51 -04:00
|
|
|
"CGO_ENABLED=1",
|
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
|
2015-07-16 13:32:51 -04:00
|
|
|
clang, cflags, err = envClang("iphonesimulator")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-07-10 16:47:46 -06:00
|
|
|
}
|
2015-07-16 13:32:51 -04:00
|
|
|
darwin386Env = []string{
|
|
|
|
"GOOS=darwin",
|
|
|
|
"GOARCH=386",
|
|
|
|
"CC=" + clang,
|
|
|
|
"CXX=" + clang,
|
|
|
|
"CGO_CFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch " + archClang("386"),
|
|
|
|
"CGO_LDFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch " + archClang("386"),
|
|
|
|
"CGO_ENABLED=1",
|
|
|
|
}
|
|
|
|
darwinAmd64Env = []string{
|
|
|
|
"GOOS=darwin",
|
|
|
|
"GOARCH=amd64",
|
|
|
|
"CC=" + clang,
|
|
|
|
"CXX=" + clang,
|
|
|
|
"CGO_CFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch x86_64",
|
|
|
|
"CGO_LDFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch x86_64",
|
|
|
|
"CGO_ENABLED=1",
|
2015-07-10 16:47:46 -06:00
|
|
|
}
|
|
|
|
|
2015-07-16 13:32:51 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func envClang(sdkName string) (clang, cflags string, err error) {
|
2015-07-16 20:36:27 -04:00
|
|
|
if buildN {
|
|
|
|
return "clang-" + sdkName, "-isysroot=" + sdkName, nil
|
|
|
|
}
|
2015-07-16 13:32:51 -04:00
|
|
|
cmd := exec.Command("xcrun", "--sdk", sdkName, "--find", "clang")
|
2015-10-01 14:30:07 -04:00
|
|
|
out, err := cmd.CombinedOutput()
|
2015-07-16 13:32:51 -04:00
|
|
|
if err != nil {
|
|
|
|
return "", "", fmt.Errorf("xcrun --find: %v\n%s", err, out)
|
|
|
|
}
|
|
|
|
clang = strings.TrimSpace(string(out))
|
|
|
|
|
|
|
|
cmd = exec.Command("xcrun", "--sdk", sdkName, "--show-sdk-path")
|
2015-10-01 14:30:07 -04:00
|
|
|
out, err = cmd.CombinedOutput()
|
2015-07-16 13:32:51 -04:00
|
|
|
if err != nil {
|
|
|
|
return "", "", fmt.Errorf("xcrun --show-sdk-path: %v\n%s", err, out)
|
|
|
|
}
|
|
|
|
sdk := strings.TrimSpace(string(out))
|
|
|
|
|
|
|
|
return clang, "-isysroot " + sdk, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func archClang(goarch string) string {
|
|
|
|
switch goarch {
|
|
|
|
case "arm":
|
|
|
|
return "armv7"
|
|
|
|
case "arm64":
|
|
|
|
return "arm64"
|
|
|
|
case "386":
|
|
|
|
return "i386"
|
|
|
|
case "amd64":
|
|
|
|
return "x86_64"
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown GOARCH: %q", goarch))
|
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
}
|
2015-07-13 21:02:58 -04:00
|
|
|
|
|
|
|
// environ merges os.Environ and the given "key=value" pairs.
|
|
|
|
// If a key is in both os.Environ and kv, kv takes precedence.
|
|
|
|
func environ(kv []string) []string {
|
|
|
|
cur := os.Environ()
|
|
|
|
new := make([]string, 0, len(cur)+len(kv))
|
|
|
|
|
|
|
|
envs := make(map[string]string, len(cur))
|
|
|
|
for _, ev := range cur {
|
|
|
|
elem := strings.SplitN(ev, "=", 2)
|
|
|
|
if len(elem) != 2 || elem[0] == "" {
|
|
|
|
// pass the env var of unusual form untouched.
|
|
|
|
// e.g. Windows may have env var names starting with "=".
|
|
|
|
new = append(new, ev)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if goos == "windows" {
|
|
|
|
elem[0] = strings.ToUpper(elem[0])
|
|
|
|
}
|
|
|
|
envs[elem[0]] = elem[1]
|
|
|
|
}
|
|
|
|
for _, ev := range kv {
|
|
|
|
elem := strings.SplitN(ev, "=", 2)
|
|
|
|
if len(elem) != 2 || elem[0] == "" {
|
|
|
|
panic(fmt.Sprintf("malformed env var %q from input", ev))
|
|
|
|
}
|
|
|
|
if goos == "windows" {
|
|
|
|
elem[0] = strings.ToUpper(elem[0])
|
|
|
|
}
|
|
|
|
envs[elem[0]] = elem[1]
|
|
|
|
}
|
|
|
|
for k, v := range envs {
|
|
|
|
new = append(new, k+"="+v)
|
|
|
|
}
|
|
|
|
return new
|
|
|
|
}
|
2015-07-16 13:32:51 -04:00
|
|
|
|
|
|
|
func getenv(env []string, key string) string {
|
|
|
|
prefix := key + "="
|
|
|
|
for _, kv := range env {
|
|
|
|
if strings.HasPrefix(kv, prefix) {
|
|
|
|
return kv[len(prefix):]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func pkgdir(env []string) string {
|
|
|
|
return gomobilepath + "/pkg_" + getenv(env, "GOOS") + "_" + getenv(env, "GOARCH")
|
|
|
|
}
|
2015-12-10 15:52:40 -05:00
|
|
|
|
|
|
|
type ndkToolchain struct {
|
|
|
|
arch string
|
|
|
|
abi string
|
|
|
|
platform string
|
|
|
|
gcc string
|
|
|
|
toolPrefix string
|
|
|
|
minGoVer goToolVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tc *ndkToolchain) Path(toolName string) string {
|
|
|
|
if goos == "windows" {
|
|
|
|
toolName += ".exe"
|
|
|
|
}
|
|
|
|
return filepath.Join(ndk.Root(), tc.arch, "bin", tc.toolPrefix+"-"+toolName)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ndkConfig map[string]ndkToolchain // map: GOOS->androidConfig.
|
|
|
|
|
|
|
|
func (nc ndkConfig) Root() string {
|
|
|
|
return filepath.Join(gomobilepath, "android-"+ndkVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nc ndkConfig) Toolchain(arch string) ndkToolchain {
|
|
|
|
tc, ok := nc[arch]
|
|
|
|
if !ok || tc.minGoVer > goVersion {
|
|
|
|
panic(`unsupported architecture: ` + arch)
|
|
|
|
}
|
|
|
|
return tc
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: share this with release.go
|
|
|
|
var ndk = ndkConfig{
|
|
|
|
"arm": {
|
|
|
|
arch: "arm",
|
|
|
|
abi: "armeabi-v7a",
|
|
|
|
platform: "android-15",
|
2016-06-01 10:20:32 +02:00
|
|
|
gcc: "arm-linux-androideabi-4.9",
|
2015-12-10 15:52:40 -05:00
|
|
|
toolPrefix: "arm-linux-androideabi",
|
|
|
|
minGoVer: go1_5,
|
|
|
|
},
|
2016-03-14 17:47:25 -04:00
|
|
|
"arm64": {
|
|
|
|
arch: "arm64",
|
|
|
|
abi: "arm64-v8a",
|
|
|
|
platform: "android-21",
|
|
|
|
gcc: "aarch64-linux-android-4.9",
|
|
|
|
toolPrefix: "aarch64-linux-android",
|
|
|
|
minGoVer: go1_6,
|
|
|
|
},
|
2016-02-18 05:25:39 -05:00
|
|
|
|
|
|
|
"386": {
|
|
|
|
arch: "x86",
|
|
|
|
abi: "x86",
|
|
|
|
platform: "android-15",
|
2016-06-01 10:20:32 +02:00
|
|
|
gcc: "x86-4.9",
|
2016-02-18 05:25:39 -05:00
|
|
|
toolPrefix: "i686-linux-android",
|
|
|
|
minGoVer: go1_6,
|
|
|
|
},
|
|
|
|
"amd64": {
|
|
|
|
arch: "x86_64",
|
|
|
|
abi: "x86_64",
|
|
|
|
platform: "android-21",
|
|
|
|
gcc: "x86_64-4.9",
|
|
|
|
toolPrefix: "x86_64-linux-android",
|
|
|
|
minGoVer: go1_6,
|
|
|
|
},
|
2015-12-10 15:52:40 -05:00
|
|
|
}
|