diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go index 248db89..ef9a5b3 100644 --- a/cmd/gomobile/bind.go +++ b/cmd/gomobile/bind.go @@ -93,8 +93,10 @@ func runBind(cmd *command) error { return fmt.Errorf("-prefix is supported only for ios target") } - if ctx.GOOS == "android" && !hasNDK() { - 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.") + if ctx.GOOS == "android" { + if _, err := ndkRoot(); err != nil { + return err + } } if ctx.GOOS == "darwin" { diff --git a/cmd/gomobile/bind_androidapp.go b/cmd/gomobile/bind_androidapp.go index 9897fbd..149fab4 100644 --- a/cmd/gomobile/bind_androidapp.go +++ b/cmd/gomobile/bind_androidapp.go @@ -6,7 +6,6 @@ package main import ( "archive/zip" - "errors" "fmt" "go/build" "io" @@ -23,10 +22,6 @@ func goAndroidBind(gobind string, pkgs []*build.Package, androidArchs []string) return fmt.Errorf("this command requires ANDROID_HOME environment variable (path to the Android SDK)") } - if !hasNDK() { - 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.") - } - // Run gobind to generate the bindings cmd := exec.Command( gobind, diff --git a/cmd/gomobile/bind_test.go b/cmd/gomobile/bind_test.go index 5129590..88c2aef 100644 --- a/cmd/gomobile/bind_test.go +++ b/cmd/gomobile/bind_test.go @@ -176,7 +176,7 @@ func TestBindIOS(t *testing.T) { var bindAndroidTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile WORK=$WORK GOOS=android CGO_ENABLED=1 gobind -lang=go,java -outdir=$WORK{{if .JavaPkg}} -javapkg={{.JavaPkg}}{{end}} golang.org/x/mobile/asset -GOOS=android GOARCH=arm CC=$GOMOBILE/ndk-toolchains/arm/bin/arm-linux-androideabi-clang CXX=$GOMOBILE/ndk-toolchains/arm/bin/arm-linux-androideabi-clang++ CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go build -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so gobind +GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang++ CGO_ENABLED=1 GOARM=7 GOPATH=$WORK:$GOPATH go build -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so gobind PWD=$WORK/java javac -d $WORK/javac-output -source 1.7 -target 1.7 -bootclasspath {{.AndroidPlatform}}/android.jar *.java jar c -C $WORK/javac-output . `)) diff --git a/cmd/gomobile/build_androidapp.go b/cmd/gomobile/build_androidapp.go index 06b2571..7b5e06f 100644 --- a/cmd/gomobile/build_androidapp.go +++ b/cmd/gomobile/build_androidapp.go @@ -25,8 +25,9 @@ import ( ) func goAndroidBuild(pkg *build.Package, androidArchs []string) (map[string]bool, error) { - if !hasNDK() { - return nil, 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.") + ndkRoot, err := ndkRoot() + if err != nil { + return nil, err } appName := path.Base(pkg.ImportPath) libName := androidPkgName(appName) @@ -79,7 +80,7 @@ func goAndroidBuild(pkg *build.Package, androidArchs []string) (map[string]bool, if err != nil { return nil, err } - nmpkgs[arch], err = extractPkgs(toolchain.Path("nm"), libAbsPath) + nmpkgs[arch], err = extractPkgs(toolchain.Path(ndkRoot, "nm"), libAbsPath) if err != nil { return nil, err } diff --git a/cmd/gomobile/build_test.go b/cmd/gomobile/build_test.go index eb6ef58..1bce42f 100644 --- a/cmd/gomobile/build_test.go +++ b/cmd/gomobile/build_test.go @@ -105,7 +105,7 @@ func TestAndroidBuild(t *testing.T) { var androidBuildTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile WORK=$WORK mkdir -p $WORK/lib/armeabi-v7a -GOOS=android GOARCH=arm CC=$GOMOBILE/ndk-toolchains/arm/bin/arm-linux-androideabi-clang CXX=$GOMOBILE/ndk-toolchains/arm/bin/arm-linux-androideabi-clang++ CGO_ENABLED=1 GOARM=7 go build -tags tag1 -x -buildmode=c-shared -o $WORK/lib/armeabi-v7a/libbasic.so golang.org/x/mobile/example/basic +GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.NDKARCH}}/bin/armv7a-linux-androideabi16-clang++ CGO_ENABLED=1 GOARM=7 go build -tags tag1 -x -buildmode=c-shared -o $WORK/lib/armeabi-v7a/libbasic.so golang.org/x/mobile/example/basic `)) func TestParseBuildTargetFlag(t *testing.T) { diff --git a/cmd/gomobile/env.go b/cmd/gomobile/env.go index 471b009..5a523fe 100644 --- a/cmd/gomobile/env.go +++ b/cmd/gomobile/env.go @@ -81,14 +81,14 @@ func envInit() (err error) { } // Setup the cross-compiler environments. - if hasNDK() { + if ndkRoot, err := ndkRoot(); err == nil { androidEnv = make(map[string][]string) for arch, toolchain := range ndk { androidEnv[arch] = []string{ "GOOS=android", "GOARCH=" + arch, - "CC=" + toolchain.Path("clang"), - "CXX=" + toolchain.Path("clang++"), + "CC=" + toolchain.Path(ndkRoot, "clang"), + "CXX=" + toolchain.Path(ndkRoot, "clang++"), "CGO_ENABLED=1", } if arch == "arm" { @@ -139,13 +139,25 @@ func envInit() (err error) { return nil } -func hasNDK() bool { +func ndkRoot() (string, error) { if buildN { - return true + return "$NDK_PATH", nil } - tcPath := filepath.Join(gomobilepath, "ndk-toolchains") - _, err := os.Stat(tcPath) - return err == nil + androidHome := os.Getenv("ANDROID_HOME") + if androidHome == "" { + return "", errors.New("The Android SDK was not found. Please set ANDROID_HOME to the root of the Android SDK.") + } + ndkRoot := filepath.Join(androidHome, "ndk-bundle") + _, err := os.Stat(ndkRoot) + if err != nil { + return "", fmt.Errorf("The NDK was not found in $ANDROID_HOME/ndk-bundle (%q). Install the NDK with `sdkmanager 'ndk-bundle'`", ndkRoot) + } + prebuiltPath := filepath.Join(androidHome, "ndk-bundle", "toolchains", "llvm", "prebuilt") + _, err = os.Stat(prebuiltPath) + if err != nil { + return "", fmt.Errorf("No prebuilt toolchains found in $ANDROID_HOME/ndk-bundle/toolchains/llvm/prebuilt (%q). Make sure your NDK version is >= r19b. Use `sdkmanager --update` to update it.", prebuiltPath) + } + return ndkRoot, nil } func envClang(sdkName string) (clang, cflags string, err error) { @@ -247,15 +259,21 @@ func archNDK() string { } type ndkToolchain struct { - arch string - abi string - platform string - gcc string - toolPrefix string + arch string + abi string + toolPrefix string + clangPrefix string } -func (tc *ndkToolchain) Path(toolName string) string { - return filepath.Join(gomobilepath, "ndk-toolchains", tc.arch, "bin", tc.toolPrefix+"-"+toolName) +func (tc *ndkToolchain) Path(ndkRoot, toolName string) string { + var pref string + switch toolName { + case "clang", "clang++": + pref = tc.clangPrefix + default: + pref = tc.toolPrefix + } + return filepath.Join(ndkRoot, "toolchains", "llvm", "prebuilt", archNDK(), "bin", pref+"-"+toolName) } type ndkConfig map[string]ndkToolchain // map: GOOS->androidConfig. @@ -270,33 +288,29 @@ func (nc ndkConfig) Toolchain(arch string) ndkToolchain { var ndk = ndkConfig{ "arm": { - arch: "arm", - abi: "armeabi-v7a", - platform: "android-16", - gcc: "arm-linux-androideabi-4.9", - toolPrefix: "arm-linux-androideabi", + arch: "arm", + abi: "armeabi-v7a", + toolPrefix: "arm-linux-androideabi", + clangPrefix: "armv7a-linux-androideabi16", }, "arm64": { - arch: "arm64", - abi: "arm64-v8a", - platform: "android-21", - gcc: "aarch64-linux-android-4.9", - toolPrefix: "aarch64-linux-android", + arch: "arm64", + abi: "arm64-v8a", + toolPrefix: "aarch64-linux-android", + clangPrefix: "aarch64-linux-android21", }, "386": { - arch: "x86", - abi: "x86", - platform: "android-16", - gcc: "x86-4.9", - toolPrefix: "i686-linux-android", + arch: "x86", + abi: "x86", + toolPrefix: "i686-linux-android", + clangPrefix: "i686-linux-android16", }, "amd64": { - arch: "x86_64", - abi: "x86_64", - platform: "android-21", - gcc: "x86_64-4.9", - toolPrefix: "x86_64-linux-android", + arch: "x86_64", + abi: "x86_64", + toolPrefix: "x86_64-linux-android", + clangPrefix: "x86_64-linux-android21", }, } diff --git a/cmd/gomobile/init.go b/cmd/gomobile/init.go index fba9308..ed40230 100644 --- a/cmd/gomobile/init.go +++ b/cmd/gomobile/init.go @@ -26,26 +26,18 @@ var ( var cmdInit = &command{ run: runInit, Name: "init", - Usage: "[-ndk dir] [-openal dir]", - Short: "install NDK toolchains and build OpenAL for Android", + Usage: "[-openal dir]", + Short: "build OpenAL for Android", Long: ` -If the -ndk flag is specified or the Android NDK is installed at -$ANDROID_HOME/ndk-bundle, init will create NDK standalone toolchains -for Android targets. - If a OpenAL source directory is specified with -openal, init will build an Android version of OpenAL for use with gomobile build and gomobile install. `, } -var ( - initNDK string // -ndk - initOpenAL string // -openal -) +var initOpenAL string // -openal func init() { - cmdInit.flag.StringVar(&initNDK, "ndk", "", "Android NDK path") cmdInit.flag.StringVar(&initOpenAL, "openal", "", "OpenAL source path") } @@ -91,32 +83,8 @@ func runInit(cmd *command) error { } if buildN { - initNDK = "$NDK_PATH" initOpenAL = "$OPENAL_PATH" } else { - toolsDir := filepath.Join("prebuilt", archNDK(), "bin") - // Try the ndk-bundle SDK package package, if installed. - if initNDK == "" { - if sdkHome := os.Getenv("ANDROID_HOME"); sdkHome != "" { - path := filepath.Join(sdkHome, "ndk-bundle") - if st, err := os.Stat(filepath.Join(path, toolsDir)); err == nil && st.IsDir() { - initNDK = path - } - } - } - if initNDK != "" { - var err error - if initNDK, err = filepath.Abs(initNDK); err != nil { - return err - } - // Check if the platform directory contains a known subdirectory. - if _, err := os.Stat(filepath.Join(initNDK, toolsDir)); err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("%q does not point to an Android NDK.", initNDK) - } - return err - } - } if initOpenAL != "" { var err error if initOpenAL, err = filepath.Abs(initOpenAL); err != nil { @@ -130,10 +98,6 @@ func runInit(cmd *command) error { start := time.Now() - if err := installNDKToolchains(gomobilepath); err != nil { - return err - } - if err := installOpenAL(gomobilepath); err != nil { return err } @@ -145,36 +109,13 @@ func runInit(cmd *command) error { return nil } -func installNDKToolchains(gomobilepath string) error { - if initNDK == "" { - return nil - } - toolsDir := filepath.Join(initNDK, "prebuilt", archNDK(), "bin") - py27 := filepath.Join(toolsDir, "python2.7") - for _, arch := range allArchs { - t := ndk[arch] - // Split android-XX to get the api version. - platform := strings.SplitN(t.platform, "-", 2) - api := platform[1] - cmd := exec.Command(py27, - "build/tools/make_standalone_toolchain.py", - "--arch="+t.arch, - "--api="+api, - "--install-dir="+filepath.Join(gomobilepath, "ndk-toolchains", t.arch)) - cmd.Dir = initNDK - if err := runCmd(cmd); err != nil { - return err - } - } - return nil -} - func installOpenAL(gomobilepath string) error { if initOpenAL == "" { return nil } - if !hasNDK() { - return errors.New("The Android NDK is needed to build OpenAL but it was not found. Please run gomobile init with the ndk-bundle installed through the Android SDK manager or with the -ndk flag set.") + ndkRoot, err := ndkRoot() + if err != nil { + return err } var cmake string @@ -232,8 +173,7 @@ func installOpenAL(gomobilepath string) error { if abi == "arm" { abi = "armeabi" } - tcPath := filepath.Join(gomobilepath, "ndk-toolchains", t.arch, "bin") - make := filepath.Join(tcPath, "make") + make := filepath.Join(ndkRoot, "prebuilt", archNDK(), "bin", "make") // Split android-XX to get the api version. buildDir := alTmpDir + "/build/" + abi if err := mkdir(buildDir); err != nil { @@ -242,8 +182,9 @@ func installOpenAL(gomobilepath string) error { cmd := exec.Command(cmake, initOpenAL, "-DCMAKE_TOOLCHAIN_FILE="+initOpenAL+"/XCompile-Android.txt", - "-DHOST="+t.toolPrefix) + "-DHOST="+t.clangPrefix) cmd.Dir = buildDir + tcPath := filepath.Join(ndkRoot, "toolchains", "llvm", "prebuilt", archNDK(), "bin") if !buildN { orgPath := os.Getenv("PATH") cmd.Env = []string{"PATH=" + tcPath + string(os.PathListSeparator) + orgPath} diff --git a/cmd/gomobile/init_test.go b/cmd/gomobile/init_test.go index c0180ce..a1d8f39 100644 --- a/cmd/gomobile/init_test.go +++ b/cmd/gomobile/init_test.go @@ -8,7 +8,6 @@ import ( "bytes" "os" "path/filepath" - "runtime" "strings" "testing" "text/template" @@ -88,7 +87,7 @@ func defaultOutputData() outputData { GOOS: goos, GOARCH: goarch, GOPATH: gopath, - NDKARCH: ndkarch(), + NDKARCH: archNDK(), Xproj: projPbxproj, Xcontents: contentsJSON, Xinfo: infoplistTmplData{BundleID: "org.golang.todo.basic", Name: "Basic"}, @@ -99,48 +98,33 @@ func defaultOutputData() outputData { return data } -func ndkarch() string { - switch runtime.GOARCH { - case "amd64": - return "x86_64" - case "386": - return "x86" - default: - return runtime.GOARCH - } -} - var initTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile rm -r -f "$GOMOBILE" mkdir -p $GOMOBILE WORK={{.GOPATH}}/pkg/gomobile/work go install -x golang.org/x/mobile/cmd/gobind -PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=arm --api=16 --install-dir=$GOMOBILE/ndk-toolchains/arm -PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=arm64 --api=21 --install-dir=$GOMOBILE/ndk-toolchains/arm64 -PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=x86 --api=16 --install-dir=$GOMOBILE/ndk-toolchains/x86 -PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=x86_64 --api=21 --install-dir=$GOMOBILE/ndk-toolchains/x86_64 cp $OPENAL_PATH/include/AL/al.h $GOMOBILE/include/AL/al.h mkdir -p $GOMOBILE/include/AL cp $OPENAL_PATH/include/AL/alc.h $GOMOBILE/include/AL/alc.h mkdir -p $GOMOBILE/include/AL mkdir -p $WORK/build/armeabi -PWD=$WORK/build/armeabi cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=arm-linux-androideabi -PWD=$WORK/build/armeabi $GOMOBILE/ndk-toolchains/arm/bin/make +PWD=$WORK/build/armeabi cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=armv7a-linux-androideabi16 +PWD=$WORK/build/armeabi $NDK_PATH/prebuilt/{{.NDKARCH}}/bin/make cp $WORK/build/armeabi/libopenal.so $GOMOBILE/lib/armeabi-v7a/libopenal.so mkdir -p $GOMOBILE/lib/armeabi-v7a mkdir -p $WORK/build/arm64 -PWD=$WORK/build/arm64 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=aarch64-linux-android -PWD=$WORK/build/arm64 $GOMOBILE/ndk-toolchains/arm64/bin/make +PWD=$WORK/build/arm64 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=aarch64-linux-android21 +PWD=$WORK/build/arm64 $NDK_PATH/prebuilt/{{.NDKARCH}}/bin/make cp $WORK/build/arm64/libopenal.so $GOMOBILE/lib/arm64-v8a/libopenal.so mkdir -p $GOMOBILE/lib/arm64-v8a mkdir -p $WORK/build/x86 -PWD=$WORK/build/x86 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=i686-linux-android -PWD=$WORK/build/x86 $GOMOBILE/ndk-toolchains/x86/bin/make +PWD=$WORK/build/x86 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=i686-linux-android16 +PWD=$WORK/build/x86 $NDK_PATH/prebuilt/{{.NDKARCH}}/bin/make cp $WORK/build/x86/libopenal.so $GOMOBILE/lib/x86/libopenal.so mkdir -p $GOMOBILE/lib/x86 mkdir -p $WORK/build/x86_64 -PWD=$WORK/build/x86_64 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=x86_64-linux-android -PWD=$WORK/build/x86_64 $GOMOBILE/ndk-toolchains/x86_64/bin/make +PWD=$WORK/build/x86_64 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=x86_64-linux-android21 +PWD=$WORK/build/x86_64 $NDK_PATH/prebuilt/{{.NDKARCH}}/bin/make cp $WORK/build/x86_64/libopenal.so $GOMOBILE/lib/x86_64/libopenal.so mkdir -p $GOMOBILE/lib/x86_64 rm -r -f "$WORK"