mobile/cmd/gomobile: use NDK r12

Also:

- Replace GCC with Clang. GCC is slated for removal in future NDK
releases.
- Replace the deprecated obsoleted make-standalone-toolchain.sh
script with the new make_standalone_toolchain.py script.
- Add the NDK version to OpenAL tarball name, to avoid name clashes
with previous builds of the same OpenAL version.
- Removed obsolete workaround for a linker problem with NDK r10c.
- Update the URLs and unpack logic for the full NDK mode.

Change-Id: Ifeec6fee624862ba2ff2d4520dab42f800414f7b
Reviewed-on: https://go-review.googlesource.com/24490
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/cmd/gomobile/env.go b/cmd/gomobile/env.go
index 4c65a65..89395d1 100644
--- a/cmd/gomobile/env.go
+++ b/cmd/gomobile/env.go
@@ -98,11 +98,24 @@
 			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("gcc"),
-			"CXX=" + toolchain.Path("g++"),
+			"CC=" + toolchain.Path("clang"),
+			"CXX=" + toolchain.Path("clang++"),
+			"CGO_CFLAGS=" + flags,
+			"CGO_CPPFLAGS=" + flags,
+			"CGO_LDFLAGS=" + flags,
 			"CGO_ENABLED=1",
 		}
 		if arch == "arm" {
diff --git a/cmd/gomobile/hashes.go b/cmd/gomobile/hashes.go
index 0771196..41e8f9e 100644
--- a/cmd/gomobile/hashes.go
+++ b/cmd/gomobile/hashes.go
@@ -7,13 +7,13 @@
 // Hashes were computed with 'go run release.go'
 
 var fetchHashes = map[string]string{
-	"android-ndk-r11c-darwin-x86_64.zip":      "fe2f8986074717240df45f03e93a4436dac2040dc12fecee4853953d584424b3",
-	"gomobile-ndk-r11c-darwin-x86_64.tar.gz":  "1d563c5c27dcb3643eb3cbd4e44b535aec052c600350d6203374e0100b1d75ff",
-	"android-ndk-r11c-linux-x86_64.zip":       "ba85dbe4d370e4de567222f73a3e034d85fc3011b3cbd90697f3e8dcace3ad94",
-	"gomobile-ndk-r11c-linux-x86_64.tar.gz":   "eb0a657e03dad70dce33281414d2fdf38f2b313a8a1a4323d0648e9d16198d74",
-	"android-ndk-r11c-windows-x86.zip":        "eea7d148cf046baad2b8f3fd8e4a27d3695964079db0da6b9fca08051bb4dccb",
-	"gomobile-ndk-r11c-windows-x86.tar.gz":    "7f6b53f55993aa0deccace0ab2829ebb0e93d6bf2196a964f63bfde46da95d9e",
-	"android-ndk-r11c-windows-x86_64.zip":     "55c69f1d5a3602b3f6d6cea280b16938f17d6a4b509af01641d4db1280088d90",
-	"gomobile-ndk-r11c-windows-x86_64.tar.gz": "b3e1e17ec6d19b7512e6b5277fccc3a61e4c4b6bab6c4a3874d856c0332a61db",
-	"gomobile-openal-soft-1.16.0.1.tar.gz":    "84c9361734902df622dd49a8c0cfb0090fd7743a2cbf927a9ae85c4826beb173",
+	"android-ndk-r12-darwin-x86_64.zip":            "fe10b64a65f3e818b852862975ea7b5896295cd1301d1acdbeb1294d9592fb19",
+	"gomobile-ndk-r12-darwin-x86_64.tar.gz":        "861c5b6d464923f4bac16f47999d838a6a15184b17f5482049e52949dfece44c",
+	"android-ndk-r12-linux-x86_64.zip":             "7876e3b99f3596a3215ecf4e9f152d24b82dfdf2bbe7d3a38c423ae6a3edee79",
+	"gomobile-ndk-r12-linux-x86_64.tar.gz":         "ffa4b99736b8df22f26027167ab5e6e3d1d74aaf838622009134cd022b9bc9e2",
+	"android-ndk-r12-windows-x86.zip":              "72a18b670086601e827f6fe4f49e31239f969138ddc9468800e64d6a4fc5bd81",
+	"gomobile-ndk-r12-windows-x86.tar.gz":          "ed201197b87bb1ed830d5ac5cdbccf65b1833d8b338a82804e7d6a3dc8da80cd",
+	"android-ndk-r12-windows-x86_64.zip":           "85c5d6d43a04b43cd004771394db37e8f7a7a074ce4c7db7476880cc1a41607f",
+	"gomobile-ndk-r12-windows-x86_64.tar.gz":       "f9ccdf1687a41a4dddf221c451f43fb8cd5729d8a40d9c61da3d8c86b352b1b0",
+	"gomobile-openal-soft-1.16.0.1-ndk-r12.tar.gz": "ede844ee14491a45686657998a12ccb591e7958332fb099581018a11697ffbb2",
 }
diff --git a/cmd/gomobile/init.go b/cmd/gomobile/init.go
index 94fa759..90ce965 100644
--- a/cmd/gomobile/init.go
+++ b/cmd/gomobile/init.go
@@ -23,15 +23,15 @@
 	"time"
 )
 
-// useStrippedNDK determines whether the init subcommand fetches the GCC
+// 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-r11c"
-const openALVersion = "openal-soft-1.16.0.1"
+const ndkVersion = "ndk-r12"
+const openALVersion = "openal-soft-1.16.0.1-" + ndkVersion
 
 var (
 	goos    = runtime.GOOS
@@ -399,6 +399,26 @@
 		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")
+	llvmbin := filepath.Join(llvmdst, "bin")
+	if err := mkdir(llvmbin); err != nil {
+		return err
+	}
+	if err := move(llvmbin, filepath.Join(llvmsrc, "bin"), "clang", "clang++"); err != nil {
+		return err
+	}
+	if err := move(llvmdst, llvmsrc, "lib64"); err != nil {
+		return err
+	}
+
 	for arch := range androidEnv {
 		toolchain := ndk.Toolchain(arch)
 		dst := filepath.Join(ndk.Root(), toolchain.arch)
@@ -420,35 +440,16 @@
 		} else {
 			ndkpath = filepath.Join(ndkpath, goos+"-"+ndkarch)
 		}
-		if err := move(dst, ndkpath, "bin", "lib", "libexec"); err != nil {
+		if err := move(dst, ndkpath, "bin", "lib"); err != nil {
 			return err
 		}
 
-		// ndk-r10e arm64 toolchain has a bug in ld.bfd and for aarch64
-		// ld.bfd is the default linker. Workaround by switching the
-		// default to ld.gold.
-		// TODO(hyangah): remove this when using the new version of ndk.
-		if toolchain.arch == "arm64" {
-			ld := filepath.Join(dst, "bin/aarch64-linux-android-ld")
-			ldgold := ld + ".gold"
-			if goos == "windows" {
-				ld += ".exe"
-				ldgold += ".exe"
-			}
-			if err := rm(ld); err != nil {
-				return err
-			}
-			if err := symlink(ldgold, ld); 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", "gcc", "g++"} {
+		for _, name := range []string{"ld", "as"} {
 			if goos == "windows" {
 				name += ".exe"
 			}
@@ -456,6 +457,14 @@
 				return err
 			}
 		}
+		for _, name := range []string{"clang", "clang++"} {
+			if goos == "windows" {
+				name += ".exe"
+			}
+			if err := symlink(filepath.Join(llvmbin, name), filepath.Join(dst, "bin", toolchain.toolPrefix+"-"+name)); err != nil {
+				return err
+			}
+		}
 	}
 	return nil
 }
@@ -470,35 +479,16 @@
 }
 
 func fetchFullNDK() error {
-	url := "https://dl.google.com/android/ndk/android-" + ndkVersion + "-" + goos + "-" + ndkarch + "."
-	if goos == "windows" {
-		url += "exe"
-	} else {
-		url += "bin"
-	}
+	url := "https://dl.google.com/android/repository/android-" + ndkVersion + "-" + goos + "-" + ndkarch + ".zip"
 	archive, err := fetch(url)
 	if err != nil {
 		return err
 	}
-
-	// The self-extracting ndk dist file for Windows terminates
-	// with an error (error code 2 - corrupted or incomplete file)
-	// but there are no details on what caused this.
-	//
-	// Strangely, if the file is launched from file browser or
-	// unzipped with 7z.exe no error is reported.
-	//
-	// In general we use the stripped NDK, so this code path
-	// is not used, and 7z.exe is not a normal dependency.
 	var inflate *exec.Cmd
 	if goos != "windows" {
-		// The downloaded archive is executed on linux and os x to unarchive.
-		// To do this execute permissions are needed.
-		os.Chmod(archive, 0755)
-
-		inflate = exec.Command(archive)
+		inflate = exec.Command("unzip", archive)
 	} else {
-		inflate = exec.Command("7z.exe", "x", archive)
+		inflate = exec.Command("unzip.exe", archive)
 	}
 	inflate.Dir = tmpdir
 	return runCmd(inflate)
diff --git a/cmd/gomobile/release.go b/cmd/gomobile/release.go
index 54288b1..6dfac02 100644
--- a/cmd/gomobile/release.go
+++ b/cmd/gomobile/release.go
@@ -28,9 +28,10 @@
 	"os/exec"
 	"path/filepath"
 	"runtime"
+	"strconv"
 )
 
-const ndkVersion = "ndk-r11c"
+const ndkVersion = "ndk-r12"
 
 type version struct {
 	os   string
@@ -46,16 +47,16 @@
 
 type target struct {
 	arch       string
-	platform   string
+	platform   int
 	gcc        string
 	toolPrefix string
 }
 
 var targets = []target{
-	{"arm", "android-15", "arm-linux-androideabi-4.9", "arm-linux-androideabi"},
-	{"arm64", "android-21", "aarch64-linux-android-4.9", "aarch64-linux-android"},
-	{"x86", "android-15", "x86-4.9", "i686-linux-android"},
-	{"x86_64", "android-21", "x86_64-4.9", "x86_64-linux-android"},
+	{"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 (
@@ -132,16 +133,13 @@
 		}
 		buildDir := alTmpDir + "/build/" + abi
 		toolchain := buildDir + "/toolchain"
-		if err := os.MkdirAll(toolchain, 0755); err != nil {
-			return err
-		}
 		// standalone ndk toolchains make openal-soft's build config easier.
 		if err := run(ndkRoot, "env",
-			"build/tools/make-standalone-toolchain.sh",
+			"build/tools/make_standalone_toolchain.py",
 			"--arch="+t.arch,
-			"--platform="+t.platform,
+			"--api="+strconv.Itoa(t.platform),
 			"--install-dir="+toolchain); err != nil {
-			return fmt.Errorf("make-standalone-toolchain.sh failed: %v", err)
+			return fmt.Errorf("make_standalone_toolchain.py failed: %v", err)
 		}
 
 		orgPath := os.Getenv("PATH")
@@ -162,7 +160,7 @@
 	}
 
 	// Build the tarball.
-	aw := newArchiveWriter("gomobile-openal-soft-1.16.0.1.tar.gz")
+	aw := newArchiveWriter("gomobile-openal-soft-1.16.0.1-" + ndkVersion + ".tar.gz")
 	defer func() {
 		err2 := aw.Close()
 		if err == nil {
@@ -239,7 +237,7 @@
 	// 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/%s/arch-%s/usr/", ndkVersion, t.platform, t.arch)
+		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" {
@@ -251,7 +249,7 @@
 		if err := os.MkdirAll(dst+"/"+usr, 0755); err != nil {
 			return err
 		}
-		if err := os.MkdirAll(dst+"/"+gcc, 0755); err != nil {
+		if err := os.MkdirAll(dst+"/"+gcc+"/bin", 0755); err != nil {
 			return err
 		}
 
@@ -264,9 +262,34 @@
 			return err
 		}
 
-		if err := move(dst+"/"+gcc, src+"/"+gcc, "bin", "lib", "libexec", "COPYING", "COPYING.LIB"); err != nil {
+		if err := move(dst+"/"+gcc, src+"/"+gcc, "lib", "COPYING", "COPYING.LIB"); err != nil {
 			return err
 		}
+		for _, exe := range []string{"as", "ld"} {
+			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.