Build static lib for Apple Silicon mac simulator

1.9k views Asked by At

My static lib is built with xcodebuild and then a fat lib is created from the simulator and device build result. Here is my xcodebuild command:

xcodebuild OTHER_CFLAGS="-fembed-bitcode" -configuration "iphoneos" -target "${LIB_NAME}Common" -sdk iphoneos

xcodebuild OTHER_CFLAGS="-fembed-bitcode" -configuration "iphonesimulator" -target "${LIB_NAME}Common" -sdk iphonesimulator

lipo command:

lipo -create "${DEVICE_DIR}/lib${LIB_NAME}Common.a" "${SIMULATOR_DIR}/lib${LIB_NAME}Common.a" -output "${INSTALL_DIR}/include/${LIB_NAME}/lib${LIB_NAME}Common.a"

After checking the architectures in the fat lib, I got:

$ lipo -info MyLibCommon.a 
Architectures in the fat file: MyLibCommon.a are: armv7 i386 x86_64 arm64

However, when I add the lib to a project via cocoapods and run the project on Apple's new Silicon (with arm64 chipset) on simulator, I got the following compile error:

building for iOS Simulator, but linking in object file built for iOS, file 'MyLibCommon.a' for architecture arm64

Excluding the arm64 architecture for Simulator is not an option because on Apple Silicon Mac has arm64 chipset.

Any idea how can I build a static library for Apple Silicon Simulator?

1

There are 1 answers

2
Siguza On BEST ANSWER

This is not possible.

In all likelihood, your simulator binaries are just i386 and x86_64. If you truly had arm64 iOS binaries and arm64 macOS binaries, lipo would error out on you:

fatal error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: test.a.ios and test.a.macos have the same architectures (arm64) and can't be in the same fat output file

This happens no matter whether you try it with full-fledged binaries, unlinked object files or static libraries. The reason is simply a shortcoming in the fat file format: you can only have one slice per architecture. You want both arm64 iOS and the Apple Silicon simulator, but that would be 2x arm64.

You might be tempted to try and build a single thin arm64 binary that works on both iOS and macOS, but that too is impossible. Binaries are platform-locked:

% otool -l test.o.ios | fgrep -B1 -A5 LC_BUILD_VERSION
Load command 1
       cmd LC_BUILD_VERSION
   cmdsize 24
  platform 2
       sdk 14.2
     minos 14.2
    ntools 0
% otool -l test.o.macos | fgrep -B1 -A5 LC_BUILD_VERSION
Load command 1
       cmd LC_BUILD_VERSION
   cmdsize 24
  platform 1
       sdk 11.0
     minos 11.0
    ntools 0

Notice the platform 2 vs platform 1. The kernel will actually ignore this load command, but dyld will not. And you can't have two such load commands in a single binary either, that's considered invalid as well.

You might also remember talk of a "Universal 2" file format from Apple's announcement or something citing that - but they lied. There is no "universal 2", it's exactly the same file format it was a decade ago. When they say "universal 2", all they mean is "adding an arm64 slice to your macOS binary".

The way I see it, you have three options:

  1. You build separate libraries and keep the names separate.
  2. You never build both architectures at the same time.
  3. You build the simulator target for x86_64 and run it under Rosetta.

The latter two are both being suggested widely across the internet, with number 2 as "build only for active architecture" and number 3 being "exclude arm64". Given that Rosetta is expected to go away eventually, the third option seems not viable for the long term.