Universal binaries with more than one arch for macOS fail on creating xcframework
Deadpikle opened this issue · comments
As reported in #49:
If you try to build a .xcframework file with i386 and x86_64, it will fail with a similar error to the following:
Both macos-i386 and macos-x86_64 represent two equivalent library definitions
.
I did a lot of digging, and it turns out the one other case I can find of this is in Firebase by Google. See comments at https://github.com/firebase/firebase-ios-sdk/blob/master/ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift#L757
The gist: some binaries need to be lipo'd together before sticking them in the xcframework, especially noticed with macOS. We need to lipo all the macOS (including silicon!) libboost.a
files together before putting them in the xcframework file.
This actually makes a lot of sense -- this way, you can distribute one raw binary to all your macOS users, and this has "backwards compatibility" to old versions of macOS because of the lipo -- the end executable you distribute has the lipo'd version, not 2 different libs (one for x64 and one for arm64, for example).
Yes, this is indeed the case. In some testing done manually on the command line with arm64 and x86_64, which has a similar error:
lipo -create -output ulibboost.a build/boost/1.71.0/macos/release/build/x86_64/libboost.a build/boost/1.71.0/macos-silicon/release/build/arm64/libboost.a
xcrun xcodebuild -create-xcframework -library ulibboost.a -headers build/boost/1.71.0/macos/release/prefix/include/ -output test.xcframework
generates the following Info.plist:
<?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>AvailableLibraries</key>
<array>
<dict>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>macos-arm64_x86_64</string>
<key>LibraryPath</key>
<string>ulibboost.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>macos</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>
and the following structure:
I realize the headers and file name aren't set up right (this was only a proof of concept test), but this should show the direction to go for a fix for i386, x86_64, and Apple Silicon support all in one xcframework.
I finally got my M1 and used the new script to rebuild my framework. Once I got the command line correct, it worked great! Thank you so much.
I did run into a couple of minor issues. Happy to submit a PR; just let me know if you agree w/ these.
First, I didn't understand why the script wasn't following my instruction to build for multiple architectures. Finally realized that when printing the ARCHs it's going to build for, it's only printing the first element of the array; I fixed by adding [@] to all arches in printVar Calls.
If XCFramework build fails, script still reports success at the end. (Should check status after find on line 1484)
Getting an error now: "ld: warning: option -s is obsolete and being ignored"
There's still the spin lock issue (I'll post on Issue #50 as well).
The pThreads used in EXTRA_ARM_FLAGS is incompatible with iOS; one symptom is that the filesystem directory_iterator fails. It should be:
EXTRA_ARM_FLAGS="-DBOOST_SP_USE_SPINLOCK -g -DNDEBUG"
Default Boost Version should be updated to 1.75.0, no?
Formatting problems:
It'd be nice if BUILD_XXXOS is NO, then don't list SDK/Version info for that system.
Missing a blank line echo after printVar MIN_MACOS_SILICON_VERSION
Need to bump 20 spaces to 25 in PrintVar due to longer