Merging iOS .dylib into framework with lipo breaks bitcode recompilation - ios

I'm trying to build a dynamic iOS framework manually from .dylib files. Binaries are created with cmake and xcodebuild and produce two .dylib files, one containing armv7, armv7s and arm64 and the other x86_64 and i386 architectures. Libraries are compiled with -fembed-bitcode parameter and everything succeeds.
.dylib files are then merged with next command:
lipo -create lib_arm.dylib lib_i386.dylib -output MyFramework
Framework is then created by copying the output from lipo command to:
MyFramework.framework/MyFramework
Headers and Info.plist are manually generated and added to the framework.
This framework is then installed via CocoaPods into the application as a vendored_framework. It is known that CocoaPods will strip the i386/x86_64 libraries from any fat binaries, for App Store distribution.
Application builds, runs, archives and uploads successfully to App Store.
However, since Bitcode is enabled, App Store will process the .ipa, and recompile with bitcode, this is where it fails and I receive an email from App Store, that it failed processing. Following instructions, I can reproduce the error locally with Exporting for Ad-Hoc Distribution and recompiling for bitcode. The error I receive is this:
ipatool failed with an exception: #<Errno::ENOENT: No such file or directory - /lib_arm.dylib>\n
So apparently during the recompilation, there is still a reference or somewhere to lib_arm.dylib, even though it was merged into a fat dylib Mach-O universal binary (file output of the merged dylib binary below):
>> file MyFramework
MyFramework: Mach-O universal binary with 5 architectures: [x86_64: Mach-O 64-bit dynamically linked shared library x86_64] [i386] [arm_v7] [arm_v7s] [arm64]
MyFramework (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64
MyFramework (for architecture i386): Mach-O dynamically linked shared library i386
MyFramework (for architecture armv7): Mach-O dynamically linked shared library arm_v7
MyFramework (for architecture armv7s): Mach-O dynamically linked shared library arm_v7s
MyFramework (for architecture arm64): Mach-O 64-bit dynamically linked shared library arm64
This is pretty much where my compiler/linker knowledge gets out of scope.
So my question is:
Where am I going wrong with this? Maybe bitcode should be compiled differently? Or maybe I am using lipo in wrong way?
Thank you!

After inspecting load commands of the created fat binary with otool -l command, I realized that using lipo itself does not change the LC_ID_DYLIB in the binary and it will reuse one from the first provided library. Using install_name_tool to change the id to correct one of the framework (including #rpath for iOS dynamic frameworks) fixes the error.
install_name_tool -id #rpath/MyFramework.framework/MyFramework MyFramework
Make sure the entire path to binary is included, together with .framework directory.

Related

Will the compiler strip the unrelated arch symbols from dependent static library in product binary file?

I'm a new in framework development, here is my case. I build a private static library to provide it to the vendors to link it.
Currently, I build my library with arch armv7 and arm64 only, this should be work for vendors to debug it in iOS device and archive their apps, but not for debugging in iOS Simulator. The simulator needs the x86_64 ( and even i386 in iPhone 5 Simulator). It isn't friendly to disable the ability to debug it in a simulator. I'm considering to provide a fat architecture of static library for them.
Here is the action
lipo -create libSignatureLibary_armv6.a libSignatureLibary_armv7.a libSignatureLibary_i368.a -output libSignatureLibary.a
After the merge operation, the output library has a double size than the single one.
The question is, will the compiler/Xcode strip the i386 and x86_64 arch symbols from final app product binary? If not, the fat arch library will increase the product app's size directly, right? Should I build two versions of the library for vendors, one for debugging, another for archiving? What's the right solution for this case?
I don't know what keywords I should research, I didn't have an existing product app linking it to verify this, either. (Maybe I should build a new later.)
Don't worry, the linker only uses the .o (relocatable object file, it is the output file of assembler, when you build a static library, a .m file will be translated to a .o file. The static library is a collection of relocatable object files) files for target arch in the static library, so it will strip the x86_64 and i386 .o files when building product binary.
Also the linker won't link the .o file which is not referenced directly or indirectly by compiled files into executable file.

Xcode 7 builds i386 instead of arm binary for Release-iphoneos

I moved from XCode 6 to XCode 7 and without any changes to the source, or project, or anything my Archive builds started to fail.
After research on the error that lipo produced:
lipo:.../Release-iphoneos/libSDWebImage.a and .../Release-iphonesimulator/libSDWebImage.a have the same architectures (i386) and can't be in the same fat output file
I found that the following:
In XCode 6 the lipo -info returns Architectures in the fat file: .../Release-iphoneos/libSDWebImage.a are: armv7 arm64 and Architectures in the fat file: .../Release-iphonesimulator/libSDWebImage.a are: i386 x86_64 which is correct. I have arm for iphone device and i386 for iphone simulator.
In XCode 7 these two files are the same, and have the i386 architecture! So framework scripts that uses lipo to join these two .a files into one fails.
Why XCode 7 suddenly stopped building my SDWebImage framework for arm? The project settings are unchanged, the library is the same, the scheme has Archive set to Release. Please help.
iMac:~ lukasz$ lipo -info /Users/lukasz/Library/Developer/Xcode/DerivedData/…-etcsjmgakpylpmgchumhnsqpyrev/Build/Intermediates/ArchiveIntermediates/adhoc-stage/BuildProductsPath/Release-iphoneos/libSDWebImage.a
input file /Users/lukasz/Library/Developer/Xcode/DerivedData/…-etcsjmgakpylpmgchumhnsqpyrev/Build/Intermediates/ArchiveIntermediates/adhoc-stage/BuildProductsPath/Release-iphoneos/libSDWebImage.a is not a fat file
Non-fat file: /Users/lukasz/Library/Developer/Xcode/DerivedData/…-etcsjmgakpylpmgchumhnsqpyrev/Build/Intermediates/ArchiveIntermediates/adhoc-stage/BuildProductsPath/Release-iphoneos/libSDWebImage.a is architecture: i386
iMac:~ lukasz$ lipo -info /Users/lukasz/Library/Developer/Xcode/DerivedData/…-etcsjmgakpylpmgchumhnsqpyrev/Build/Intermediates/ArchiveIntermediates/adhoc-stage/BuildProductsPath/Release-iphonesimulator/libSDWebImage.a
input file /Users/lukasz/Library/Developer/Xcode/DerivedData/…-etcsjmgakpylpmgchumhnsqpyrev/Build/Intermediates/ArchiveIntermediates/adhoc-stage/BuildProductsPath/Release-iphonesimulator/libSDWebImage.a is not a fat file
Non-fat file: /Users/lukasz/Library/Developer/Xcode/DerivedData/…-etcsjmgakpylpmgchumhnsqpyrev/Build/Intermediates/ArchiveIntermediates/adhoc-stage/BuildProductsPath/Release-iphonesimulator/libSDWebImage.a is architecture: i386
I ran into the same problem trying to build a multi-architecture framework on Xcode 7. It seems like you are building a static library, which is different, but could be related. I'm assuming you are using xcodebuild command (in an Aggregate target run script?) to build your library for different SDKs and then doing lipo at the end to join all of them.
The problem for me was that the framework/library being built is located in the build/UninstalledProducts folder, and what lives in the BUILD_DIR are symlinks to that. So most likely the libraries in your Release-iphoneos and Release-iphonesimulator are aliases to the same one, hence you see that they have the same architecture (i386 in your case).
To avoid this, navigate to the 'Build Settings' of your static library target in Xcode and ensure the following under 'Deployment':
Deployment Location is NO for release
Deployment Postprocessing is NO for release
You should see that the build no longer outputs UninstalledProducts folder and that all libraries/frameworks built in the BUILD_DIR are unique files, which should now have the correct architectures. You can then do whatever you like with them using lipo. You might have to delete your DerivedData before attempting the above.
For me, the fix was to set the 'Skip install' from Yes no No (which is the default). So insure that in addition to other two options mentioned by Andrew Wei. +1 for great analysis dude.

How do you build PoDoFo on iOS with Openssl support?

I have a project which uses PoDoFo for digitally signing PDF so I am trying to integrate CTOpenSSLWrapper into a single project which uses both PoDoFo and OpenSSL libraries. But when i try to build it, iam getting issues with linking:
The same error repeats with ld: symbol(s) not found for architecture armv7 if I try to run on device.
I have cross-checked every thing:
header search paths
library search paths
Buildphases->compile sources for any missing .m files
But i still have the same issue.
How can i resolve this?
I have cross-checked every thing:
1. header search paths
2. library search paths
3. Buildphases->compile sources for any missing .m files
It appears you are not including the library (only providing the library search path).
Here's what a typical library include looks like for me. Its for OpenSSL, but the same will apply to PoDoFo. In the example below, OpenSSL built for iOS is located in /usr/local/ssl/ios. Headers are located in is located in /usr/local/ssl/ios/include/openssl, and libraries are located in /usr/local/ssl/ios/lib.
Paths:
Library:
If you still have linker errors after adding the library, then perform the following to ensure you library has the architectures you need. I'm using OpenSSL as an example, you should use the PoDoFo library's name.
xcrun -sdk iphoneos lipo -info libcrypto.a
You should see something like:
Architectures in the fat file: libcrypto.a are: armv7 armv7s arm64 i386
If you need an OpenSSL built for iOS, then try this GitHub: noloader/openssl-1.0.1h-ios. Its a fat library, and has all the architectures you might need for iOS.

Check supported architectures of framework in Objective-C

As requested by Apple in the next February (February 2014), every app submitted to AppStore needs to support Arm64 architecture. In my project, I used many static libraries (*.a) and I can check if these libs support arm64 arch. However, I don't know if some frameworks such as Facebook.framework supports this new arch. How can I check it?
Each framework is really just a directory - not even like a package directory, but a plain directory you can browse directly into with Finder. Go into the .framework folder, at the top level you'll find a file with the same name as the framework (for older frameworks that file may be located under a folder called Versions/A within the .framework folder).
That file is really a static library (.a) file, just without the extension. Check it as you would any static library (using file or lipo -info) and you'll see what binaries the file contains.
You'll also know through XCode though. If you switch your project to support arm64 and the libraries you are linking to do not have arm64 support, XCode will not finish linking when compiling for a device.
Check below command in Terminal
lipo -info yourlib.a
Output like :
Architectures in the fat file: yourlib.a are: i386 x86_64 armv7 arm64
In case Framework.framework
Go to inside framework like below
cd /Your_Path_/CocoaLumberjack.framework
then run command
lipo -info CocoaLumberjack

XCode linking error to cached library information without being called (Undefined symbols for architecture)

I am attempting to use a static library (.a) for which I also have the header files (no .m files), but when I add the library to my target (without even referencing/importing the library in my project code), I receive the Undefined symbols for architecture armv7 linking error at compile time. The linker is complaining about classes that supposedly no longer exist in the newest version of the library, and may be using cached data? That suspicion could be supported by the finding that if I create a blank project and add the library in the exact same way as I added it to my project, the new project compiles without issue. But on another note, when I grep my file system for the classes/ivars the linker is complaining about, I get matches in my static library file and my xcuserdata workspace folders. So I am wondering if the issue is in my project, the library I am trying to import, or the way in which I am trying to add the library to my project? And also, why do I get linker errors when I am not even importing/calling the library from my code? I am using XCode 4.6.1.
The linking error is as follows:
Undefined symbols for architecture armv7:
"_OBJC_IVAR_$_GenericPopupView.delegateTheme", referenced from:
-[MyListView setDelegateTheme:] in myLib.a(MyListView.o)
-[MyListView tableView:cellForRowAtIndexPath:] in myLib.a(MyListView.o)
"_OBJC_CLASS_$_SampleMarkView", referenced from:
objc-class-ref in myLib.a(MyListView.o)
"_OBJC_CLASS_$_CheckMarkView", referenced from:
objc-class-ref in myLib.a(MyListView.o)
"_OBJC_METACLASS_$_GenericPopupView", referenced from:
_OBJC_METACLASS_$_MyListView in myLib.a(MyListView.o)
"_OBJC_CLASS_$_GenericPopupView", referenced from:
_OBJC_CLASS_$_MyListView in myLib.a(MyListView.o)
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I added the library to my target by performing the following steps:
Right-click on my project
Select 'Add Files to "My Project"'
Locate the .a file through the popup Finder window
Verify the library path is in my Target -> Build Settings -> Library Search Paths (automatically added when I complete step 3)
Add the header directory to Target -> Build Settings -> Header Search Paths
Verify that the library is listed in Target -> Build Phases -> Link Binary With Libraries
Verify that the library is compiled for armv7 via file, otool, & lipo (I heard the library may also include armv6s, but I didn't see armv6 when I used the three commands)
$ file myLib.a
myLib.a: Mach-O universal binary with 2 architectures
myLib.a (for architecture armv7): current ar archive random library
myLib.a (for architecture cputype (12) cpusubtype (11)): current ar archive random library
$ otool -L myLib.a
Archive : myLib.a (architecture cputype (12) cpusubtype (11))
…
$ lipo -info myLib.a
Architectures in the fat file: myLib.a are: armv7 (cputype (12) cpusubtype (11))
Here are things I have tried inbetween each compile attempt without any success:
Clean the project with (apple)+shift+k
Clean the build directory (DerivedData) with (apple)+alt+shift+k
Delete all the DerivedData directories from the command line
Remove and re-add the library and its headers to my project
Add the library without the headers
Put the libraries in a project that is a target dependency instead of in my project directly
Note that I am unable to add the library as a Target Dependency in Build Settings; should I be able to?
I am also unable to add the library to the target's Build Scheme (via XCode menu's Product -> Scheme -> Edit Scheme... -> Build -> +)
Check the minimum iOS version via iOS Deployment Target in Build Settings – both my target and the library are iOS 6.0
Removed all target search paths except those required for my project to build without the new library as shown below (most importantly I removed the DerivedData from the linker path); my overall project build settings (not the target's build settings) contains no other search paths
Header Search Paths
$(BUILT_PRODUCTS_DIR)/usr/local/include
$(SRCROOT)/../MyLibDirectory/headers
Library Search Paths
$(inherited)
$(SRCROOT)/../TargetDependencyDirectory
$(SRCROOT)/../openssl/lib
$(SRCROOT)/../MyLibDirectory
Removed all other instances of the library on my machine (old versions)
Set the user-defined build setting USE_HEADERMAP = NO
Install on a newly imaged machine with a fresh project checkout and repeat all of the above steps
Add –ObjC++ to the other linker flags (-ObjC is already a linker flag)
Use armv6s by itself and with armv6 in the Valid Architecture under Build Settings for my target and the target dependencies
Set Build Active Architecture Only to Yes
Switched between adding the library in a project group vs. at the parent project group level
Add an import and instantiation of an object from the library in my project code
Played with the library's different settings for Location under Identity and Type in the Utilities window on the right side of XCode when the library is selected
Add the library to the target's Build Phases -> Link Binary With Libraries via the + then Add Other...
I have already tried the recommended steps in these and other posts (only the last two of these listed have actual instructions):
Xcode referencing old
framework
Xcode referencing old/removed frameworks, causing multiple interface
declarations
Xcode custom framework - No known method error
Undefined symbols for architecture armv7
I have not tried changing all of my files to .mm (I didn't need to for previous versions of this library), but I have a lot of .m files that I think might break if I change them all...
Any help or succestions are greatly appreciated! I'm really having a hard time figuring out why this new version of the library is giving me problems.
I resolved my own problem, or rather, the issue was with the black box of a library that I was including. From the command line performed the following command in a terminal window:
grep SampleMarkView myLib.a
Which resulted in:
Binary File:myLib.a matches
I talked to the person who created the library and they sent me an updated version that didn't cause the problem - I guess they had old references in their code. So at the very least I hope this post provides a list of various things to try if you are having a library referencing problem!

Resources