app,cmd/gomobile,exp/gl/glutil: support target architectures for iOS build and bind

While we're here, add 386 to the list of supported architectures
on iOS.

To support gomobile build for amd64 and 386, use the "ios" tag to
distinguish between iOS and macOS builds.

Change-Id: Ie09a432794bd8d9853950115349f8d3b57cf43f5
Reviewed-on: https://go-review.googlesource.com/102915
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/app/darwin_amd64.go b/app/darwin_desktop.go
similarity index 100%
rename from app/darwin_amd64.go
rename to app/darwin_desktop.go
diff --git a/app/darwin_amd64.m b/app/darwin_desktop.m
similarity index 100%
rename from app/darwin_amd64.m
rename to app/darwin_desktop.m
diff --git a/app/darwin_armx.go b/app/darwin_ios.go
similarity index 100%
rename from app/darwin_armx.go
rename to app/darwin_ios.go
diff --git a/app/darwin_armx.m b/app/darwin_ios.m
similarity index 100%
rename from app/darwin_armx.m
rename to app/darwin_ios.m
diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go
index e5e1e57..adbf66e 100644
--- a/cmd/gomobile/bind.go
+++ b/cmd/gomobile/bind.go
@@ -133,8 +133,7 @@
 		if !xcodeAvailable() {
 			return fmt.Errorf("-target=ios requires XCode")
 		}
-		// TODO: use targetArchs?
-		return goIOSBind(gobind, pkgs)
+		return goIOSBind(gobind, pkgs, targetArchs)
 	default:
 		return fmt.Errorf(`invalid -target=%q`, buildTarget)
 	}
diff --git a/cmd/gomobile/bind_iosapp.go b/cmd/gomobile/bind_iosapp.go
index c7f327a..6d95def 100644
--- a/cmd/gomobile/bind_iosapp.go
+++ b/cmd/gomobile/bind_iosapp.go
@@ -15,7 +15,7 @@
 	"text/template"
 )
 
-func goIOSBind(gobind string, pkgs []*build.Package) error {
+func goIOSBind(gobind string, pkgs []*build.Package, archs []string) error {
 	// Run gobind to generate the bindings
 	cmd := exec.Command(
 		gobind,
@@ -56,14 +56,14 @@
 
 	cmd = exec.Command("xcrun", "lipo", "-create")
 
-	for _, env := range [][]string{darwinArmEnv, darwinArm64Env, darwinAmd64Env} {
+	for _, arch := range archs {
+		env := darwinEnv[arch]
 		env = append(env, gopath)
-		arch := archClang(getenv(env, "GOARCH"))
 		path, err := goIOSBindArchive(name, env)
 		if err != nil {
 			return fmt.Errorf("darwin-%s: %v", arch, err)
 		}
-		cmd.Args = append(cmd.Args, "-arch", arch, path)
+		cmd.Args = append(cmd.Args, "-arch", archClang(arch), path)
 	}
 
 	// Build static framework output directory.
diff --git a/cmd/gomobile/build.go b/cmd/gomobile/build.go
index 6ee55f2..bdd3fe0 100644
--- a/cmd/gomobile/build.go
+++ b/cmd/gomobile/build.go
@@ -111,17 +111,19 @@
 			return err
 		}
 	case "darwin":
-		// TODO: use targetArchs?
 		if !xcodeAvailable() {
 			return fmt.Errorf("-target=ios requires XCode")
 		}
 		if pkg.Name != "main" {
-			if err := goBuild(pkg.ImportPath, darwinArmEnv); err != nil {
-				return err
+			for _, arch := range targetArchs {
+				env := darwinEnv[arch]
+				if err := goBuild(pkg.ImportPath, env); err != nil {
+					return err
+				}
 			}
-			return goBuild(pkg.ImportPath, darwinArm64Env)
+			return nil
 		}
-		nmpkgs, err = goIOSBuild(pkg, buildBundleID)
+		nmpkgs, err = goIOSBuild(pkg, buildBundleID, targetArchs)
 		if err != nil {
 			return err
 		}
@@ -329,16 +331,8 @@
 	}
 
 	// verify all archs are supported one while deduping.
-	var supported []string
-	switch os {
-	case "ios":
-		supported = []string{"arm", "arm64", "amd64"}
-	case "android":
-		supported = []string{"arm", "arm64", "386", "amd64"}
-	}
-
 	isSupported := func(arch string) bool {
-		for _, a := range supported {
+		for _, a := range allArchs {
 			if a == arch {
 				return true
 			}
@@ -364,7 +358,7 @@
 		targetOS = "darwin"
 	}
 	if all {
-		return targetOS, supported, nil
+		return targetOS, allArchs, nil
 	}
 	return targetOS, archs, nil
 }
diff --git a/cmd/gomobile/build_darwin_test.go b/cmd/gomobile/build_darwin_test.go
index d375a7a..3434952 100644
--- a/cmd/gomobile/build_darwin_test.go
+++ b/cmd/gomobile/build_darwin_test.go
@@ -76,7 +76,7 @@
 echo "{{template "infoplist" .Xinfo}}" > $WORK/main/Info.plist
 mkdir -p $WORK/main/Images.xcassets/AppIcon.appiconset
 echo "{{.Xcontents}}" > $WORK/main/Images.xcassets/AppIcon.appiconset/Contents.json
-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 build -tags tag1 ios -x -o=$WORK/arm golang.org/x/mobile/example/basic
+GOARM=7 GOOS=darwin GOARCH=arm 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 build -tags tag1 ios -x -o=$WORK/arm golang.org/x/mobile/example/basic
 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 build -tags tag1 ios -x -o=$WORK/arm64 golang.org/x/mobile/example/basic
 xcrun lipo -create $WORK/arm $WORK/arm64 -o $WORK/main/main
 mkdir -p $WORK/main/assets
diff --git a/cmd/gomobile/build_iosapp.go b/cmd/gomobile/build_iosapp.go
index 3146173..67455f9 100644
--- a/cmd/gomobile/build_iosapp.go
+++ b/cmd/gomobile/build_iosapp.go
@@ -19,7 +19,7 @@
 	"text/template"
 )
 
-func goIOSBuild(pkg *build.Package, bundleID string) (map[string]bool, error) {
+func goIOSBuild(pkg *build.Package, bundleID string, archs []string) (map[string]bool, error) {
 	src := pkg.ImportPath
 	if buildO != "" && !strings.HasSuffix(buildO, ".app") {
 		return nil, fmt.Errorf("-o must have an .app for -target=ios")
@@ -64,29 +64,28 @@
 
 	ctx.BuildTags = append(ctx.BuildTags, "ios")
 
-	armPath := filepath.Join(tmpdir, "arm")
-	if err := goBuild(src, darwinArmEnv, "-o="+armPath); err != nil {
-		return nil, err
-	}
-	nmpkgs, err := extractPkgs(darwinArmNM, armPath)
-	if err != nil {
-		return nil, err
-	}
-
-	arm64Path := filepath.Join(tmpdir, "arm64")
-	if err := goBuild(src, darwinArm64Env, "-o="+arm64Path); err != nil {
-		return nil, err
-	}
-
-	// Apple requires builds to target both darwin/arm and darwin/arm64.
 	// We are using lipo tool to build multiarchitecture binaries.
-	// TODO(jbd): Investigate the new announcements about iO9's fat binary
-	// size limitations are breaking this feature.
 	cmd := exec.Command(
 		"xcrun", "lipo",
-		"-create", armPath, arm64Path,
 		"-o", filepath.Join(tmpdir, "main/main"),
+		"-create",
 	)
+	var nmpkgs map[string]bool
+	for _, arch := range archs {
+		path := filepath.Join(tmpdir, arch)
+		if err := goBuild(src, darwinEnv[arch], "-o="+path); err != nil {
+			return nil, err
+		}
+		if nmpkgs == nil {
+			var err error
+			nmpkgs, err = extractPkgs(darwinArmNM, path)
+			if err != nil {
+				return nil, err
+			}
+		}
+		cmd.Args = append(cmd.Args, path)
+	}
+
 	if err := runCmd(cmd); err != nil {
 		return nil, err
 	}
diff --git a/cmd/gomobile/build_test.go b/cmd/gomobile/build_test.go
index f353f7b..eb6ef58 100644
--- a/cmd/gomobile/build_test.go
+++ b/cmd/gomobile/build_test.go
@@ -109,8 +109,7 @@
 `))
 
 func TestParseBuildTargetFlag(t *testing.T) {
-	androidArchs := "arm,arm64,386,amd64"
-	iosArchs := "arm,arm64,amd64"
+	archs := strings.Join(allArchs, ",")
 
 	tests := []struct {
 		in        string
@@ -118,12 +117,12 @@
 		wantOS    string
 		wantArchs string
 	}{
-		{"android", false, "android", androidArchs},
-		{"android,android/arm", false, "android", androidArchs},
+		{"android", false, "android", archs},
+		{"android,android/arm", false, "android", archs},
 		{"android/arm", false, "android", "arm"},
 
-		{"ios", false, "darwin", iosArchs},
-		{"ios,ios/arm", false, "darwin", iosArchs},
+		{"ios", false, "darwin", archs},
+		{"ios,ios/arm", false, "darwin", archs},
 		{"ios/arm", false, "darwin", "arm"},
 		{"ios/amd64", false, "darwin", "amd64"},
 
diff --git a/cmd/gomobile/env.go b/cmd/gomobile/env.go
index 45bd9a1..8714dbb 100644
--- a/cmd/gomobile/env.go
+++ b/cmd/gomobile/env.go
@@ -18,15 +18,12 @@
 
 	androidEnv map[string][]string // android arch -> []string
 
-	darwinArmEnv   []string
-	darwinArm64Env []string
-	darwin386Env   []string
-	darwinAmd64Env []string
+	darwinEnv map[string][]string
 
 	androidArmNM string
 	darwinArmNM  string
 
-	archs = []string{"arm", "arm64", "386", "amd64"}
+	allArchs = []string{"arm", "arm64", "386", "amd64"}
 )
 
 func buildEnvInit() (cleanup func(), err error) {
@@ -104,52 +101,38 @@
 		return nil
 	}
 
-	clang, cflags, err := envClang("iphoneos")
-	if err != nil {
-		return err
-	}
-	darwinArmEnv = []string{
-		"GOOS=darwin",
-		"GOARCH=arm",
-		"GOARM=7",
-		"CC=" + clang,
-		"CXX=" + clang,
-		"CGO_CFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm"),
-		"CGO_LDFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm"),
-		"CGO_ENABLED=1",
-	}
 	darwinArmNM = "nm"
-	darwinArm64Env = []string{
-		"GOOS=darwin",
-		"GOARCH=arm64",
-		"CC=" + clang,
-		"CXX=" + clang,
-		"CGO_CFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm64"),
-		"CGO_LDFLAGS=" + cflags + " -miphoneos-version-min=6.1 -arch " + archClang("arm64"),
-		"CGO_ENABLED=1",
-	}
-
-	clang, cflags, err = envClang("iphonesimulator")
-	if err != nil {
-		return err
-	}
-	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",
+	darwinEnv = make(map[string][]string)
+	for _, arch := range allArchs {
+		var env []string
+		var err error
+		var clang, cflags string
+		switch arch {
+		case "arm":
+			env = append(env, "GOARM=7")
+			fallthrough
+		case "arm64":
+			clang, cflags, err = envClang("iphoneos")
+			cflags += " -miphoneos-version-min=6.1"
+		case "386", "amd64":
+			clang, cflags, err = envClang("iphonesimulator")
+			cflags += " -mios-simulator-version-min=6.1"
+		default:
+			panic(fmt.Errorf("unknown GOARCH: %q", arch))
+		}
+		if err != nil {
+			return err
+		}
+		env = append(env,
+			"GOOS=darwin",
+			"GOARCH="+arch,
+			"CC="+clang,
+			"CXX="+clang,
+			"CGO_CFLAGS="+cflags+" -arch "+archClang(arch),
+			"CGO_LDFLAGS="+cflags+" -arch "+archClang(arch),
+			"CGO_ENABLED=1",
+		)
+		darwinEnv[arch] = env
 	}
 
 	return nil
@@ -181,7 +164,6 @@
 		return "", "", fmt.Errorf("xcrun --show-sdk-path: %v\n%s", err, out)
 	}
 	sdk := strings.TrimSpace(string(out))
-
 	return clang, "-isysroot " + sdk, nil
 }
 
diff --git a/cmd/gomobile/init.go b/cmd/gomobile/init.go
index 10e7370..fba9308 100644
--- a/cmd/gomobile/init.go
+++ b/cmd/gomobile/init.go
@@ -151,7 +151,7 @@
 	}
 	toolsDir := filepath.Join(initNDK, "prebuilt", archNDK(), "bin")
 	py27 := filepath.Join(toolsDir, "python2.7")
-	for _, arch := range archs {
+	for _, arch := range allArchs {
 		t := ndk[arch]
 		// Split android-XX to get the api version.
 		platform := strings.SplitN(t.platform, "-", 2)
@@ -226,7 +226,7 @@
 		}
 	}
 
-	for _, arch := range archs {
+	for _, arch := range allArchs {
 		t := ndk[arch]
 		abi := t.arch
 		if abi == "arm" {
diff --git a/exp/gl/glutil/context_darwin_amd64.go b/exp/gl/glutil/context_darwin_desktop.go
similarity index 100%
rename from exp/gl/glutil/context_darwin_amd64.go
rename to exp/gl/glutil/context_darwin_desktop.go