The updated linker and libtool that come with Xcode 4.6 apparently contain some changes that are causing problem when building projects that link and depend on static libraries. One appears to be a bug, the other is an annoyance. Fortunately we found workarounds for both.
Update: Not a bug after all. Rather a “learning experience”. Details below.
For iOS apps you can have two kinds of targets that compile source code. One are apps, for these the linker combines the object code files into a single binary executable and “links” symbols. The other are static libraries, for these the linker is not called. Instead libtool does some post processing.
It is a bit counter-intuitive that there is still a linker build phase, even though the linker has no work to do. Instead of linking this phase assembles the .o files that came out of the compiler into library archives with the .a extension. It also is able to combine multiple static libraries into a bigger one.
For example I’m using this mechanism for DTRichTextEditor where I am combining the libDTCoreText into the libDTRichTextEditor so that developers using it only have to add a single library.
Bug: Linker tries to link Files belonging to iOS 6.0 SDK
The first issue prevented us from building one of those large compound static libraries. libtool failed complaining that it couldn’t find libicucore.dylib in the iPhone6.0.sdk folder.
I had already raised this issue while Xcode 4.6 was still in beta, Apple didn’t get around to fixing it in time. It appears that somewhere there is a hard-coded reference for the 6.0 version of libicucore, either by itself or caused by the other external libraries that are getting mashed together here: Google Analytics, Urban Airship, ShareKit and a few of our own.
The workaround we found is to restore the 6.0 SDK from Time Machine backup after having replaced Xcode.app in /Applications with the 4.6 version.
This allows the build to go through. I hope that there is no adverse effect on the resulting app which would be the case if Apple changed something in libicucore’s APIs from 6.0 to 6.1 – which they probably didn’t because this SDK version centered around additional LTE carriers and local search for MapKit.
Update: Somebody at Apple gave me a hint that helped me solve the riddle. He suggested that I might have an absolute path somewhere in my project, the correct would be $(SDKROOT).
We are including a library that uses Square’s SocketRocket and thus requires that you add libicucore.dylib to the app target. We also had it in our fat static library which in itself is totally useless because libtool does not add anything from dynamic libraries into the library archive.
In this screenshot, which depicts the “Link Binary With Libraries” build phase for a static library you can see libicucore.dylib (mistake #1) which has a location “Relative to Developer Directory” (mistake #2).
When I simply removed the erroneously added dylib in this place the error ceased to occur. Ok, not a bug. There I said it.
Problem: Size Too Large?!
With the above problem worked around, we started to see a new problem. Something about an archive member problem: “size too large (archive member extends past the end of the file)”. Now having a too large member is troublesome.
I think the problem in this case resulted from having different valid architectures set for my main app and the static libraries which are included as sub-projects. Since we don’t support armv6 any more I went through all sub-project settings and removed the additional armv6 entries.
Long story short: when building for armv7+armv7s you want to make sure that all sub-libraries also contain code for the two processor types. Check the build log and make sure that you see two Libtool entries, one for each CPU. If there is only one then this probably causes this issue.
Annoyance: Libtool Warnings About -dynamic, -ObjC and -all_load
When linking in Objective-C code from static libraries you need the -ObjC linker flag. Previously you also needed the -all_load if the static library contained categories since the optimizer would throw away those category extensions believing that they weren’t used.
Apparently Libtool gets the same flags as the Linker and this causes it to complain about these now, as you can see in the screenshot above.
“-dynamic not specified, -all_load invalid”
“-dynamic not specified, the following flags are invalid: -ObjC”
Once we fixed the SDK 6.0 problem those messages didn’t cause Xcode to think that there was an error in this processing step. So this doesn’t appear to be a problem, rather an annoyance.
The library tool does not need to care about these linker flags since it always merges all object code into a single lib. I never noticed these warnings before the two described bugs which caused me to inspect the build log.
The fix is very simple: remove all “Other Linker Flags” from static library build settings.
When I went through this large project I was astonished to find -ObjC in so many places. I didn’t remember adding these. It turns out that Xcode has this in the target template for static iOS libraries. A quick test …
If you create a new project for a static library Xcode adds the -ObjC for you.
You have to this entry again if you want to fix the mentioned warnings. The part about the -dynamic is a side-effect, if you remove all linker flags then there is no mention about dynamic any more as well.
It becomes clearer with every new Xcode release – which regularily updates LLVM now as well – that we should set up our continuous integration system to also build all our projects with the newest BETA version of Xcode so that we can catch such problems early on.
This gives Apple a chance to address them in time for the actual releases. Don’t get me wrong, I’m quite happy for Apple spending so much time on making LLVM the most awesome compiler for us. And where there are changes bugs tend to creep in.