Sunday, December 6, 2009

Fat iPhone Static Libraries: One File = Device + Simulator Code

Abstract
This post explains how to build a static iPhone library that contains ARM code (for iPhones and iPod Touches) and x86 code (for the simulator), in a single file.

This is useful for companies which do not wish to expose their source code when distributing iPhone code, e.g. AdMob. The currently accepted practice is to distribute separate static library files for devices and for the simulator. This means the that the library clients have to setup separate targets for the simulator and device. The separate targets duplicate most information, and only differ in the static libraries included. This violates the DRY principle,

Solution

Open a Terminal, cd into your library's Xcode project directory, and issue the following commands.

The commands above assume your library's name is ClosedLib and you're targeting iPhone OS 3.0. It should be straightforward to tweak the commands to accomodate different library names and SDK versions.

Assumptions
I assume that the library is not updated too often, so a couple of manual steps are acceptable. I would like to make add an automatic build feature to my zerg-xcode tool, but there is no ETA for that. I also assume that your project's default target is the static library.

I do not assume that the device and simulator SDKs have similar headers. The beauty of my method is that the simulator code is built with the simulator SDK, and the device code is build with the device SDK.

Explanation
The format used in Mac OS X and iPhone OS libraries and executable files is Mach-O. The format supports "fat" binaries, which are pretty much concatenated Mach-O files for different architectures, with a thin header.

Xcode's output is controlled by the options ARCH (Architectures) and VALID_ARCH (Valid architectures). The architectures that actually get built are the intersections of these two options. Due to thedifferent ARM processors in iPhones, device builds have VALID_ARCH set to include ARMv6 and ARMv7. However, simulator builds only target the i386 platform. I want a fat binary for the simulator as well, so I change the VALID_ARCH option to include the AMD64 platform.

The last step in the process is the lipo tool that comes with the Xcode Tools, and manages fat binaries. I use it to create a fat binary containing the union of the architectures in the two static libraries. The device build contributes the ARM architectures, and the simulator build contributes the Intel architectures.

The build process can be tweaked to throw out the AMD64 code, but I wanted to avoid hard-coding processor constants. Most importantly, using a fat library does not translate to larger iPhone applications, because the GNU linker strips out unused architectures when building binaries.

Testing
I tested this method by creating a iPhone static library, and an iPhone Window Application. I built the library using the steps above, and I included the headers and static library in the application. Then I used the debugger to confirm that the library code works both on the simulator and on an iPod Touch 2G.

My solution was only tested with the latest version of Xcode at the time of this writing (Xcode 3.1.2), but it should work on any version of Xcode 3, assuming no bugs come up. I tested on Snow Leopard, but the Leopard version of the iPhone SDK should work as well.

@Anonymous: who are you to complain? The author has provided a great resource to people that need it. If it's not what you want, you should consider asking more politely for the author to spend some of their time to help you.

Or you could just use google, the information that you are looking for has been available since the middle of last year, and wasn't even hard to find.

THANK YOU! Thank you! Thank you! This was exactly what I was looking for. How the heck you figured it out is beyond me, but I'm glad you did. I'm now using this technique on the latest iOS 4 SDK and it works perfectly. Thanks again.

This was awesome, it worked perfectly for me, thanks so much. I'm trying to figure out how to find the header files with role 'Public' inside the library from the host project. I have a question on this open at Stack Overflow:

Short Bio

Victor Costan got infected with a passion for coding at the age of 10, and proceeded to earn a M.Eng in Computer science from MIT. Victor also holds a B.S. in Management that he pursued as he fell prey to the belief that great software requires legions of developers manipulating bloated code bases in repetitive ways. Thanks to Paul Graham's essays, Ruby, and Rails, he re-discovered the joy of coding, and came back from the dark ways of management.

Nowadays, Victor is still crazy about building software that he can impress his friends with, but he also dreams of contributing to making coding even more enjoyable. Victor likes developing mobile and Web 2.0 applications, and occasionally enjoys building some distributed systems behind the apps. When he is not coding, you will most likely find him reading up on some new programming language or software development technique.

At times, Victor likes to pretend he's mentally sane, and can be seen catching up with friends, watching a movie, or exploring Boston.