diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go index 2c70f92..8888e3b 100644 --- a/cmd/gomobile/bind.go +++ b/cmd/gomobile/bind.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "errors" "fmt" "go/ast" "go/build" @@ -102,6 +103,10 @@ func runBind(cmd *command) error { return fmt.Errorf("-prefix is supported only for ios target") } + if ctx.GOOS == "android" && ndkRoot == "" { + 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.") + } + var pkgs []*build.Package switch len(args) { case 0: diff --git a/cmd/gomobile/bind_test.go b/cmd/gomobile/bind_test.go index de12951..d2f4d1c 100644 --- a/cmd/gomobile/bind_test.go +++ b/cmd/gomobile/bind_test.go @@ -37,6 +37,7 @@ func TestBindAndroid(t *testing.T) { buildX = true buildO = "asset.aar" buildTarget = "android" + ndkRoot = "/NDK" tests := []struct { javaPkg string @@ -106,7 +107,7 @@ mkdir -p $WORK/gomobile_bind mkdir -p $WORK/gomobile_bind mkdir -p $WORK/gomobile_bind mkdir -p $WORK/gen/src/Java -GOOS=android GOARCH=arm CC=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang++ CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_ENABLED=1 GOARM=7 GOPATH=$WORK/gen:$GOPATH go install -pkgdir=$GOMOBILE/pkg_android_arm -x golang.org/x/mobile/asset +GOOS=android GOARCH=arm CC=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -L/NDK/platforms/android-15/arch-arm/usr/lib -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 GOPATH=$WORK/gen:$GOPATH go install -pkgdir=$GOMOBILE/pkg_android_arm -x golang.org/x/mobile/asset rm -r -f "$WORK/fakegopath" mkdir -p $WORK/fakegopath/pkg cp $GOMOBILE/pkg_android_arm/golang.org/x/mobile/asset.a $WORK/fakegopath/pkg/android_arm/golang.org/x/mobile/asset.a @@ -134,7 +135,7 @@ mkdir -p $WORK/gomobile_bind cp $GOPATH/src/golang.org/x/mobile/bind/seq.go.support $WORK/gomobile_bind/seq.go mkdir -p $WORK/gomobile_bind mkdir -p $WORK/android/src/main/java/go -GOOS=android GOARCH=arm CC=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang{{.EXE}} CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_ENABLED=1 GOARM=7 GOPATH=$WORK/gen:$GOPATH go build -pkgdir=$GOMOBILE/pkg_android_arm -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so $WORK/androidlib/main.go +GOOS=android GOARCH=arm CC=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -L/NDK/platforms/android-15/arch-arm/usr/lib -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 GOPATH=$WORK/gen:$GOPATH go build -pkgdir=$GOMOBILE/pkg_android_arm -x -buildmode=c-shared -o=$WORK/android/src/main/jniLibs/armeabi-v7a/libgojni.so $WORK/androidlib/main.go rm $WORK/android/src/main/java/go/Seq.java ln -s $GOPATH/src/golang.org/x/mobile/bind/java/Seq.java $WORK/android/src/main/java/go/Seq.java rm $WORK/android/src/main/java/go/LoadJNI.java diff --git a/cmd/gomobile/build_androidapp.go b/cmd/gomobile/build_androidapp.go index 3136d7d..8d261f3 100644 --- a/cmd/gomobile/build_androidapp.go +++ b/cmd/gomobile/build_androidapp.go @@ -22,6 +22,9 @@ import ( ) func goAndroidBuild(pkg *build.Package, androidArchs []string) (map[string]bool, error) { + if ndkRoot == "" { + 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.") + } appName := path.Base(pkg.ImportPath) libName := androidPkgName(appName) manifestPath := filepath.Join(pkg.Dir, "AndroidManifest.xml") @@ -170,13 +173,11 @@ func goAndroidBuild(pkg *build.Package, androidArchs []string) (map[string]bool, toolchain := ndk.Toolchain(arch) if nmpkgs[arch]["golang.org/x/mobile/exp/audio/al"] { dst := "lib/" + toolchain.abi + "/libopenal.so" - src := dst - if arch == "arm" { - src = "lib/armeabi/libopenal.so" - } else if arch == "arm64" { - src = "lib/arm64/libopenal.so" + src := filepath.Join(gomobilepath, dst) + if _, err := os.Stat(src); err != nil { + return nil, errors.New("the Android requires the golang.org/x/mobile/exp/audio/al, but the OpenAL libraries was not found. Please run gomobile init with the -openal flag pointing to an OpenAL source directory.") } - if err := apkwWriteFile(dst, filepath.Join(ndk.Root(), "openal/"+src)); err != nil { + if err := apkwWriteFile(dst, src); err != nil { return nil, err } } diff --git a/cmd/gomobile/build_test.go b/cmd/gomobile/build_test.go index 1234c7a..569441e 100644 --- a/cmd/gomobile/build_test.go +++ b/cmd/gomobile/build_test.go @@ -76,6 +76,7 @@ func TestAndroidBuild(t *testing.T) { buildX = true buildO = "basic.apk" buildTarget = "android" + ndkRoot = "/NDK" gopath = filepath.ToSlash(filepath.SplitList(os.Getenv("GOPATH"))[0]) if goos == "windows" { os.Setenv("HOMEDRIVE", "C:") @@ -100,7 +101,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/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang{{.EXE}} CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_ENABLED=1 GOARM=7 go build -pkgdir=$GOMOBILE/pkg_android_arm -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/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=/NDK/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot /NDK/platforms/android-15/arch-arm -gcc-toolchain /NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -L/NDK/platforms/android-15/arch-arm/usr/lib -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 go build -pkgdir=$GOMOBILE/pkg_android_arm -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/doc.go b/cmd/gomobile/doc.go index fb17b6a..5405a13 100644 --- a/cmd/gomobile/doc.go +++ b/cmd/gomobile/doc.go @@ -123,15 +123,20 @@ Usage: gomobile init [-u] -Init installs the Android C++ compiler toolchain and builds copies -of the Go standard library for mobile devices. +Init builds copies of the Go standard library for mobile devices. If ANDROID_HOME +is set and the Android NDK is available, Android support is built. If on Darwin +and the Xcode command line utilities are installed, iOS support is built. -When first run, it downloads part of the Android NDK. -The toolchain is installed in $GOPATH/pkg/gomobile. +The toolchains are installed in $GOPATH/pkg/gomobile. -The -u option forces download and installation of the new toolchain -even when the toolchain exists. +Init use the Android NDK installed by the Android SDK manager by default. Use the +-ndk flag to specify a custom location for the NDK. +If the -openal flag is specified, init also builds an Android version of OpenAL +from the source directory given. OpenAL enables support for gomobile build and +install with mobile apps using the golang.org/x/mobile/exp/audio/al package. +It needs cmake and, on Windows, nmake installed. If cmake is installed through +the Android SDK manager, init will use that. Compile android APK and install on device diff --git a/cmd/gomobile/env.go b/cmd/gomobile/env.go index 89395d1..fbddaba 100644 --- a/cmd/gomobile/env.go +++ b/cmd/gomobile/env.go @@ -26,6 +26,10 @@ var ( androidArmNM string darwinArmNM string + + ndkRoot string + + archs = []string{"arm", "arm64", "386", "amd64"} ) func buildEnvInit() (cleanup func(), err error) { @@ -38,10 +42,6 @@ func buildEnvInit() (cleanup func(), err error) { } } - if err := envInit(); err != nil { - return nil, err - } - if buildX { fmt.Fprintln(xout, "GOMOBILE="+gomobilepath) } @@ -51,6 +51,27 @@ func buildEnvInit() (cleanup func(), err error) { if gomobilepath == "" { return nil, errors.New("toolchain not installed, run `gomobile init`") } + + // Read the NDK root path stored by gomobile init -ndk, if any. + if !buildN { + root, err := ioutil.ReadFile(filepath.Join(gomobilepath, "android_ndk_root")) + if err != nil && !os.IsNotExist(err) { + return nil, err + } + ndkRoot = string(root) + if ndkRoot != "" { + if _, err := os.Stat(filepath.Join(ndkRoot, "toolchains")); err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("The ndk path %q doesn't exist. Please re-run gomobile with the ndk-bundle install through the Android SDK manager or with the -ndk flag set.", ndkRoot) + } + return nil, err + } + } + } + if err := envInit(); err != nil { + return nil, err + } + cleanupFn := func() { if buildWork { fmt.Printf("WORK=%s\n", tmpdir) @@ -92,34 +113,39 @@ func envInit() (err error) { // Setup the cross-compiler environments. - androidEnv = make(map[string][]string) - for arch, toolchain := range ndk { - if goVersion < toolchain.minGoVer { - continue - } + if ndkRoot != "" { + androidEnv = make(map[string][]string) + for arch, toolchain := range ndk { + if goVersion < toolchain.minGoVer { + continue + } - // 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) - androidEnv[arch] = []string{ - "GOOS=android", - "GOARCH=" + arch, - "CC=" + toolchain.Path("clang"), - "CXX=" + toolchain.Path("clang++"), - "CGO_CFLAGS=" + flags, - "CGO_CPPFLAGS=" + flags, - "CGO_LDFLAGS=" + flags, - "CGO_ENABLED=1", - } - if arch == "arm" { - androidEnv[arch] = append(androidEnv[arch], "GOARM=7") + // 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(ndkRoot, "platforms", toolchain.platform, "arch-"+toolchain.arch) + gcctoolchain := filepath.Join(ndkRoot, "toolchains", toolchain.gcc, "prebuilt", archNDK()) + flags := fmt.Sprintf("-target %s --sysroot %s -gcc-toolchain %s", target, sysroot, gcctoolchain) + cflags := fmt.Sprintf("%s -I%s/include", flags, gomobilepath) + ldflags := fmt.Sprintf("%s -L%s/usr/lib -L%s/lib/%s", flags, sysroot, gomobilepath, arch) + androidEnv[arch] = []string{ + "GOOS=android", + "GOARCH=" + arch, + "CC=" + toolchain.Path("clang"), + "CXX=" + toolchain.Path("clang++"), + "CGO_CFLAGS=" + cflags, + "CGO_CPPFLAGS=" + cflags, + "CGO_LDFLAGS=" + ldflags, + "CGO_ENABLED=1", + } + if arch == "arm" { + androidEnv[arch] = append(androidEnv[arch], "GOARM=7") + } } } @@ -264,6 +290,23 @@ func pkgdir(env []string) string { return gomobilepath + "/pkg_" + getenv(env, "GOOS") + "_" + getenv(env, "GOARCH") } +func archNDK() string { + if runtime.GOOS == "windows" && runtime.GOARCH == "386" { + return "windows" + } else { + var arch string + switch runtime.GOARCH { + case "386": + arch = "x86" + case "amd64": + arch = "x86_64" + default: + panic("unsupported GOARCH: " + runtime.GOARCH) + } + return runtime.GOOS + "-" + arch + } +} + type ndkToolchain struct { arch string abi string @@ -274,18 +317,24 @@ type ndkToolchain struct { } func (tc *ndkToolchain) Path(toolName string) string { + // The nm tool is located in the GCC directory structure. + isUtil := toolName == "nm" if goos == "windows" { toolName += ".exe" } - return filepath.Join(ndk.Root(), tc.arch, "bin", tc.toolPrefix+"-"+toolName) + path := filepath.Join(ndkRoot, "toolchains") + if isUtil { + toolName = tc.toolPrefix + "-" + toolName + path = filepath.Join(path, tc.gcc) + } else { + path = filepath.Join(path, "llvm") + } + path = filepath.Join(path, "prebuilt") + return filepath.Join(path, archNDK(), "bin", 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 { @@ -294,7 +343,6 @@ func (nc ndkConfig) Toolchain(arch string) ndkToolchain { return tc } -// TODO: share this with release.go var ndk = ndkConfig{ "arm": { arch: "arm", diff --git a/cmd/gomobile/hashes.go b/cmd/gomobile/hashes.go deleted file mode 100644 index f2fec62..0000000 --- a/cmd/gomobile/hashes.go +++ /dev/null @@ -1,19 +0,0 @@ -// 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 - -// Hashes were computed with 'go run release.go' - -var fetchHashes = map[string]string{ - "android-ndk-r12b-darwin-x86_64.zip": "2bdef9143a2c7680fcb7c9fd54fe85013d591f106aea43831eba5e13e10db77e", - "gomobile-ndk-r12b-darwin-x86_64.tar.gz": "2c4365d033fc3c9e1fbeeb6c2773f75116a0c76000ebf0dab7290506ba9acb07", - "android-ndk-r12b-linux-x86_64.zip": "eafae2d614e5475a3bcfd7c5f201db5b963cc1290ee3e8ae791ff0c66757781e", - "gomobile-ndk-r12b-linux-x86_64.tar.gz": "3eaeb237f89fa7ca44dbc760085a79a1efe9c6e830f3bedf68e5473bbd716fed", - "android-ndk-r12b-windows-x86.zip": "4b3b522775858bdf4e5e8f7365e06fdabb9913fb7b9f45d7010232f8271fb42c", - "gomobile-ndk-r12b-windows-x86.tar.gz": "2108256dac7fe7499c139915672c0f68eca1bcfe3d9660b1ef0f9c3e776459ee", - "android-ndk-r12b-windows-x86_64.zip": "a201b9dad71802464823dcfc77f61445ec1bbd8a29baa154d0a6ed84f50298ae", - "gomobile-ndk-r12b-windows-x86_64.tar.gz": "558b124266add3bced8591596911862fdf7949f689a79f612c3f9e48ef8a2199", - "gomobile-openal-soft-1.16.0.1-ndk-r12b.tar.gz": "848307aa48311a2d4f6cd05661724b121c8f8663d616dfc8c573f9e5d926cf1d", -} diff --git a/cmd/gomobile/init.go b/cmd/gomobile/init.go index 769d61e..5b8a5e0 100644 --- a/cmd/gomobile/init.go +++ b/cmd/gomobile/init.go @@ -5,34 +5,19 @@ package main import ( - "archive/tar" "bytes" - "compress/gzip" - "crypto/sha256" - "encoding/hex" + "errors" "fmt" "io" "io/ioutil" - "net/http" "os" "os/exec" - "path" "path/filepath" "runtime" "strings" "time" ) -// useStrippedNDK determines whether the init subcommand fetches the clang -// toolchain from the original Android NDK, or from the stripped-down NDK -// hosted specifically for the gomobile tool. -// -// There is a significant size different (400MB compared to 30MB). -var useStrippedNDK = true - -const ndkVersion = "ndk-r12b" -const openALVersion = "openal-soft-1.16.0.1-" + ndkVersion - var ( goos = runtime.GOOS goarch = runtime.GOARCH @@ -54,23 +39,26 @@ var cmdInit = &command{ run: runInit, Name: "init", Usage: "[-u]", - Short: "install android compiler toolchain", + Short: "install mobile compiler toolchain", Long: ` -Init installs the Android C++ compiler toolchain and builds copies -of the Go standard library for mobile devices. - -When first run, it downloads part of the Android NDK. -The toolchain is installed in $GOPATH/pkg/gomobile. - -The -u option forces download and installation of the new toolchain -even when the toolchain exists. +Init builds copies of the Go standard library for mobile devices. +It uses Xcode, if available, to build for iOS and uses the Android +NDK from the ndk-bundle SDK package or from the -ndk flag, to build +for Android. +If a OpenAL source directory is specified with -openal, init will +also build an Android version of OpenAL for use with gomobile build +and gomobile install. `, } -var initU bool // -u +var ( + initNDK string // -ndk + initOpenAL string // -openal +) func init() { - cmdInit.flag.BoolVar(&initU, "u", false, "force toolchain download") + cmdInit.flag.StringVar(&initNDK, "ndk", "", "Android NDK path") + cmdInit.flag.StringVar(&initOpenAL, "openal", "", "OpenAL source path") } func runInit(cmd *command) error { @@ -84,9 +72,9 @@ func runInit(cmd *command) error { if buildX || buildN { fmt.Fprintln(xout, "GOMOBILE="+gomobilepath) } - removeGomobilepkg() + removeAll(gomobilepath) - if err := mkdir(ndk.Root()); err != nil { + if err := mkdir(gomobilepath); err != nil { return err } @@ -110,17 +98,49 @@ func runInit(cmd *command) error { removeAll(tmpdir) }() + 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 + } + ndkFile := filepath.Join(gomobilepath, "android_ndk_root") + if err := ioutil.WriteFile(ndkFile, []byte(initNDK), 0644); err != nil { + return err + } + } + if initOpenAL != "" { + var err error + if initOpenAL, err = filepath.Abs(initOpenAL); err != nil { + return err + } + } + } + ndkRoot = initNDK if err := envInit(); err != nil { return err } - if err := fetchNDK(); err != nil { - return err - } - if err := fetchOpenAL(); err != nil { - return err - } - if runtime.GOOS == "darwin" { // Install common x/mobile packages for local development. // These are often slow to compile (due to cgo) and easy to forget. @@ -154,6 +174,10 @@ func runInit(cmd *command) error { return err } + if err := installOpenAL(gomobilepath); err != nil { + return err + } + if buildX || buildN { printcmd("go version > %s", verpath) } @@ -169,6 +193,121 @@ func runInit(cmd *command) error { return nil } +func installOpenAL(gomobilepath string) error { + if ndkRoot == "" || initOpenAL == "" { + return nil + } + sdkRoot := os.Getenv("ANDROID_HOME") + if sdkRoot == "" { + return nil + } + var cmake string + if buildN { + cmake = "cmake" + } else { + var err error + cmake, err = exec.LookPath("cmake") + if err != nil { + cmakePath := filepath.Join(sdkRoot, "cmake") + cmakeDir, err := os.Open(cmakePath) + if err != nil { + if os.IsNotExist(err) { + // Skip OpenAL install if the cmake package is not installed. + return errors.New("cmake was not found in the PATH. Please install it through the Android SDK manager.") + } + return err + } + defer cmakeDir.Close() + // There might be multiple versions of CMake installed. Use any one for now. + cmakeVers, err := cmakeDir.Readdirnames(1) + if err != nil || len(cmakeVers) == 0 { + return errors.New("cmake was not found in the PATH. Please install it through the Android SDK manager.") + } + cmake = filepath.Join(cmakePath, cmakeVers[0], "bin", "cmake") + } + } + var alTmpDir string + if buildN { + alTmpDir = filepath.Join(gomobilepath, "work") + } else { + var err error + alTmpDir, err = ioutil.TempDir(gomobilepath, "openal-release-") + if err != nil { + return err + } + defer removeAll(alTmpDir) + } + + for _, f := range []string{"include/AL/al.h", "include/AL/alc.h"} { + dst := filepath.Join(gomobilepath, f) + src := filepath.Join(initOpenAL, f) + if err := copyFile(dst, src); err != nil { + return err + } + } + + toolsDir := filepath.Join(ndkRoot, "prebuilt", archNDK(), "bin") + py27 := filepath.Join(toolsDir, "python2.7") + var make string + if !buildN && runtime.GOOS == "windows" { + var err error + make, err = exec.LookPath("nmake") + if err != nil { + return nil + } + } else { + make = filepath.Join(toolsDir, "make") + } + for _, arch := range archs { + t := ndk[arch] + abi := t.arch + if abi == "arm" { + abi = "armeabi" + } + // Split android-XX to get the api version. + platform := strings.SplitN(t.platform, "-", 2) + api := platform[1] + buildDir := alTmpDir + "/build/" + abi + toolchain := buildDir + "/toolchain" + // standalone ndk toolchains make openal-soft's build config easier. + cmd := exec.Command(py27, + "build/tools/make_standalone_toolchain.py", + "--arch="+t.arch, + "--api="+api, + "--install-dir="+toolchain) + cmd.Dir = ndkRoot + if err := runCmd(cmd); err != nil { + return err + } + + cmd = exec.Command(cmake, + initOpenAL, + "-DCMAKE_TOOLCHAIN_FILE="+initOpenAL+"/XCompile-Android.txt", + "-DHOST="+t.toolPrefix) + cmd.Dir = buildDir + orgPath := os.Getenv("PATH") + if !buildN { + cmd.Env = []string{"PATH=" + toolchain + "/bin" + string(os.PathListSeparator) + orgPath} + } + if err := runCmd(cmd); err != nil { + return err + } + + cmd = exec.Command(make) + cmd.Dir = buildDir + if err := runCmd(cmd); err != nil { + return err + } + + dst := filepath.Join(gomobilepath, "lib", t.abi, "libopenal.so") + src := filepath.Join(alTmpDir, "build", abi, "libopenal.so") + if err := copyFile(dst, src); err != nil { + return err + } + } + return nil +} + var commonPkgs = []string{ "golang.org/x/mobile/gl", "golang.org/x/mobile/app", @@ -225,44 +364,6 @@ func installPkg(pkg string, env []string, args ...string) error { return runCmd(cmd) } -func removeGomobilepkg() { - dir, err := os.Open(gomobilepath) - if err != nil { - return - } - names, err := dir.Readdirnames(-1) - if err != nil { - return - } - for _, name := range names { - if name == "dl" { - continue - } - removeAll(filepath.Join(gomobilepath, name)) - } -} - -func move(dst, src string, names ...string) error { - for _, name := range names { - srcf := filepath.Join(src, name) - dstf := filepath.Join(dst, name) - if buildX || buildN { - printcmd("mv %s %s", srcf, dstf) - } - if buildN { - continue - } - if goos == "windows" { - // os.Rename fails if dstf already exists. - removeAll(dstf) - } - if err := os.Rename(srcf, dstf); err != nil { - return err - } - } - return nil -} - func mkdir(dir string) error { if buildX || buildN { printcmd("mkdir -p %s", dir) @@ -296,280 +397,6 @@ func rm(name string) error { return os.Remove(name) } -func fetchOpenAL() error { - url := "https://dl.google.com/go/mobile/gomobile-" + openALVersion + ".tar.gz" - archive, err := fetch(url) - if err != nil { - return err - } - if err := extract("openal", archive); err != nil { - return err - } - if goos == "windows" { - resetReadOnlyFlagAll(filepath.Join(tmpdir, "openal")) - } - ndkroot := ndk.Root() - src := filepath.Join(tmpdir, "openal/include/AL") - for arch := range androidEnv { - toolchain := ndk.Toolchain(arch) - dst := filepath.Join(ndkroot, toolchain.arch+"/sysroot/usr/include/AL") - if buildX || buildN { - printcmd("cp -r %s %s", src, dst) - } - if buildN { - continue - } - if err := doCopyAll(dst, src); err != nil { - return err - } - } - libDst := filepath.Join(ndkroot, "openal") - libSrc := filepath.Join(tmpdir, "openal") - if err := mkdir(libDst); err != nil { - return nil - } - if err := move(libDst, libSrc, "lib"); err != nil { - return err - } - return nil -} - -func extract(dst, src string) error { - if buildX || buildN { - printcmd("tar xfz %s", src) - } - if buildN { - return nil - } - tf, err := os.Open(src) - if err != nil { - return err - } - defer tf.Close() - zr, err := gzip.NewReader(tf) - if err != nil { - return err - } - tr := tar.NewReader(zr) - for { - hdr, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return err - } - dst := filepath.Join(tmpdir, dst+"/"+hdr.Name) - if hdr.Typeflag == tar.TypeSymlink { - if err := symlink(hdr.Linkname, dst); err != nil { - return err - } - continue - } - if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { - return err - } - f, err := os.OpenFile(dst, os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.FileMode(hdr.Mode)&0777) - if err != nil { - return err - } - if _, err := io.Copy(f, tr); err != nil { - return err - } - if err := f.Close(); err != nil { - return err - } - } - return nil -} - -func fetchNDK() error { - if useStrippedNDK { - if err := fetchStrippedNDK(); err != nil { - return err - } - } else { - if err := fetchFullNDK(); err != nil { - return err - } - } - if goos == "windows" { - resetReadOnlyFlagAll(filepath.Join(tmpdir, "android-"+ndkVersion)) - } - - // Copy the cross compiling clang and clang++ compilers - llvmsrc := filepath.Join(tmpdir, fmt.Sprintf( - "android-%s/toolchains/llvm/prebuilt", ndkVersion)) - if goos == "windows" && ndkarch == "x86" { - llvmsrc = filepath.Join(llvmsrc, "windows") - } else { - llvmsrc = filepath.Join(llvmsrc, goos+"-"+ndkarch) - } - llvmdst := filepath.Join(ndk.Root(), "llvm") - if err := mkdir(llvmdst); err != nil { - return err - } - if err := move(llvmdst, llvmsrc, "lib64", "bin"); err != nil { - return err - } - - for arch := range androidEnv { - toolchain := ndk.Toolchain(arch) - dst := filepath.Join(ndk.Root(), toolchain.arch) - dstSysroot := filepath.Join(dst, "sysroot") - if err := mkdir(dstSysroot); err != nil { - return err - } - - srcSysroot := filepath.Join(tmpdir, fmt.Sprintf( - "android-%s/platforms/%s/arch-%s", ndkVersion, toolchain.platform, toolchain.arch)) - if err := move(dstSysroot, srcSysroot, "usr"); err != nil { - return err - } - - ndkpath := filepath.Join(tmpdir, fmt.Sprintf( - "android-%s/toolchains/%s/prebuilt", ndkVersion, toolchain.gcc)) - if goos == "windows" && ndkarch == "x86" { - ndkpath = filepath.Join(ndkpath, "windows") - } else { - ndkpath = filepath.Join(ndkpath, goos+"-"+ndkarch) - } - if err := move(dst, ndkpath, "bin", "lib"); err != nil { - return err - } - - linkpath := filepath.Join(dst, toolchain.toolPrefix+"/bin") - if err := mkdir(linkpath); err != nil { - return err - } - - for _, name := range []string{"ld", "as", "nm"} { - if goos == "windows" { - name += ".exe" - } - if err := symlink(filepath.Join(dst, "bin", toolchain.toolPrefix+"-"+name), filepath.Join(linkpath, name)); err != nil { - return err - } - } - for _, toname := range []string{"clang", "clang++"} { - fromname := toname - if goos == "windows" { - if goarch == "386" { - if toname == "clang++" { - // there is no 32-bit version of clang++ - continue - } - fromname += "_32" - } - fromname += ".exe" - toname += ".exe" - } - if err := symlink(filepath.Join(llvmdst, "bin", fromname), filepath.Join(dst, "bin", toolchain.toolPrefix+"-"+toname)); err != nil { - return err - } - } - if err := symlink(filepath.Join(llvmdst, "lib64"), filepath.Join(dst, "lib64")); err != nil { - return err - } - } - return nil -} - -func fetchStrippedNDK() error { - url := "https://dl.google.com/go/mobile/gomobile-" + ndkVersion + "-" + goos + "-" + ndkarch + ".tar.gz" - archive, err := fetch(url) - if err != nil { - return err - } - return extract("", archive) -} - -func fetchFullNDK() error { - url := "https://dl.google.com/android/repository/android-" + ndkVersion + "-" + goos + "-" + ndkarch + ".zip" - archive, err := fetch(url) - if err != nil { - return err - } - var inflate *exec.Cmd - if goos != "windows" { - inflate = exec.Command("unzip", archive) - } else { - inflate = exec.Command("unzip.exe", archive) - } - inflate.Dir = tmpdir - return runCmd(inflate) -} - -// fetch reads a URL into $GOPATH/pkg/gomobile/dl and returns the path -// to the downloaded file. Downloading is skipped if the file is -// already present. -func fetch(url string) (dst string, err error) { - if err := mkdir(filepath.Join(gomobilepath, "dl")); err != nil { - return "", err - } - name := path.Base(url) - dst = filepath.Join(gomobilepath, "dl", name) - - // Use what's in the cache if force update is not required. - if !initU { - if buildX { - printcmd("stat %s", dst) - } - if _, err = os.Stat(dst); err == nil { - return dst, nil - } - } - if buildX { - printcmd("curl -o%s %s", dst, url) - } - if buildN { - return dst, nil - } - - if buildV { - fmt.Fprintf(os.Stderr, "Downloading %s.\n", url) - } - - f, err := ioutil.TempFile(tmpdir, "partial-"+name) - if err != nil { - return "", err - } - defer func() { - if err != nil { - f.Close() - os.Remove(f.Name()) - } - }() - hashw := sha256.New() - - resp, err := http.Get(url) - if err != nil { - return "", err - } - if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("error fetching %v, status: %v", url, resp.Status) - } else { - _, err = io.Copy(io.MultiWriter(hashw, f), resp.Body) - } - if err2 := resp.Body.Close(); err == nil { - err = err2 - } - if err != nil { - return "", err - } - if err = f.Close(); err != nil { - return "", err - } - hash := hex.EncodeToString(hashw.Sum(nil)) - if fetchHashes[name] != hash { - return "", fmt.Errorf("sha256 for %q: %v, want %v. Try 'gomobile clean'", name, hash, fetchHashes[name]) - } - if err = os.Rename(f.Name(), dst); err != nil { - return "", err - } - return dst, nil -} - func doCopyAll(dst, src string) error { return filepath.Walk(src, func(path string, info os.FileInfo, errin error) (err error) { if errin != nil { diff --git a/cmd/gomobile/init_test.go b/cmd/gomobile/init_test.go index cdb6024..43384ce 100644 --- a/cmd/gomobile/init_test.go +++ b/cmd/gomobile/init_test.go @@ -22,13 +22,11 @@ func TestInit(t *testing.T) { xout = os.Stderr buildN = false buildX = false - initU = false os.Setenv("GOPATH", gopathorig) }() xout = buf buildN = true buildX = true - initU = true // Test that first GOPATH element is chosen correctly. gopath = "/GOPATH1" @@ -75,7 +73,6 @@ func diffOutput(got string, wantTmpl *template.Template) (string, error) { } type outputData struct { - NDK string GOOS string GOARCH string GOPATH string @@ -88,7 +85,6 @@ type outputData struct { func defaultOutputData() outputData { data := outputData{ - NDK: ndkVersion, GOOS: goos, GOARCH: goarch, GOPATH: gopath, @@ -104,38 +100,40 @@ func defaultOutputData() outputData { } var initTmpl = template.Must(template.New("output").Parse(`GOMOBILE={{.GOPATH}}/pkg/gomobile -mkdir -p $GOMOBILE/android-{{.NDK}} -WORK={{.GOPATH}}/pkg/gomobile/work -mkdir -p $GOMOBILE/dl -curl -o$GOMOBILE/dl/gomobile-{{.NDK}}-{{.GOOS}}-{{.NDKARCH}}.tar.gz https://dl.google.com/go/mobile/gomobile-{{.NDK}}-{{.GOOS}}-{{.NDKARCH}}.tar.gz -tar xfz $GOMOBILE/dl/gomobile-{{.NDK}}-{{.GOOS}}-{{.NDKARCH}}.tar.gz -mkdir -p $GOMOBILE/android-{{.NDK}}/llvm -mv $WORK/android-{{.NDK}}/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/lib64 $GOMOBILE/android-{{.NDK}}/llvm/lib64 -mv $WORK/android-{{.NDK}}/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin $GOMOBILE/android-{{.NDK}}/llvm/bin -mkdir -p $GOMOBILE/android-{{.NDK}}/arm/sysroot -mv $WORK/android-{{.NDK}}/platforms/android-15/arch-arm/usr $GOMOBILE/android-{{.NDK}}/arm/sysroot/usr -mv $WORK/android-{{.NDK}}/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin $GOMOBILE/android-{{.NDK}}/arm/bin -mv $WORK/android-{{.NDK}}/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}}/lib $GOMOBILE/android-{{.NDK}}/arm/lib -mkdir -p $GOMOBILE/android-{{.NDK}}/arm/arm-linux-androideabi/bin -ln -s $GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-ld{{.EXE}} $GOMOBILE/android-{{.NDK}}/arm/arm-linux-androideabi/bin/ld{{.EXE}} -ln -s $GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-as{{.EXE}} $GOMOBILE/android-{{.NDK}}/arm/arm-linux-androideabi/bin/as{{.EXE}} -ln -s $GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-nm{{.EXE}} $GOMOBILE/android-{{.NDK}}/arm/arm-linux-androideabi/bin/nm{{.EXE}} -ln -s $GOMOBILE/android-{{.NDK}}/llvm/bin/clang $GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang -ln -s $GOMOBILE/android-{{.NDK}}/llvm/bin/clang++ $GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang++ -ln -s $GOMOBILE/android-{{.NDK}}/llvm/lib64 $GOMOBILE/android-{{.NDK}}/arm/lib64 -mkdir -p $GOMOBILE/dl -curl -o$GOMOBILE/dl/gomobile-openal-soft-1.16.0.1-{{.NDK}}.tar.gz https://dl.google.com/go/mobile/gomobile-openal-soft-1.16.0.1-{{.NDK}}.tar.gz -tar xfz $GOMOBILE/dl/gomobile-openal-soft-1.16.0.1-{{.NDK}}.tar.gz -cp -r $WORK/openal/include/AL $GOMOBILE/android-{{.NDK}}/arm/sysroot/usr/include/AL -mkdir -p $GOMOBILE/android-{{.NDK}}/openal -mv $WORK/openal/lib $GOMOBILE/android-{{.NDK}}/openal/lib{{if eq .GOOS "darwin"}} +rm -r -f "$GOMOBILE" +mkdir -p $GOMOBILE +WORK={{.GOPATH}}/pkg/gomobile/work{{if eq .GOOS "darwin"}} go install -x golang.org/x/mobile/gl go install -x golang.org/x/mobile/app go install -x golang.org/x/mobile/exp/app/debug{{end}} -GOOS=android GOARCH=arm CC=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang{{.EXE}} CXX=$GOMOBILE/android-{{.NDK}}/arm/bin/arm-linux-androideabi-clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot $GOMOBILE/android-{{.NDK}}/arm/sysroot CGO_ENABLED=1 GOARM=7 go install -pkgdir=$GOMOBILE/pkg_android_arm -x std +GOOS=android GOARCH=arm CC=$NDK_PATH/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang{{.EXE}} CXX=$NDK_PATH/toolchains/llvm/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/clang++{{.EXE}} CGO_CFLAGS=-target armv7a-none-linux-androideabi --sysroot $NDK_PATH/platforms/android-15/arch-arm -gcc-toolchain $NDK_PATH/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_CPPFLAGS=-target armv7a-none-linux-androideabi --sysroot $NDK_PATH/platforms/android-15/arch-arm -gcc-toolchain $NDK_PATH/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -I$GOMOBILE/include CGO_LDFLAGS=-target armv7a-none-linux-androideabi --sysroot $NDK_PATH/platforms/android-15/arch-arm -gcc-toolchain $NDK_PATH/toolchains/arm-linux-androideabi-4.9/prebuilt/{{.GOOS}}-{{.NDKARCH}} -L$NDK_PATH/platforms/android-15/arch-arm/usr/lib -L$GOMOBILE/lib/arm CGO_ENABLED=1 GOARM=7 go install -pkgdir=$GOMOBILE/pkg_android_arm -x std {{if eq .GOOS "darwin"}}GOOS=darwin GOARCH=arm GOARM=7 CC=clang-iphoneos CXX=clang-iphoneos CGO_CFLAGS=-isysroot=iphoneos -miphoneos-version-min=6.1 -arch armv7 CGO_LDFLAGS=-isysroot=iphoneos -miphoneos-version-min=6.1 -arch armv7 CGO_ENABLED=1 go install -pkgdir=$GOMOBILE/pkg_darwin_arm -x std GOOS=darwin GOARCH=arm64 CC=clang-iphoneos CXX=clang-iphoneos CGO_CFLAGS=-isysroot=iphoneos -miphoneos-version-min=6.1 -arch arm64 CGO_LDFLAGS=-isysroot=iphoneos -miphoneos-version-min=6.1 -arch arm64 CGO_ENABLED=1 go install -pkgdir=$GOMOBILE/pkg_darwin_arm64 -x std GOOS=darwin GOARCH=amd64 CC=clang-iphonesimulator CXX=clang-iphonesimulator CGO_CFLAGS=-isysroot=iphonesimulator -mios-simulator-version-min=6.1 -arch x86_64 CGO_LDFLAGS=-isysroot=iphonesimulator -mios-simulator-version-min=6.1 -arch x86_64 CGO_ENABLED=1 go install -tags=ios -pkgdir=$GOMOBILE/pkg_darwin_amd64 -x std -{{end}}go version > $GOMOBILE/version +{{end}}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 +PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=arm --api=15 --install-dir=$WORK/build/armeabi/toolchain +PWD=$WORK/build/armeabi cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=arm-linux-androideabi +PWD=$WORK/build/armeabi $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/make +cp $WORK/build/armeabi/libopenal.so $GOMOBILE/lib/armeabi-v7a/libopenal.so +mkdir -p $GOMOBILE/lib/armeabi-v7a +PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=arm64 --api=21 --install-dir=$WORK/build/arm64/toolchain +PWD=$WORK/build/arm64 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=aarch64-linux-android +PWD=$WORK/build/arm64 $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/make +cp $WORK/build/arm64/libopenal.so $GOMOBILE/lib/arm64-v8a/libopenal.so +mkdir -p $GOMOBILE/lib/arm64-v8a +PWD=$NDK_PATH $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/python2.7 build/tools/make_standalone_toolchain.py --arch=x86 --api=15 --install-dir=$WORK/build/x86/toolchain +PWD=$WORK/build/x86 cmake $OPENAL_PATH -DCMAKE_TOOLCHAIN_FILE=$OPENAL_PATH/XCompile-Android.txt -DHOST=i686-linux-android +PWD=$WORK/build/x86 $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/make +cp $WORK/build/x86/libopenal.so $GOMOBILE/lib/x86/libopenal.so +mkdir -p $GOMOBILE/lib/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=$WORK/build/x86_64/toolchain +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 $NDK_PATH/prebuilt/{{.GOOS}}-{{.NDKARCH}}/bin/make +cp $WORK/build/x86_64/libopenal.so $GOMOBILE/lib/x86_64/libopenal.so +mkdir -p $GOMOBILE/lib/x86_64 +go version > $GOMOBILE/version rm -r -f "$WORK" `)) diff --git a/cmd/gomobile/release.go b/cmd/gomobile/release.go deleted file mode 100644 index 589a124..0000000 --- a/cmd/gomobile/release.go +++ /dev/null @@ -1,489 +0,0 @@ -// 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. - -//+build ignore - -// Release is a tool for building the NDK tarballs hosted on dl.google.com. -// -// The Go toolchain only needs the gcc compiler and headers, which are ~10MB. -// The entire NDK is ~400MB. Building smaller toolchain binaries reduces the -// run time of gomobile init significantly. -package main - -import ( - "archive/tar" - "bufio" - "compress/gzip" - "crypto/sha256" - "encoding/hex" - "flag" - "fmt" - "hash" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" -) - -const ndkVersion = "ndk-r12b" - -type version struct { - os string - arch string -} - -var hosts = []version{ - {"darwin", "x86_64"}, - {"linux", "x86_64"}, - {"windows", "x86"}, - {"windows", "x86_64"}, -} - -type target struct { - arch string - platform int - gcc string - toolPrefix string -} - -var targets = []target{ - {"arm", 15, "arm-linux-androideabi-4.9", "arm-linux-androideabi"}, - {"arm64", 21, "aarch64-linux-android-4.9", "aarch64-linux-android"}, - {"x86", 15, "x86-4.9", "i686-linux-android"}, - {"x86_64", 21, "x86_64-4.9", "x86_64-linux-android"}, -} - -var ( - ndkdir = flag.String("ndkdir", "", "Directory for the downloaded NDKs for caching") - tmpdir string -) - -func main() { - flag.Parse() - var err error - tmpdir, err = ioutil.TempDir("", "gomobile-release-") - if err != nil { - log.Panic(err) - } - defer os.RemoveAll(tmpdir) - - fmt.Println("var fetchHashes = map[string]string{") - for _, host := range hosts { - if err := mkpkg(host); err != nil { - log.Panic(err) - } - } - if err := mkALPkg(); err != nil { - log.Panic(err) - } - - fmt.Println("}") -} - -func run(dir, path string, args ...string) error { - cmd := exec.Command(path, args...) - cmd.Dir = dir - buf, err := cmd.CombinedOutput() - if err != nil { - fmt.Printf("%s\n", buf) - } - return err -} - -func mkALPkg() (err error) { - ndkPath, _, err := fetchNDK(version{os: hostOS, arch: hostArch}) - if err != nil { - return err - } - ndkRoot := tmpdir + "/android-" + ndkVersion - if err := inflate(tmpdir, ndkPath); err != nil { - return err - } - - alTmpDir, err := ioutil.TempDir("", "openal-release-") - if err != nil { - return err - } - defer os.RemoveAll(alTmpDir) - - if err := run(alTmpDir, "git", "clone", "-v", "git://repo.or.cz/openal-soft.git", alTmpDir); err != nil { - return err - } - // TODO: use more recent revision? - if err := run(alTmpDir, "git", "checkout", "19f79be57b8e768f44710b6d26017bc1f8c8fbda"); err != nil { - return err - } - - files := map[string]string{ - "include/AL/al.h": "include/AL/al.h", - "include/AL/alc.h": "include/AL/alc.h", - "COPYING": "include/AL/COPYING", - } - - for _, t := range targets { - abi := t.arch - if abi == "arm" { - abi = "armeabi" - } - buildDir := alTmpDir + "/build/" + abi - toolchain := buildDir + "/toolchain" - // standalone ndk toolchains make openal-soft's build config easier. - if err := run(ndkRoot, "env", - "build/tools/make_standalone_toolchain.py", - "--arch="+t.arch, - "--api="+strconv.Itoa(t.platform), - "--install-dir="+toolchain); err != nil { - return fmt.Errorf("make_standalone_toolchain.py failed: %v", err) - } - - orgPath := os.Getenv("PATH") - os.Setenv("PATH", toolchain+"/bin"+string(os.PathListSeparator)+orgPath) - if err := run(buildDir, "cmake", - "../../", - "-DCMAKE_TOOLCHAIN_FILE=../../XCompile-Android.txt", - "-DHOST="+t.toolPrefix); err != nil { - return fmt.Errorf("cmake failed: %v", err) - } - os.Setenv("PATH", orgPath) - - if err := run(buildDir, "make"); err != nil { - return fmt.Errorf("make failed: %v", err) - } - - files["build/"+abi+"/libopenal.so"] = "lib/" + abi + "/libopenal.so" - } - - // Build the tarball. - aw := newArchiveWriter("gomobile-openal-soft-1.16.0.1-" + ndkVersion + ".tar.gz") - defer func() { - err2 := aw.Close() - if err == nil { - err = err2 - } - }() - - for src, dst := range files { - f, err := os.Open(filepath.Join(alTmpDir, src)) - if err != nil { - return err - } - fi, err := f.Stat() - if err != nil { - return err - } - aw.WriteHeader(&tar.Header{ - Name: dst, - Mode: int64(fi.Mode()), - Size: fi.Size(), - }) - io.Copy(aw, f) - f.Close() - } - return nil -} - -func fetchNDK(host version) (binPath, url string, err error) { - ndkName := "android-" + ndkVersion + "-" + host.os + "-" + host.arch + ".zip" - - url = "https://dl.google.com/android/repository/" + ndkName - binPath = *ndkdir - if binPath == "" { - binPath = tmpdir - } - binPath += "/" + ndkName - - if _, err := os.Stat(binPath); err == nil { - log.Printf("\t%q: using cached NDK\n", ndkName) - return binPath, url, nil - } - - log.Printf("%s\n", url) - binHash, err := fetch(binPath, url) - if err != nil { - return "", "", err - } - - fmt.Printf("\t%q: %q,\n", ndkName, binHash) - return binPath, url, nil -} - -func mkpkg(host version) error { - binPath, url, err := fetchNDK(host) - if err != nil { - return err - } - - src := tmpdir + "/" + host.os + "-" + host.arch + "-src" - dst := tmpdir + "/" + host.os + "-" + host.arch + "-dst" - defer os.RemoveAll(src) - defer os.RemoveAll(dst) - - if err := os.Mkdir(src, 0755); err != nil { - return err - } - - if err := inflate(src, binPath); err != nil { - return err - } - - // The NDK is unpacked into tmpdir/linux-x86_64-src/android-{{ndkVersion}}. - // Move the files we want into tmpdir/linux-x86_64-dst/android-{{ndkVersion}}. - // We preserve the same file layout to make the full NDK interchangable - // with the cut down file. - for _, t := range targets { - usr := fmt.Sprintf("android-%s/platforms/android-%d/arch-%s/usr/", ndkVersion, t.platform, t.arch) - gcc := fmt.Sprintf("android-%s/toolchains/%s/prebuilt/", ndkVersion, t.gcc) - - if host.os == "windows" && host.arch == "x86" { - gcc += "windows" - } else { - gcc += host.os + "-" + host.arch - } - - if err := os.MkdirAll(dst+"/"+usr, 0755); err != nil { - return err - } - if err := os.MkdirAll(dst+"/"+gcc+"/bin", 0755); err != nil { - return err - } - - subdirs := []string{"include", "lib"} - switch t.arch { - case "x86_64": - subdirs = append(subdirs, "lib64", "libx32") - } - if err := move(dst+"/"+usr, src+"/"+usr, subdirs...); err != nil { - return err - } - - if err := move(dst+"/"+gcc, src+"/"+gcc, "lib", "COPYING", "COPYING.LIB"); err != nil { - return err - } - for _, exe := range []string{"as", "ld", "nm"} { - if host.os == "windows" { - exe += ".exe" - } - if err := move(dst+"/"+gcc+"/bin", src+"/"+gcc+"/bin", t.toolPrefix+"-"+exe); err != nil { - return err - } - } - } - - // Copy the LLVM clang and clang++ compilers - llvm := fmt.Sprintf("android-%s/toolchains/llvm/prebuilt/", ndkVersion) - - if host.os == "windows" && host.arch == "x86" { - llvm += "windows" - } else { - llvm += host.os + "-" + host.arch - } - - if err := os.MkdirAll(dst+"/"+llvm, 0755); err != nil { - return err - } - - if err := move(dst+"/"+llvm, src+"/"+llvm, "bin", "lib64", "NOTICE"); err != nil { - return err - } - - // Build the tarball. - aw := newArchiveWriter("gomobile-" + ndkVersion + "-" + host.os + "-" + host.arch + ".tar.gz") - defer func() { - err2 := aw.Close() - if err == nil { - err = err2 - } - }() - - readme := "Stripped down copy of:\n\n\t" + url + "\n\nGenerated by golang.org/x/mobile/cmd/gomobile/release.go." - aw.WriteHeader(&tar.Header{ - Name: "README", - Mode: 0644, - Size: int64(len(readme)), - }) - io.WriteString(aw, readme) - - return filepath.Walk(dst, func(path string, fi os.FileInfo, err error) error { - defer func() { - if err != nil { - err = fmt.Errorf("%s: %v", path, err) - } - }() - if err != nil { - return err - } - if path == dst { - return nil - } - name := path[len(dst)+1:] - if fi.IsDir() { - return nil - } - if fi.Mode()&os.ModeSymlink != 0 { - dst, err := os.Readlink(path) - if err != nil { - log.Printf("bad symlink: %s", name) - return nil - } - aw.WriteHeader(&tar.Header{ - Name: name, - Linkname: dst, - Typeflag: tar.TypeSymlink, - }) - return nil - } - aw.WriteHeader(&tar.Header{ - Name: name, - Mode: int64(fi.Mode()), - Size: fi.Size(), - }) - f, err := os.Open(path) - if err != nil { - return err - } - io.Copy(aw, f) - f.Close() - return nil - }) -} - -func fetch(dst, url string) (string, error) { - f, err := os.OpenFile(dst, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0755) - if err != nil { - return "", err - } - resp, err := http.Get(url) - if err != nil { - return "", err - } - if sc := resp.StatusCode; sc != http.StatusOK { - return "", fmt.Errorf("invalid HTTP status %d", sc) - } - hashw := sha256.New() - _, err = io.Copy(io.MultiWriter(hashw, f), resp.Body) - err2 := resp.Body.Close() - err3 := f.Close() - if err != nil { - return "", err - } - if err2 != nil { - return "", err2 - } - if err3 != nil { - return "", err3 - } - return hex.EncodeToString(hashw.Sum(nil)), nil -} - -func inflate(dst, path string) error { - unzip := "unzip" - cmd := exec.Command(unzip, path) - cmd.Dir = dst - out, err := cmd.CombinedOutput() - if err != nil { - os.Stderr.Write(out) - return err - } - return nil -} - -func move(dst, src string, names ...string) error { - for _, name := range names { - if err := os.Rename(src+"/"+name, dst+"/"+name); err != nil { - return err - } - } - return nil -} - -// archiveWriter writes a .tar.gz archive and prints its SHA256 to stdout. -// If any error occurs, it continues as a no-op until Close, when it is reported. -type archiveWriter struct { - name string - hashw hash.Hash - f *os.File - zw *gzip.Writer - bw *bufio.Writer - tw *tar.Writer - err error -} - -func (aw *archiveWriter) WriteHeader(h *tar.Header) { - if aw.err != nil { - return - } - aw.err = aw.tw.WriteHeader(h) -} - -func (aw *archiveWriter) Write(b []byte) (n int, err error) { - if aw.err != nil { - return len(b), nil - } - n, aw.err = aw.tw.Write(b) - return n, nil -} - -func (aw *archiveWriter) Close() (err error) { - err = aw.tw.Close() - if aw.err == nil { - aw.err = err - } - err = aw.zw.Close() - if aw.err == nil { - aw.err = err - } - err = aw.bw.Flush() - if aw.err == nil { - aw.err = err - } - err = aw.f.Close() - if aw.err == nil { - aw.err = err - } - if aw.err != nil { - return aw.err - } - hash := hex.EncodeToString(aw.hashw.Sum(nil)) - fmt.Printf("\t%q: %q,\n", aw.name, hash) - return nil -} - -func newArchiveWriter(name string) *archiveWriter { - aw := &archiveWriter{name: name} - aw.f, aw.err = os.Create(name) - if aw.err != nil { - return aw - } - aw.hashw = sha256.New() - aw.bw = bufio.NewWriter(io.MultiWriter(aw.f, aw.hashw)) - aw.zw, aw.err = gzip.NewWriterLevel(aw.bw, gzip.BestCompression) - if aw.err != nil { - return aw - } - aw.tw = tar.NewWriter(aw.zw) - return aw -} - -var hostOS, hostArch string - -func init() { - switch runtime.GOOS { - case "linux", "darwin": - hostOS = runtime.GOOS - } - switch runtime.GOARCH { - case "386": - hostArch = "x86" - case "amd64": - hostArch = "x86_64" - } - if hostOS == "" || hostArch == "" { - panic(fmt.Sprintf("cannot run release from OS/Arch: %s/%s", hostOS, hostArch)) - } -}