Skip to content

Commit b04d93b

Browse files
scosmanhajimehoshi
authored andcommitted
cmd/gomobile: produce frameworks which follow Apple's specs per platform, fixing Xcode 15.3 compatibility issues
This patch updates the framework generation code to follow the Apple spec for placing content: https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle Previously, we setup the framework in MacOS format, and used symlinks to make it compatible with iOS format. This approach no longer works (it works locally in simulator, but causes signing issues when attempting to upload apps for distribution, or running on real hardware). We now setup the expected bundle format for each platform. Other benefits: - Thirds the size of the xcframework and resulting app binary when distributing the xcframework by zip (common for SPM and other formats). The symlinks resulted in duplicate files after zipping, which made it into the final app. - Set MinimumOSVersion, fixing SPM compatibility issue - Eliminates the blank Info.plist - Initial testing shows this also fixes golang/go#66406 (code signing issues) as a side effect of using the proper format Testing: - Tested all 4 platforms (iOS, simulator, macOS, Catalyst) on Xcode 15.3 Fixes golang/go#66406 Fixes golang/go#66500 Change-Id: I8538989efe67cb0d2d0496087dcbeca923c3dffe GitHub-Last-Rev: 28dca85 GitHub-Pull-Request: golang#98 Reviewed-on: https://go-review.googlesource.com/c/mobile/+/574055 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Hajime Hoshi <hajimehoshi@gmail.com> Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
1 parent 0b2c675 commit b04d93b

File tree

1 file changed

+67
-42
lines changed

1 file changed

+67
-42
lines changed

cmd/gomobile/bind_iosapp.go

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,12 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
151151
frameworkDirs = append(frameworkDirs, frameworkDir)
152152
frameworkArchCount[frameworkDir] = frameworkArchCount[frameworkDir] + 1
153153

154-
versionsDir := filepath.Join(frameworkDir, "Versions")
155-
versionsADir := filepath.Join(versionsDir, "A")
156-
titlePath := filepath.Join(versionsADir, title)
154+
frameworkLayout, err := frameworkLayoutForTarget(t, title)
155+
if err != nil {
156+
return err
157+
}
158+
159+
titlePath := filepath.Join(frameworkDir, frameworkLayout.binaryPath, title)
157160
if frameworkArchCount[frameworkDir] > 1 {
158161
// Not the first static lib, attach to a fat library and skip create headers
159162
fatCmd := exec.Command(
@@ -166,17 +169,8 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
166169
continue
167170
}
168171

169-
versionsAHeadersDir := filepath.Join(versionsADir, "Headers")
170-
if err := mkdir(versionsAHeadersDir); err != nil {
171-
return err
172-
}
173-
if err := symlink("A", filepath.Join(versionsDir, "Current")); err != nil {
174-
return err
175-
}
176-
if err := symlink("Versions/Current/Headers", filepath.Join(frameworkDir, "Headers")); err != nil {
177-
return err
178-
}
179-
if err := symlink(filepath.Join("Versions/Current", title), filepath.Join(frameworkDir, title)); err != nil {
172+
headersDir := filepath.Join(frameworkDir, frameworkLayout.headerPath)
173+
if err := mkdir(headersDir); err != nil {
180174
return err
181175
}
182176

@@ -199,7 +193,7 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
199193
if len(fileBases) == 1 {
200194
headerFiles = append(headerFiles, title+".h")
201195
err := copyFile(
202-
filepath.Join(versionsAHeadersDir, title+".h"),
196+
filepath.Join(headersDir, title+".h"),
203197
filepath.Join(gobindDir, bindPrefix+title+".objc.h"),
204198
)
205199
if err != nil {
@@ -209,46 +203,36 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
209203
for _, fileBase := range fileBases {
210204
headerFiles = append(headerFiles, fileBase+".objc.h")
211205
err := copyFile(
212-
filepath.Join(versionsAHeadersDir, fileBase+".objc.h"),
206+
filepath.Join(headersDir, fileBase+".objc.h"),
213207
filepath.Join(gobindDir, fileBase+".objc.h"),
214208
)
215209
if err != nil {
216210
return err
217211
}
218212
}
219213
err := copyFile(
220-
filepath.Join(versionsAHeadersDir, "ref.h"),
214+
filepath.Join(headersDir, "ref.h"),
221215
filepath.Join(gobindDir, "ref.h"),
222216
)
223217
if err != nil {
224218
return err
225219
}
226220
headerFiles = append(headerFiles, title+".h")
227-
err = writeFile(filepath.Join(versionsAHeadersDir, title+".h"), func(w io.Writer) error {
221+
err = writeFile(filepath.Join(headersDir, title+".h"), func(w io.Writer) error {
228222
return appleBindHeaderTmpl.Execute(w, map[string]interface{}{
229223
"pkgs": pkgs, "title": title, "bases": fileBases,
230224
})
231225
})
232226
if err != nil {
233227
return err
234228
}
235-
236-
err = writeFile(filepath.Join(frameworkDir, "Info.plist"), func(w io.Writer) error {
237-
_, err := w.Write([]byte(appleBlankInfoPlist))
238-
return err
239-
})
240-
if err != nil {
241-
return err
242-
}
243229
}
244230

245-
if err := mkdir(filepath.Join(versionsADir, "Resources")); err != nil {
246-
return err
247-
}
248-
if err := symlink("Versions/Current/Resources", filepath.Join(frameworkDir, "Resources")); err != nil {
231+
frameworkInfoPlistDir := filepath.Join(frameworkDir, frameworkLayout.infoPlistPath)
232+
if err := mkdir(frameworkInfoPlistDir); err != nil {
249233
return err
250234
}
251-
err = writeFile(filepath.Join(frameworkDir, "Resources", "Info.plist"), func(w io.Writer) error {
235+
err = writeFile(filepath.Join(frameworkInfoPlistDir, "Info.plist"), func(w io.Writer) error {
252236
infoFrameworkPlistlData := infoFrameworkPlistlData{
253237
BundleID: escapePlistValue(rfc1034Label(title)),
254238
ExecutableName: escapePlistValue(title),
@@ -271,15 +255,18 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
271255
Module: title,
272256
Headers: headerFiles,
273257
}
274-
err = writeFile(filepath.Join(versionsADir, "Modules", "module.modulemap"), func(w io.Writer) error {
258+
modulesDir := filepath.Join(frameworkDir, frameworkLayout.modulePath)
259+
err = writeFile(filepath.Join(modulesDir, "module.modulemap"), func(w io.Writer) error {
275260
return appleModuleMapTmpl.Execute(w, mmVals)
276261
})
277262
if err != nil {
278263
return err
279264
}
280-
err = symlink(filepath.Join("Versions/Current/Modules"), filepath.Join(frameworkDir, "Modules"))
281-
if err != nil {
282-
return err
265+
266+
for src, dst := range frameworkLayout.symlinks {
267+
if err := symlink(src, filepath.Join(frameworkDir, dst)); err != nil {
268+
return err
269+
}
283270
}
284271
}
285272

@@ -304,19 +291,53 @@ func goAppleBind(gobind string, pkgs []*packages.Package, targets []targetInfo)
304291
return err
305292
}
306293

307-
const appleBlankInfoPlist = `<?xml version="1.0" encoding="UTF-8"?>
308-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
309-
<plist version="1.0">
310-
<dict>
311-
</dict>
312-
</plist>
313-
`
294+
type frameworkLayout struct {
295+
headerPath string
296+
binaryPath string
297+
modulePath string
298+
infoPlistPath string
299+
// symlinks to create in the framework. Maps src (relative to dst) -> dst (relative to framework bundle root)
300+
symlinks map[string]string
301+
}
302+
303+
// frameworkLayoutForTarget generates the filestructure for a framework for the given target platform (macos, ios, etc),
304+
// according to Apple's spec https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle
305+
func frameworkLayoutForTarget(t targetInfo, title string) (*frameworkLayout, error) {
306+
switch t.platform {
307+
case "macos", "maccatalyst":
308+
return &frameworkLayout{
309+
headerPath: "Versions/A/Headers",
310+
binaryPath: "Versions/A",
311+
modulePath: "Versions/A/Modules",
312+
infoPlistPath: "Versions/A/Resources",
313+
symlinks: map[string]string{
314+
"A": "Versions/Current",
315+
"Versions/Current/Resources": "Resources",
316+
"Versions/Current/Headers": "Headers",
317+
"Versions/Current/Modules": "Modules",
318+
filepath.Join("Versions/Current", title): title,
319+
},
320+
}, nil
321+
case "ios", "iossimulator":
322+
return &frameworkLayout{
323+
headerPath: "Headers",
324+
binaryPath: ".",
325+
modulePath: "Modules",
326+
infoPlistPath: ".",
327+
}, nil
328+
}
329+
330+
return nil, fmt.Errorf("unsupported platform %q", t.platform)
331+
}
314332

315333
type infoFrameworkPlistlData struct {
316334
BundleID string
317335
ExecutableName string
318336
}
319337

338+
// infoFrameworkPlistTmpl is a template for the Info.plist file in a framework.
339+
// Minimum OS version == 100.0 is a workaround for SPM issue
340+
// https://github.com/firebase/firebase-ios-sdk/pull/12439/files#diff-f4eb4ff5ec89af999cbe8fa3ffe5647d7853ffbc9c1515b337ca043c684b6bb4R679
320341
var infoFrameworkPlistTmpl = template.Must(template.New("infoFrameworkPlist").Parse(`<?xml version="1.0" encoding="UTF-8"?>
321342
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
322343
<plist version="1.0">
@@ -325,6 +346,10 @@ var infoFrameworkPlistTmpl = template.Must(template.New("infoFrameworkPlist").Pa
325346
<string>{{.ExecutableName}}</string>
326347
<key>CFBundleIdentifier</key>
327348
<string>{{.BundleID}}</string>
349+
<key>MinimumOSVersion</key>
350+
<string>100.0</string>
351+
<key>CFBundlePackageType</key>
352+
<string>FMWK</string>
328353
</dict>
329354
</plist>
330355
`))

0 commit comments

Comments
 (0)