Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

Building a Universal Binary Framework from Open Source

Mac OS X ships with over 100 open source projects that provide a wealth of functionality. While Apple provides working versions of these for both PowerPC and Intel architectures, sometimes you want to build your own to tune performance or enable custom features. Most of these open source projects use a makefile to drive the build process, which is fine if you intend to build the project the same way each time.

A more flexible approach is to create an Xcode project based on the makefile. Migrating a makefile-based project over to a native Xcode project provides several advantages:

  1. You can take advantage of zero-link and other Xcode features in your Debug builds. Zero-link speeds up the compile-link-debug cycle. Release builds disable Zero-link.
  2. You can distribute builds across machines on your network, which is useful if you have a really large piece of software that takes a long time to build on a single machine.
  3. You have additional output options available in Xcode. For example, you can build a framework instead of a regular library.

With the transition of Mac OS X to the Intel architecture, distributing a single binary that encompasses multiple architectures is often preferable to keeping track of separate, architecture-specific binaries. This can be accomplished by building the project as a Universal Binary, a file that contains code for both the PowerPC and Intel architectures.

In this article we use Xcode to build a Universal Binary framework from the OpenSSL source. You may first wish to read the previous article, Building an Open Source Universal Binary, in which we built static libraries and test applications. That article provides a foundation that will help you understand the project we are building in this article.

You can download the Xcode project .DMG file (540KB), created for this article.

Project Structure

The first step is to download and unpack the OpenSSL source code archive. This article uses version 0.9.8a. After downloading, double-click on the openssl-0.9.8a.tar.gz file to unarchive it. Figure 1 shows the top-level folder structure of OpenSSL. Our discussion will focus on the folders named crypto and ssl.

OpenSSL Project Folder Hierarchy

Figure 1: OpenSSL Project Folder Hierarchy

Most open source projects use a makefile to control the build process. The makefile sets flags that instruct the compiler and linker which source files to include, what build options to use, and the type of the final binary. A discussion of makefiles is included in several ADC articles and documents that are referenced at the end of this article.

What this means is that if you want to port a makefile-based project to Xcode, you should spend some time getting familiar with the makefile. In this example, the makefile helps pare down the list of source code files. OpenSSL includes some files that only pertain to specific platforms, such as the file ssl_task.c, which compiles for DECnet. Including these in an Xcode project will result in errors, so it is best to use the makefile to determine the actual list of files to include.

Build Sequence

The overall build sequence is shown below. Note that we build the framework just once; Xcode handles the packaging into one Universal Binary.

  1. Build the framework
    • Create the project
    • Create the Build Framework target
    • Set the target properties
    • Copy the OpenSSL source files into the project
    • Build the framework
    • Copy the framework
  2. Build the test application
    • Create the Build Cast Test target
    • Set the target properties
    • Copy the framework into the project
    • Build the app
    • Run the app

This general plan may be used with other open source projects.

Creating the Framework

Now that we have taken a cursory glance at the OpenSSL archive structure and the desired set of build steps, we can start the Xcode project process. If you prefer to download the finished Xcode project, use the link provided in the "For More Information" section at the end of this article.

Our first step is to create a new project. In Xcode, select File > New Project.... Then, in the Assistant dialog, choose project type "Cocoa Framework", as shown in Figure 2.

Create a New Xcode Project

Figure 2: Create A New Xcode Project

Name the project "OpenSSLFramework", and create it inside the OpenSSL folder, as shown in Figure 3. Note that the default Xcode behavior is to create a subfolder named after the target, but in this case we removed the subfolder from the path.

Create The Project Directly In The OpenSSL Folder

Figure 3: Create The Project Directly In The OpenSSL Folder

Figure 4 is a snapshot of the final project, in case you would like to see where this is going. Note that we deleted the default groups "Classes" and "Other Sources". We moved the file OpenSSLFramework.pch up one level, out of "Other Sources". We will discuss some of the other files and folders in the next section.

Snapshot Of The Final Project

Figure 4: Snapshot Of The Final Project

Target Properties

The following target properties require modification in order to properly build the framework.

Setting Value
Architectures ppc i386
Header Search Paths . ../include ../../include include
Installation Directory @executable_path/../Frameworks
Preprocessor Macros OPENSSL_NO_ENGINE

Adding the Source Code Files

This step involves adding the source files that make up the crypto and ssl libraries, and a couple of additional header files, to the framework. This step is not difficult, but it can be tedious because you need to be selective when including files. Since OpenSSL is designed to build and run on various platforms, it includes many source files that are specific to certain platforms. Those files are not needed for our Mac OS X build, and will generate errors if included.

The simplest way to handle this is to first add all the source files from crypto and ssl, then use the Makefile in each subdirectory to remove files that are not listed. See Figures 5 and 6. Remove readme files, Makefiles, asm folders, .com files, .pl files, .S files, and even .cpp files. Keep only the .c files that are explicitly listed in the Makefiles. Also keep the .h files. Those may not be listed in the Makefiles, but they are necessary and do not generate warnings if included by mistake.

Add Existing Files

Figure 5: Add Existing Files

Add Source Files To Target

Figure 6: Add Source Files To Target

For example, Listing 1 shows a portion of crypto/aes/Makefile. The LIBSRC variable lists the source files needed for this particular library.

Listing 1: Sub-Project Makefile

#
# crypto/aes/Makefile
#

# snip

LIB=$(TOP)/libcrypto.a
LIBSRC=aes_core.c aes_misc.c aes_ecb.c aes_cbc.c aes_cfb.c aes_ofb.c aes_ctr.c
LIBOBJ=aes_misc.o aes_ecb.o aes_cfb.o aes_ofb.o aes_ctr.o $(AES_ASM_OBJ)

# snip

In the project, navigate to crypto/aes and remove files not listed in the Makefile, as shown in Figure 7. To remove a file, select it and click the <Delete> key. You should click "Delete References" in order to remove the file from the build, but keep it on the drive. This way, if you accidentally remove a file from the build that should be included, you can easily add the file back. If you instead click ""Delete References & Files", you will need to unpack the OpenSSL archive again to fetch a copy of the deleted file.

Remove Unnecessary Source Files

Figure 7: Remove Unnecessary Source Files

What you should be left with for this particular folder is shown in Figure 8.

Remaining Files Match The Makefile

Figure 8: Remaining Files Match the Makefile

Finally, add to the top-level of "Groups & Files" the file MacOS/buildinf.h.

Building the Framework

Now that the project is ready, try building the framework using Build > Build. If all goes well it should build without error, as shown in Figure 9. Note the summary of steps in the bottom pane. This is GCC's way of telling you exactly what commands ran during the process.

Build Result

Figure 9: Build Result

The file command prints information about a file, including its architecture if relevant for that type of file. Listing 2 shows the results of running file in the project build folder. The framework file is "fat", meaning it contains code for multiple architectures.

Listing 2: The file Command

mertz:/Frameworks/OpenSSLFramework.framework/Versions/A asd$ file OpenSSLFramework
OpenSSLFramework: Mach-O fat file with 2 architectures
OpenSSLFramework (for architecture i386): Mach-O dynamically linked shared library i386
OpenSSLFramework (for architecture ppc):  Mach-O dynamically linked shared library ppc
mertz:/Frameworks/OpenSSLFramework.framework/Versions/A asd$ 

Build Issues

If the framework does not build on your first attempt, the most likely cause is including files that have dependencies on other files that are not included. Look at any included header files, and make sure there is a path to those files. You may need to update the User Search Path property in the target properties window. The makefiles specify individual folders and/or header files that automatically get added to the search path. In Xcode, you need to specify the same folders as header search paths.

Some of the makefiles contain targets that include .S (assembly language) files in the build. There are also readme files, makefiles, .com files, and so on. You do not need these files to build the core libraries, so do not include them in the Xcode project targets.

Creating the Test Application

OpenSSL includes a number of test files, usually just a main function that exercises a specific portion of the library. In this section we will create an Xcode target for testing OpenSSL's cast functionality. This target builds a Carbon application that both links against the framework and copies the framework into the application's bundle. This means you do not need to worry about OpenSSL.framework being available on the target system when you run the app.

Integrating a Cocoa framework into a Carbon application provides access to many more features than we use in this example. Read more about it in the Carbon-Cocoa Integration Guide.

Create a new target of type Carbon Application, as shown in Figures 10 and 11.

Create the Target

Figure 10: Create The Target

Name the Target

Figure 11: Name The Target

We then need to modify these phases of the target:

  1. Top-Level: nest the framework build target
  2. Compile Sources: add the source file
  3. Link Binary With Libraries: link the framework
  4. Copy Files: copy the framework into the application bundle.

These modifications are discussed in the following sections.

Nesting the Framework Target

Our first task is to nest the OpenSSLFramework build target in the Cast Test target. This creates a dependency on the framework target. When you build Cast Test, the framework target will be built first. If it needs rebuilding, that will be done automatically. This eliminates the need to manually check whether the framework is up-to-date when you build the application that uses the framework.

As shown in Figure 12, drop the OpenSSLFramework target onto the Cast Test target.

Drag the Framework Target

Figure 12: Drag-and-Drop The Framework Target

The result is shown in Figure 13.

The Nested Framework Target

Figure 13: The Nested Framework Target

Compiling Sources

In this phase, we add the file casttest.c to the project. <Control>-Click on the project name and select Add > Existing Files... (see Figure 14).

Add Files

Figure 14: Add Files

Navigate to crypto/cast/casttest.c, as shown in Figure 15.

Add casttest.c

Figure 15: Add casttest.c

Using the Assistant, specify that casttest.c should be added to target Cast Test, not OpenSSLFramework.

Select Target

Figure 16: Select Target

The file casttest.c should be included in the Groups & Files section, and also in the Cast Test target, as shown in Figure 17.

Add Complete

Figure 17: Add Complete

We need to make a simple change at the top of casttest.c. It has a relative path specified to the file e_os.h. Change it as shown in Listing 3.

Listing 3: Change header location in casttest.c

// Change this:
"../e_os.h"

// to:
#include <e_os.h>

Link Binary With Libraries

The "Link Binary With Libraries" phase is where we specify any frameworks or libraries to link against. Drag the OpenSSLFramework build product to the link phase icon, and then release it, as shown in Figure 18.

Drag the Framework to the Link Phase

Figure 18: Drag The Framework To The Link Phase

Copying Files

We need to add a "Copy Files" phase to explicitly copy the framework into the application's bundle. As shown in Figure 19, select Project > New Build Phase > New Copy Files Build Phase.

Add a Copy Files Phase

Figure 19: Add A Copy Files Phase

Next, select "Frameworks" in the "Destination" popup menu.

Select The Destination For The Copy Files Phase

Figure 20: Select The Destination For The Copy Files Phase

Finally, drag the OpenSSLFramework.framework build product into this phase. See Figure 21.

Drag the Framework into the Phase

Figure 21: Drag The Framework Onto The Phase

Build And Test

Now, make Cast Test the active target, and click Build. It should build without error, and create the application as builds/Release/Cast Test.app.

The casttest.c file runs through several function calls in the framework. Since we built this project as a Universal Binary, it runs the same on either PowerPC or Intel architecture. Figure 22 shows the result of running Cast Test.app in Xcode.

Cast Test Results

Figure 22: Cast Test Results

The test applications provide an easy path to getting familiar with the libraries and how to use them, in preparation for incorporating OpenSSL into your own application.

For More Information

Using the techniques discussed in this article, you can migrate other open source projects to the Universal Binary architecture. The sources below will help you find more specific information.

Posted: 2006-5-29