Build static lib for Apple Silicon mac simulator - ios

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?

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:
You build separate libraries and keep the names separate.
You never build both architectures at the same time.
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.

Related

What architecture should it be my .a for running on iOS Device and iOS Simulator

I have build a fat library with the architectures "arm64", "armv7" and "x86" from various lib.a. I have no problem when I compile the project for iOS Devices but it seems the compilation goes wrong when I try it into a iOS simulator. It says "Undefined Symbols for x86_64". I can Imagine that I need also the x86_64 lib.a But I wonder what can I do for solving the problem.
Note:
The libraries where manually compiled from OpenHome
Than you
Edited:
Could be because of the makefile from Here
As a first step, make sure if your fat bin definitely includes x86_64.
To do so, you can check w/ cmd as follows.
$ xcrun lipo -info yourLibrary.a
If your lib definitely contains x86_64, it should show
Architectures in the fat file: yourLibrary.a are: x86_64 arm64 ...
For example, I have compiled libpng for x86_64 and arm64 for my app. The xcrun command shows as follow.
$ xcrun lipo -info libpng.a
Architectures in the fat file: libpng.a are: x86_64 arm64
Needless to say, fat bin has both arm64 and x86 (since it's FAT). So fat bin doesn't require separate x86_64 lib.a as you mentioned in you q.
Checking your Makefile is after you confirmed your fat bin has x86_64.

Build framework for multiple architectures (arm64, armv7, armv7s)

I'm trying to upload to TestFlight a project build for arm64, armv7, and armv7s. It is using a framework from another project. But the frameowork appears to be built only for arm64 and not arm64 (file was built for arm64 which is not the architecture being linked (armv7)).
The question is how do I make the framework containing all architectures? I want to keep the projects separated. And I don't care for simulators. I want to make sure it is built for release.
This is the framework target:
EDIT: My project is Cordova based. So it is using a plugin which utilize a pre-built framework. There are instructions out there for building a fat framework, containing simulators and device, then concatenating it with lipo. What I need is the architecture from the device I don't have as well. Does that actually mean I need three devices from arm64, armv7, and armv7s to be able to concatenating them altogether?
Apple has discontinued support for 32-bit in iOS 11. You can neither run 32 bit apps on iOS 11 nor run iOS 11 on 32 bit processors. Thus, you have to set your Deployment Target to an iOS version earlier than iOS 11 for your framework.
You can try to create an aggregate target and write an script which will support all the platform. This is sample script from one of my project.
unset TOOLCHAINS #Xcode 7.3 BUG FIX http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode
# define output folder environment variable
C_PROJECT_NAME="<<YOUR_FRAMEWORK_NAME>>"
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# Step 1. Build Device and Simulator versions
xcodebuild -target <<YOUR_FRAMEWORK_NAME>> ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
xcodebuild -target <<YOUR_FRAMEWORK_NAME>> ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 -arch x86_64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
rm -rf ./${C_PROJECT_NAME}.framework
cp -R ${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/${C_PROJECT_NAME}.framework ./
# Step 2. Create universal binary file using lipo
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}"
mv ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME} ./${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}

Method to determine whether a binary contains Bitcode no longer seems to work

In my search for a method to determine if a iOS binary was build with Bitcode, I found the following post:
How to check if a framework is BITCODE supported for Xcode7
Here, the following method was suggested to determine if bitcode is present in a binary:
$ otool -l libName.o | grep __LLVM
However, I have tried this on several binaries with no success. One of them is a library I know has bitcode since after I changed the flag on its project a build error went away. Another one of them is a binary for a file extension, build using Archive. And another is for apple watch.
I believe all of the above binaries should have Bitcode, and yet I always get no results from the above command.
Does anyone know any other method that works with the latest binaries?
I'm using XCode 7.2 and 10.10.5 in case it matters.
UPDATE: Here is an example of a file which is supposed to have bitcode but the above command doesn't return anything. It is a binary from a test File Provider. I generated it via Archive and Deploy as Ad Hoc, and made sure the setting for bitcode was on for the target.
https://www.dropbox.com/s/eyuzs5j1t7nsq6t/CustomDocumentProviderFileProvider?dl=0
If you have a fat binary, then you need to run otool -l on a specific slice. For instance, in the following example I chose arm64:
otool -arch arm64 -l MyFramework.framework/MyFramework | grep -a4 __LLVM
In the output you should check:
if there is at least one section named __LLVM
if the size is greater than zero
This seems to be a problem with otool as reported here. Use file to get a list of architectures and then supply the architecture to otool. Given a fat binary with Bitcode for armv7, arm64, i386 and x86_64:
$ file lib.a
lib.a: Mach-O universal binary with 4 architectures
lib.a (for architecture armv7): current ar archive random library
lib.a (for architecture i386): current ar archive random library
lib.a (for architecture x86_64): current ar archive random library
lib.a (for architecture arm64): current ar archive random library
$ otool -arch armv7 -l lib.a | grep bitcode
sectname __bitcode
According to this question, otool does not report Bitcode for x86_64 and i368.
CustomDocumentProviderFileProvider does not seem to contain Bitcode:
$ file CustomDocumentProviderFileProvider
CustomDocumentProviderFileProvider: Mach-O universal binary with 2 architectures
CustomDocumentProviderFileProvider (for architecture armv7): Mach-O executable arm
CustomDocumentProviderFileProvider (for architecture arm64): Mach-O 64-bit executable
$ otool -arch armv7 -l CustomDocumentProviderFileProvider | grep bit
$
Disclaimer: I'm the author of LibEBC.
You can use ebcutil to determine whether bitcode is present in any binary (Mach-O, ELF) or library (.a/.dylib/.so).
https://github.com/JDevlieghere/LibEBC
As of today, the technique which works for me is the one mentioned in this answer from another SO thread. Specifically, for a (dynamic) framework named MyLib and containing those two device architectures:
otool -arch armv7 MyLib.framework/MyLib | grep LLVM
otool -arch arm64 MyLib.framework/MyLib | grep LLVM

How to build Cocoa Touch Framework for i386 and x86_64 architecture?

After building a Cocoa Touch framework (Swift or Object-C) and adding it to another project as "Embedded Binaries" I get the following error message when I try to build
missing required architecture i386
...
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Following various existing answers and extended research I already added i386 to the Architectures build settings …
However this doesn't seem to have an effect. When I check using
lipo -info TesterFrameworkObjC
I only get
Architectures in the fat file: TesterFrameworkObjC are: armv7 arm64
… shouldn't i386 (and x86_64) appear here as well? What am I missing?
(I am using Xcode 6.2 + building for iOS 8.2)
Some new insights
Using a version of this build script I am able to build the missing architectures for the Swift version of the framework.
However when I add this framework to my app and build I still get errors
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$__TtC20TesterFrameworkSwift18TestFrameworkSwift", referenced from:
objc-class-ref in ViewController.o
ld: symbol(s) not found for architecture x86_64
Looking at the final build folder building for the simulator
Build/Products/Debug-iphoneos/TesterFrameworkSwift.framework/
I can see that the architectures are still missing although they were part of the framework (as shown). How can I ensure all the right architecture builds of my framework are included when building the app?
Update
Looking at all the error messages to me it looks like the issue isn't actually to have the wrong products/archictures being built but one step later there the Linker isn't using the correct paths to them. Not sure how correct this.
This is the same problem that I had.
Seems this is a Xcode bug, I workaround it by adding a build script.
It will just compile your library with all architectures, and lipo it to fat binary image.
Also note
Looking at the final build folder building for the simulator
Build/Products/Debug-iphoneos/TesterFrameworkSwift.framework/
you are looking x86_64 and i386 images in iphone build folder, they should be in Debug-iphonesimulator/TesterFrameworkSwift.framework/
Also there is a recursion issue in your script, I've corrected it.
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
# Step 2. Copy the framework structure (from iphoneos build) to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 3. Copy Swift modules (from iphonesimulator build) to the copied framework directory
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
fi
6) Hit "cmd + B" (Build Project)
7) Open Product in Finder
8) Navigate 1 directory up ("cmd + ↑"), and you will see "Release-universal" directory.
There will be your "fat/universal" library, You are ready to go!
You can check it via
file test.dylib
test.dylib: Mach-O universal binary with 4 architectures
test.dylib (for architecture i386): Mach-O dynamically linked shared library i386
test.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64
test.dylib (for architecture armv7): Mach-O dynamically linked shared library arm
test.dylib (for architecture arm64): Mach-O 64-bit dynamically linked shared library
I encounter the same problem and fortunately I solved it.
When you create a framework with Xcode and build it with the iPhone 5 simulator or the simulator before iPhone 5(such as iPhone 4s or iPhone 4),you will get a framework with i386 architecture.And if you build it with iPhone 5s simulator or the simulatro after iPhone 5s,you will get a framework with x86_64 architecture.
But if you use the framework in your project and you want to run the app on a simulator,the framework must contain both i386 and x86_64 architecture,otherwise you will get a linker error.
So you need to use lipo -create command to merge two framework,at this point your framework is ready for use.
Go to Build Settings -> Set Build Active Architecture Only to NO
Build your Framework in iPhone 5 and iPhone 6 or above Simulators.
Add the run script in post actions to generate the universal framework.
Go to terminal and type cd (path of your framework, or drag and drop your framework) which will take you to the frameworks directory
type file YOUR_FRAMEWORK_NAME which lists all the architectures your framework is supporting
you can see that your framework supports all four architectures i386, x86_64, armv7 arm64.
Active Architecture => NO
Valid Architecture => "armv7 armv7s arm64 i386 x86_64"
Use Above Build Setting when in are aggregating !!!
I worked for me for all Architecture Framework creation

Xcodebuild cannot build framework library for simulator sdk

I'm having some issues when I try to build a framework using xcodebuild. My xcode version is 6.1.
This line works well:
xcodebuild -target Knot3DLib -configuration Release -sdk iphoneos
However it doesn't work:
xcodebuild -target Knot3DLib -configuration Release -sdk iphonesimulator
It says:
No architectures to compile for (ARCHS=i386 x86_64, VALID_ARCHS=arm64
armv7 armv7s).
I've no defined neither i386 nor x86_64 as archs in my project, so why does xcodebuild try to build i386? Here's my architecture build settings:
I can run the project from Xcode in both simulator and devices.
I've tried other similar questions but none of them worked for me.
The simulator is just that, a simulator. Xcode builds code that runs on a simulated environment on your computer. Your computer has an intel CPU and your phone has an ARM CPU. The compiler generates different code for these processors.
The error:
No architectures to compile for (ARCHS=i386 x86_64, VALID_ARCHS=arm64 armv7 armv7s).
Tells you that the architecture you are trying to compile for: ARCHS=i386 x86_64 is not in the list of VALID_ARCHS.
i386 and x86_64 are the architectures that intel CPUs use. If you are trying to build a framework with xcodebuild and you want to be able to link against the simulator sdk you need to add i386 and x86_64 to your list of VALID_ARCHS

Resources