func goAppleBuild()

in cmd/gomobile/build_apple.go [23:162]


func goAppleBuild(pkg *packages.Package, bundleID string, targets []targetInfo) (map[string]bool, error) {
	src := pkg.PkgPath
	if buildO != "" && !strings.HasSuffix(buildO, ".app") {
		return nil, fmt.Errorf("-o must have an .app for -target=ios")
	}

	productName := rfc1034Label(path.Base(pkg.PkgPath))
	if productName == "" {
		productName = "ProductName" // like xcode.
	}

	infoplist := new(bytes.Buffer)
	if err := infoplistTmpl.Execute(infoplist, infoplistTmplData{
		BundleID: bundleID + "." + productName,
		Name:     strings.Title(path.Base(pkg.PkgPath)),
	}); err != nil {
		return nil, err
	}

	// Detect the team ID
	teamID, err := detectTeamID()
	if err != nil {
		return nil, err
	}

	projPbxproj := new(bytes.Buffer)
	if err := projPbxprojTmpl.Execute(projPbxproj, projPbxprojTmplData{
		TeamID: teamID,
	}); err != nil {
		return nil, err
	}

	files := []struct {
		name     string
		contents []byte
	}{
		{tmpdir + "/main.xcodeproj/project.pbxproj", projPbxproj.Bytes()},
		{tmpdir + "/main/Info.plist", infoplist.Bytes()},
		{tmpdir + "/main/Images.xcassets/AppIcon.appiconset/Contents.json", []byte(contentsJSON)},
	}

	for _, file := range files {
		if err := mkdir(filepath.Dir(file.name)); err != nil {
			return nil, err
		}
		if buildX {
			printcmd("echo \"%s\" > %s", file.contents, file.name)
		}
		if !buildN {
			if err := ioutil.WriteFile(file.name, file.contents, 0644); err != nil {
				return nil, err
			}
		}
	}

	// We are using lipo tool to build multiarchitecture binaries.
	cmd := exec.Command(
		"xcrun", "lipo",
		"-o", filepath.Join(tmpdir, "main/main"),
		"-create",
	)

	var nmpkgs map[string]bool
	builtArch := map[string]bool{}
	for _, t := range targets {
		// Only one binary per arch allowed
		// e.g. ios/arm64 + iossimulator/amd64
		if builtArch[t.arch] {
			continue
		}
		builtArch[t.arch] = true

		path := filepath.Join(tmpdir, t.platform, t.arch)

		// Disable DWARF; see golang.org/issues/25148.
		if err := goBuild(src, appleEnv[t.String()], "-ldflags=-w", "-o="+path); err != nil {
			return nil, err
		}
		if nmpkgs == nil {
			var err error
			nmpkgs, err = extractPkgs(appleNM, path)
			if err != nil {
				return nil, err
			}
		}
		cmd.Args = append(cmd.Args, path)

	}

	if err := runCmd(cmd); err != nil {
		return nil, err
	}

	// TODO(jbd): Set the launcher icon.
	if err := appleCopyAssets(pkg, tmpdir); err != nil {
		return nil, err
	}

	// Build and move the release build to the output directory.
	cmdStrings := []string{
		"xcodebuild",
		"-configuration", "Release",
		"-project", tmpdir + "/main.xcodeproj",
		"-allowProvisioningUpdates",
		"DEVELOPMENT_TEAM=" + teamID,
	}

	cmd = exec.Command("xcrun", cmdStrings...)
	if err := runCmd(cmd); err != nil {
		return nil, err
	}

	// TODO(jbd): Fallback to copying if renaming fails.
	if buildO == "" {
		n := pkg.PkgPath
		if n == "." {
			// use cwd name
			cwd, err := os.Getwd()
			if err != nil {
				return nil, fmt.Errorf("cannot create .app; cannot get the current working dir: %v", err)
			}
			n = cwd
		}
		n = path.Base(n)
		buildO = n + ".app"
	}
	if buildX {
		printcmd("mv %s %s", tmpdir+"/build/Release-iphoneos/main.app", buildO)
	}
	if !buildN {
		// if output already exists, remove.
		if err := os.RemoveAll(buildO); err != nil {
			return nil, err
		}
		if err := os.Rename(tmpdir+"/build/Release-iphoneos/main.app", buildO); err != nil {
			return nil, err
		}
	}
	return nmpkgs, nil
}