cmd/gomobile: fix a compatibility issue with Xcode 15.3

This change adds compatibility for Xcode 15.3 to "gomobile bind" for building xcframeworks.

 - New blank Info.plist in the *.framework target root
 - Add CFBundleExecutable and CFBundleIdentifier to the resource level Info.plist

Tested locally on my framework on Xcode 15.3 (fixes issue) and 15.2 (doesn't create new issues).

Would love to get some more folks to try this fix, to make sure it works broadly.

Note: I'm using the framework name as the bundleID. Some chance of collision here, but didn't want to add a required top level cmd parameter. I don't *think* a collision is a serious concern, but I'm not an apple build system expert.

To test:
 - sync my branch
 - build go mobile: `go build` in the `cmd/gomobile` dir
 - Build your xcframework with this version of go mobile: `gomobile bind ... `
 - Launch a project using the xcframework in Xcode 15.3, and run in simulator

Fixes golang/go#66018

Change-Id: I3e8ee99adb09071aa89a541dc97271a44b552ea3
GitHub-Last-Rev: 6277c7d66a26efa8b9ff99dea191eeb7bef136a6
GitHub-Pull-Request: golang/mobile#96
Reviewed-on: https://go-review.googlesource.com/c/mobile/+/572175
TryBot-Result: Gopher Robot <gobot@golang.org>
TryBot-Bypass: Hyang-Ah Hana Kim <hyangah@gmail.com>
Reviewed-by: Hajime Hoshi <hajimehoshi@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Hajime Hoshi <hajimehoshi@gmail.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/cmd/gomobile/bind_iosapp.go b/cmd/gomobile/bind_iosapp.go
index 3843743..e92aa36 100644
--- a/cmd/gomobile/bind_iosapp.go
+++ b/cmd/gomobile/bind_iosapp.go
@@ -5,6 +5,8 @@
 package main
 
 import (
+	"bytes"
+	"encoding/xml"
 	"errors"
 	"fmt"
 	"io"
@@ -230,6 +232,14 @@
 			if err != nil {
 				return err
 			}
+
+			err = writeFile(filepath.Join(frameworkDir, "Info.plist"), func(w io.Writer) error {
+				_, err := w.Write([]byte(appleBlankInfoPlist))
+				return err
+			})
+			if err != nil {
+				return err
+			}
 		}
 
 		if err := mkdir(filepath.Join(versionsADir, "Resources")); err != nil {
@@ -239,7 +249,15 @@
 			return err
 		}
 		err = writeFile(filepath.Join(frameworkDir, "Resources", "Info.plist"), func(w io.Writer) error {
-			_, err := w.Write([]byte(appleBindInfoPlist))
+			infoFrameworkPlistlData := infoFrameworkPlistlData{
+				BundleID:       escapePlistValue(rfc1034Label(title)),
+				ExecutableName: escapePlistValue(title),
+			}
+			infoplist := new(bytes.Buffer)
+			if err := infoFrameworkPlistTmpl.Execute(infoplist, infoFrameworkPlistlData); err != nil {
+				return err
+			}
+			_, err := w.Write(infoplist.Bytes())
 			return err
 		})
 		if err != nil {
@@ -286,7 +304,7 @@
 	return err
 }
 
-const appleBindInfoPlist = `<?xml version="1.0" encoding="UTF-8"?>
+const appleBlankInfoPlist = `<?xml version="1.0" encoding="UTF-8"?>
     <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
     <plist version="1.0">
       <dict>
@@ -294,6 +312,29 @@
     </plist>
 `
 
+type infoFrameworkPlistlData struct {
+	BundleID       string
+	ExecutableName string
+}
+
+var infoFrameworkPlistTmpl = template.Must(template.New("infoFrameworkPlist").Parse(`<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleExecutable</key>
+  <string>{{.ExecutableName}}</string>
+  <key>CFBundleIdentifier</key>
+  <string>{{.BundleID}}</string>
+</dict>
+</plist>
+`))
+
+func escapePlistValue(value string) string {
+	var b bytes.Buffer
+	xml.EscapeText(&b, []byte(value))
+	return b.String()
+}
+
 var appleModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework module "{{.Module}}" {
 	header "ref.h"
 {{range .Headers}}    header "{{.}}"