Sub-projects and Archiving, yet another reason I hate XcodeCategory: Objective-C | 1 Comment |
18 12 2012 |
So I ran into a mind numbing issue trying to archive one of my applications today. Typically when I have a dependency on third party code or another project, I create a static library of that project and then add it as a sub project to my application. When doing this you have to make sure the header files are copied to the public folder and you have to add the path to the “Header Search Paths” so that it can find them at compile time. It seems that the standard location for these files are in /usr/local/include because that is the default folder Xcode sets when you create the static library project. I typically add the name of the project to the end of the path, because I typically reference multiple sub projects.
After you have done this the application will build with the new sub project and you will be able to run it in the simulator and the device without issue. That is until you try and archive your application for distribution. First it will magically stop compiling until you add “$(BUILD_ROOT)/../InstallationBuildProductsLocation/usr/local/include” to your Header Search Paths. This is because when archiving it builds from the intermediate files directory rather than the products directory and it will not be able to find the header files it was able to find just fine when building normally. It will even build just fine when you select build for archiving, because it only builds from the intermediate files directory when you specifically choose to archive. As you can imagine, this is very frustrating if you do not know this fact.
So after adding “$(BUILD_ROOT)/../InstallationBuildProductsLocation/usr/local/include” to the header search path the application successfully archived, but the result was a generic Xcode archive rather than an iOS application archive. After combing stack overflow for some time, I discovered when you link to the public header files this way it also drops them into the archive at the same level as the Applications directory. This causes Xcode to see the archive as a multiple module archive and not an application archive. I found that someone had discovered this on stack overflow here, but I did not like any of the solutions that were provided.
Therefore I started looking around and noticed that there are pre-action and post-action sections in the scheme when archiving and you can run arbitrary shell scripts. I decided then to add a pre-action that removed the usr directory prior to archiving. It looked like this when I was done.
This fixed the issue and made it so that I could archive and distribute the application. As I was looking around and researching the issues I was running into, I noticed that I setup the sub-project pretty much the same way that everyone else does, so why would Xcode seem to have this serious of a problem. I understand that many people do not link to dependencies in a good way and copy and paste code into their projects, but this seems like the preferred and best way to handle dependencies outside of compiling and linking to the static frameworks rather than the projects themselves, which has its benefits and challenges as well. I just keep waiting for the people at Apple to get their crap together and provide an IDE that is reliable and easy to use. We should not have to have in depth, behind the scenes knowledge of how the compiler works and the build process works just to be able to build and distribute an application. That is at least my opinion.
Thanks for this post. Even though it is old now, it was invaluable.
We have a core set of static libraries we use in a bunch of different iOS applications. The libraries are depndent some projects of the app and build static library .a files that, along with their headers, we copy into $(BUILT_PRODUCTS_DIR)/include/. Then all our apps just have search and link paths pointed there.
Adding the extra paths to $(BUILD_ROOT)../InstallationBuildProductsLocation/… as header search paths as you described got everything compiling. (Though, in Xcode 6.3, $(DSTROOT) is equivalent to this path and shorter to type.)
$OBJROOT did not work for me for the pre-archive cleanup script step however, as it terminates in IntermediateBuildFilesPath/ (where my files were not) and not InstallationBuildProductsLocation/ (where my files where).
Once again $DSTROOT to the rescue. My script looks like this:
rm -fr “${DSTROOT}/include/”
So all working now and we can archive again, but I could never have gotten it working without your post, so thanks.