Building Universal Binaries for iOS

(Complete script as a Gist)

Ok, first off, I am no expert in C or compiling but I did run across the topic and found it hard enough to wrap my head around, so that I thought it deserves a post. Basically, you might want to include an existing C library into your application and become baffled with the whole process as I have.

Here we will compile zlib, which is the code that runs when you “zip” a file in most systems. Now, zlib is included in most systems and the Mac is no different. All you really need to do in your iPhone project is go to the Build Settings of your project and in the Link Binary with Library section, click on the ‘+’ button and start typing “libz” which is the native name for the library

press_plus

 

So what is the point you ask? The points (plural) are three:

  1. Due to a known glitch on the new XCode 7.0,  we cannot directly link libz when creating a library project (it works fine on normal iOS and MacOS projects)
  2. Learn how to compile libraries for future use.
  3. Learn a bit of Bash programming (I know, dreadful!)

For everything below you must have the command line tools from XCode installed. If not check in XCode > Preferences > Downloads

Screen Shot 2016-02-13 at 19.03.27

(you cannot see them in the picture above but if they will appear if not downloaded)

About Compiling (skip if not interested)

Compilation is the process of turning source code into machine instructions. Every processor accepts a different set of instructions, so our goal will be to compile for all the different processors found in iDevices. One more processor is that of the Mac, which although not an iOS device, it is what runs the simulator which we need for debugging.

Try writing a minimal C program.

You compile it into a file called “main” by running clang (name stands for for c-language).

you can run the executable file as

The code above can run because it was compiled for the default architecture of your computer. We can compile for a different architecture by passing the appropriate flag. More than that, we need a separate clang executable that is used to compile for iOS platforms. It is installed together with XCode (see steps above) and is located in some obscure file path. To locate it, together with other tools, Apple has provided a separate tool called xcrun.

The first output above is what gets run when you type clang, The second is what we need to compile for the architectures supporting iOS.

We run the appropriate clang in the same manner.

Now however, main will not run because it was compiled for the ARM chip with a different instruction set to your computer.

The code however should run on an iPhone 4.

Quick Bash Refresh

Bash is horrible unless you are on pot or even stronger hallucinogens, which in my personal view must have helped in the development of all Unix kernels.

To create a bash script, open the Mac Terminal (located in Applications/Utilities/Terminal.app) and type

which is how we will name our script. Whatever this script contains it will run, but for this to happen we need to turn it into an executable. This is done by adding the “x” (for execute) flag in the file permissions.

Now we are ready to add commands.

Remember that in bash a variable is defined as a key value pair with no space in between. It is dereferences with the “$” sign, or (more legible) inside the ${VARIABLE} construct.

There are a few special variables that are set automatically and can help in the development of our scripts. Some of these are

Variables can be concatenated

(Quotation marks are optional)

Let’s start (Download and Unzip)

So, first step is to download the library. This is done with the curl command.

The -o option specifies the output file which we create in the TARBALLDIR variable. In the final script we will obviously check for the existence of the file in our specified path and we will use curl only if it is not there.

Then we need to unarchive the tar file.

the xfj stuff tells it to unarchive rather than archive.

What we then need is to build (compile) the library. This is commonly done in three steps

  1. Configure (./configure) – The autoconfig script will generate global variables that have specifics of our system.
  2. Make (make) – most libraries come with a makefile as explained below
  3. Install (make install) – this should move the compiled code into the specified location (such as the /usr/bin directory for command line tools).

The idea is that the steps above need to be repeated for all the different architectures we need to include into our final binary. We will be creating a number of folders named according to the architectures such as “install_i386” and “install_armv7” where we will place a compiled “libz.a” file. 

Make and Makefiles

Make is an old and reliable build system for C code and it consists of the make command which takes as input a text file by the name of makefile. 

A makefile can be as simple as this

where build is called the target, and the line underneath is the command to compile the main.c file (note that this line must begin with a tab).

The creator of the library knows which of the many files need to be compiled and linked in all the details that are needed, and this is why nearly all libraries come with a makefile.

A very common thing is to abstract most of the above in variables that can be tweaked externally like so:

This means that if we type

we will change the compiler from gcc to clang.

We will use this concept to change the output of the zlib compilation to account for the various architectures that we need to include in our universal library.

 

Build for the Simulator

There are two architectures that we need to compile for in order for our library to run in the simulator: i386 and x8664.

Before this step we will run ./configure as explained above and pass the directory that we need as output.

then we set the CC variable which by convention is the path to the compile

We expect the makefile to call it internally by $(CC). The flag -arch will compile the library for the specified architecture. We then execute the makefile by

We will repeat for x8664

Notice the short hand notation for the 3 consecutive make instructions.

Build for the Arm

One thing we can also specify is the minimum version. This will go in another standard makefile variable, the CFLAGS.

The rest is the above code repeated for armv7 armv7s and arm64. This should (at the time of writing) account for all the iDevices available as seen here.

Linking

At this stage we have produced a set of zlib.a files inside different directories. What is now needed is to link them into one library.

The shorthand $(find install_*/lib -name “libz.a”) expands into the list of files together with their paths.

Check bin-ios/ where you should have your very own, hand compiled zlib to link to your project at your heart’s content.

 

Leave a Reply

Your email address will not be published. Required fields are marked *