setting an IOS static lib architecture to "arm64" using xcode - ios

I'm building a static library for IOS as an xCode target (Cocoa touch static library). I try to "force" the archive product (.a file) or even the intermediate objects (.o files) to be built in "arm64" architecture. I do it using xCode Build Settings:
After cleaning and building this target, I check the architecture using the terminal command lipo -info, but it looks like the architecture is x86_64:
Something I missed in the way? how can I force my desired architecture?

You are building you library for iOS simulator platform. Pick "Generic iOS Device" as target platform and you will get arm64 architecture.
You can set this setting in UI:
Or by passing -sdk iphoneos option to xcodebuild:
xcodebuild -project 44635297.xcodeproj -target 44635297 -configuration Release clean build -sdk iphoneos

Change 'Base SDK' in Build Settings to 'Latest iOS'.

Related

Universal Framework Binaries: Why Build Simulator and Archive Device?

I am using a script provided by Greg Brown to successfully build a universal framework binary. Here are the two lines that invoke xcodebuild:
xcodebuild archive -project $FRAMEWORK_DIR/$FRAMEWORK.xcodeproj -scheme $FRAMEWORK -sdk iphoneos SYMROOT=$BUILD
xcodebuild build -project $FRAMEWORK_DIR/$FRAMEWORK.xcodeproj -target $FRAMEWORK -sdk iphonesimulator SYMROOT=$BUILD
Note that the archive action is being used to produce the device (arm64 architecture) whereas the build action is being used to produce the simulator (x86_64 architecture).
In a different article entitled iOS 9 Universal Cocoa Touch Frameworks the author remarks: "Note that you will both have to build with your Simulator and Archive with your device to get a universal."
So, what's up with build vs archive? Why not build both?
So, what's up with build vs archive? Why not build both?
The way it works is that Apple doesn't allow simulator binaries (x86_64 and i386) to be uploaded to the app store. If your frameworks contain those binaries, then you won't be able to upload the containing app to the app store.
You would distribute your framework with all the possible binaries so that it can run on Simulators and devices alike. But would like to strip off those Simulator binaries before uploading to the app store.
P.S. I've explained this in detail in another answer here. The answer also explains as to why the issue doesn't exist when using frameworks via Cocoapods.
Edit:
The difference between build vs archive is that archive accepts a scheme to run.
An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.
Also,
You cannot use archive option for the iOS Simulator platform.
You could essentially use xcodebuild build for both of the platforms. You will essentially get the same result. The commands that I use to build my framework are as follows (both commands use the build action):
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} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

Aggregate Script for iOS framework with Bitcode

I have a iOS framework and I want to generate a universal target full bitcode support, right now I create an aggregate script and I'm using the xcodebuild command to generate the different versions (device and simulator) but when we try to generate the archive we always receive the error because the library doesn't have full bitcode support.
This is how I'm using the xcodebuild command:
xcodebuild -fembed-bitcode -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator 2>&1
xcodebuild -fembed-bitcode -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos 2>&1
At the end the only way that I found to achieve this is first, generating the device build with Product -> Archive (Make sure to select on "Skip Installs" to NO on "Build Settings", to get the device framework) then create a package manually using the lipo command using the two builds: the Device's framework and the Simulator's framework.
The bitcode is only generated when you use the Archive command, if you use a build command Xcode won't generate the bitcode

Using xcodebuild to build different platform

I have a framework project and I'm trying to build a fat file (iphoneos + iphonesimulator) using a script build phase, but it's not working. The script is simple; it checks the platform being currently built, like to:
if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
then
SF_OTHER_PLATFORM=iphonesimulator
else
SF_OTHER_PLATFORM=iphoneos
fi
And then uses xcodebuild to build it:
xcrun xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -sdk ${SF_OTHER_PLATFORM} -configuration "${CONFIGURATION}" BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}" $ACTION
Other details on the script have been omited for simplicity.
If I chose the initial target as the simulator, this works fine, and both the simulator and device binaries are generated and I use lipo to get the fat file. The problem happens when I do it the other way around, and build the device file, and as such xcodebuild is called for the iphonesimulator SDK. The build fails with the following error:
CodeSign error: entitlements are required for product type 'Framework' in SDK 'Simulator - iOS 8.4'. Your Xcode installation may be damaged.
If I change the -sdk option I get the build, but not the simulator build, which is what I need. This would be (kinda) fine, but in order to build for release (Archive) I need to set the device as the primary target, or otherwise xcode doesn't give me the option.
What should I do?
It looks like you need to put your script in an aggregate target type. It was designed for exactly such cases: two different targets in one build.
What I do is create a new target (Other->Aggregate type) and add a script to it and use that target to create a fat release product.
Here's the script I'm using:
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -sdk iphonesimulator -configuration Release
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -sdk iphoneos -configuration Release
mkdir -p ${TARGET_BUILD_DIR}/../MyApp${CURRENT_PROJECT_VERSION}
cp -r ${TARGET_BUILD_DIR}/../Release-iphoneos/ ${TARGET_BUILD_DIR}/../MyApp${CURRENT_PROJECT_VERSION}
lipo -create "${TARGET_BUILD_DIR}/../Release-iphoneos/MyApp.framework/MyApp" "${TARGET_BUILD_DIR}/../Release-iphonesimulator/MyApp.framework/MyApp" -output "${TARGET_BUILD_DIR}/../MyApp${CURRENT_PROJECT_VERSION}/MyApp.framework/MyApp"
Works like a charm. It creates a folder with the version number (you set it in your new target) and a fat framework inside.
EDIT:
Why this didn't work for you.
Developer is free to distribute iOS framework without codesigning it
as Consumer will re-codesign it anyway, but Developer is forced by
Xcode to codesign his framework when he builds for iOS device.
Creating iOS/OSX Frameworks: is it necessary to codesign them before distributing to other developers?
When you're using the device target you're forced to code sign even if you don't have to. That's why it works with the aggregate target -> it's not expected of you to codesign and you don't need to codesign to release a framework.
Selecting the simulator, the process works fine because you are building using the Debug configuration I think.
In the Project navigator, select your project. Now select your target and under the Build Settings tab, check Code Signing Identity settings.
If you expand that, you should see a row for every configuration you set in your project (if you didn't, you should see the default Debug and Release rows).
Now check under the Release row (that is the default used when the Archive command is called) that is set the correct identity (this is based on what you selected under Provisioning Profile).
If you want to know more about provisioning profiles, signing identity and so on, check this link from Apple

How do I xcodebuild a static library with Bitcode enabled?

Xcode 7 introduces Bitcode, which is some sort of LLVM intermediate binary that means Apple's servers can recompile my app for different architectures without my involvement.
At Lookback, I distribute a static archive framework with our library. It seems that when you build with anything but a "Build & Archive", bitcode is not actually emitted into my library, and anyone who links with my library in their app and tries to do a Build & Archive with Bitcode enabled will get one of two warnings:
ld: 'Lookback(Lookback.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. (if lib is built with Xcode 6)
ld: warning: full bitcode bundle could not be generated because 'Lookback(Lookback.o)' was built only with bitcode marker. The library must be generated from Xcode archive build with bitcode enabled (Xcode setting ENABLE_BITCODE) (if lib is built with Xcode 7 with a normal xcodebuild)
I have a build script that builds a device+simulator universal binary, so I can't use Build & Archive, but rather, I run xcodebuild from commandline from my script. How can I make xcodebuild generate a proper bitcode-enabled library?
Bitcode is a compile-time feature (not a link-time feature) which means that every .o file should contain an extra section called __bitcode when built with bitcode. You can confirm whether your binary is bitcode-compatible by running otool -l (my .o or .a file) | grep __LLVM.
When you build normally, Xcode adds the build flag -fembed-bitcode-marker to any clang invocation. This seems to be some sort of 'this is where bitcode would go, if bitcode was enabled' thing, and doesn't actually enable bitcode.
When you "Build & Archive", this flag is replaced by -fembed-bitcode, which really does build a Bitcode-enabled binary.
There seems to be two ways to make xcodebuild use -fembed-bitcode:
Use the 'archive' action, as in xcodebuild -target LookbackSDK archive instead of xcodebuild -target LookbackSDK build. This has the side-effect of putting binaries in your Xcode Organizer instead of the build/ folder, though you can work around that by using -exportArchive -archivePath ./build (thanks #JensAyton)
Force usage of the flag by adding Other C Flags with OTHER_CFLAGS="-fembed-bitcode". Your xcodebuild invocation would look something like xcodebuild OTHER_CFLAGS="-fembed-bitcode" -target LookbackSDK build.
The latter is what I chose so that I don't have to change my build system, but it will generate warnings for every file, since now both -fembed-bitcode-marker and -fembed-bitcode are sent to clang. Luckilly the latter wins, generating a Bitcode-enabled library!
Resources
Apple DevForums: Bitcode and Assembly?
SO: iOS library to BitCode
With Xcode 8, I couldn't get OTHER_CFLAGS="-fembed-bitcode" to work. I kept running into something along the lines of was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build when I tried to create an Archive build of an app containing my static framework.
What I was really looking for was this:
BITCODE_GENERATION_MODE=bitcode
I'm actually using a Run Script inside of an aggregate target, the full xcodebuild line looks like this (just for reference):
xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
Once you add bitcode support for the static lib, it won't be compatible with Xcode 6. The app won't archive.
I would like to clearly mention the setting for bitcode as #nevyn's answer confused me a little.
Go to Build settings, search for "custom compiler flags". Add -fembed-bitcode. This will build your lib with bitcode.
Select project
On Build Settings -> Other C flags, set Debug to -fembed-bitcode-marker and Release to -fembed-bitcode
On Build Settings, click on the + sign at the top to add a user-defined build setting with the name BITCODE_GENERATION_MODE, and set Debug to marker, Release to bitcode
Edit schema as Release
Then click the desired library. A file and get the build path.
Get the library form Release folder.

Xcode 6 and iOS Static Frameworks

A lot of iOS 3rd Party frameworks are built around custom frameworks, that you are going to integrate in your dependent app.
Before the new iOS Embedded Framework that only works in iOS8 (see my post here), there were two elegant solution for that (when you do not choose to create your scripts for a matter of time and knowledge on that)
The iOS Universal Framework by kstenerud
and
The iOS Framework by jverkoey
They both worked and were good solution when have to create a .framework to distribute a solution for your clients.
As soon as Xcode6 came out, I was not able to make the first to work due to compilation errors of different kind (see here for details).
So I moved to the iOS Framework, and with my surprise it still works on XCode6.
So this advice is for the ones that are struggling with iOS Universal Framework and do not find any solution to make it works on Xcode6.
Since as depicted in 1 there is now way for Apple to accept Embedded Frameworks if your deployment target is >= iOS7, this is the best custom solution at the moment, and a fix for the iOS Universal Framework could help as well.
All the issues I have found on it are depicted in 5.
Here step to create static cocoa touch framework in Xcode 6.
Open Xcode and create a new static framework project by clicking File\New\Project and selecting iOS\Framework and Library\Cocoa Touch framework.
You can provide your framework name and save the project to an empty directory.
Automatically umbrella header created for our framework. In this header , you should import all the public headers of our framework using the statements like #import
A static framework project is made up of header files and implementation files, which are compiled to make the framework itself. You can create the class by using the Cocoa Touch class.
Verifying Your Build Settings Go to the Build Settings of your project Target and confirm or set the “Architectures” to “Standard Architectures.” These are arm64 and armv7, and are likely the default. As well as, Also we need to set the few architectures in setting, because iOS apps need to run on many different architectures.
armv7: Used in the oldest iOS 7-supporting devices armv7s: As used in iPhone 5 and 5C arm64: For the 64-bit ARM processor in iPhone 5S i386: For the 32-bit simulator x86_64: Used in 64-bit simulator
This means that builds are as fast as they can be. When you archive an app or build in release mode, then Xcode will build for all ARM architectures, thus allowing the app to run on most devices.
Mach-O setting: Static library:
Final Build project:
Aggregate target to combine the device and simulator framework by using lipo, you can add the below script to your aggregate target.
FRAMEWORK_NAME="${PROJECT_NAME}"
SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${FRAMEWORK_NAME}.framework"
DEVICE_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORK_NAME}.framework"
UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal"
FRAMEWORK="${UNIVERSAL_LIBRARY_DIR}/${FRAMEWORK_NAME}.framework"
xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -arch i386 -arch x86_64 -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator | echo
xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -arch arm64 -arch armv7 -arch armv7s -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos | echo
rm -rf "${UNIVERSAL_LIBRARY_DIR}"
mkdir "${UNIVERSAL_LIBRARY_DIR}"
mkdir "${FRAMEWORK}"
cp -r "${DEVICE_LIBRARY_PATH}/." "${FRAMEWORK}"
lipo "${SIMULATOR_LIBRARY_PATH}/${FRAMEWORK_NAME}""${DEVICE_LIBRARY_PATH}/${FRAMEWORK_NAME}" -create -output"${FRAMEWORK}/${FRAMEWORK_NAME}" | echo
You can use Xcode 6 Universal Framework in iOS 7, - see my answer here

Resources