end to end build for mac os x

Goal: Build CairoGraphics private Frameworks directory, using only X-Code. Produce fat binaries capable of running on 10.4, 10.5, 10.6, on PPC, and on Intel. The result is for the native OSX backends, not the any of the X-Windows ones. A "private" Framework is not a generally reusable one, but one that is packaged specifically with a .app bundle.

Original Author: Travis Griggs (travisgriggs@gmail.com)

Original Version: 1.8.8

Other Tested Versions: 1.9.4, 1.10.2

Tools Needed:

Initial Setup

Designate a directory to work in. Clean it out and then we'll do all work in there with fresh copies.

   export BuildDir=${HOME}/BuildCairo
   rm -rf ${BuildDir}
   mkdir ${BuildDir}
   cd ${BuildDir}

Download and untar tarballs

Use curl to download FOUR tarballs: pkg-config, libpng, pixman, cairo. Adjust specific version paths as desired

   curl http://pkgconfig.freedesktop.org/releases/pkg-config-0.23.tar.gz -o pkgconfig.tgz
   curl ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng-1.2.40.tar.gz -o libpng.tgz
   curl http://www.cairographics.org/releases/pixman-0.16.2.tar.gz -o pixman.tgz
   curl http://www.cairographics.org/releases/cairo-1.8.8.tar.gz -o cairo.tgz

   tar -xzf pkgconfig.tgz
   tar -xzf libpng.tgz
   tar -xzf pixman.tgz
   tar -xzf cairo.tgz

   mv pkg-config-* pkgconfig
   mv libpng-* libpng
   mv pixman-* pixman
   mv cairo-* cairo

Why the last 4 mv commands? The default directory names are things like pixman-0.1.16 (version numbers included in the name). Some of the packages have compile paths dependent on the simpler names. So we do all 4 of them to keep them simple and consistent.

Build Pkg-config

Now we compile the pkg-config utility, and set up some environment variables to manage it. Make sure that if you open a new shell, you set the two PKG_CONFIG related variables again.

Why pkg-config? Pkg-config is a package configuration management tool. OSX comes with one, but it's very old and the Cairo build system wants a newer one. You may have a new one via something like macports, but we don't want some of the other macports derived pkg-config dependencies. Building our own keeps the build nice and isolated.

   cd ${BuildDir}/pkgconfig
   ./configure --prefix=${BuildDir}
   make install

   export PKG_CONFIG=${BuildDir}/bin/pkg-config
   export PKG_CONFIG_PATH=${BuildDir}/lib/pkgconfig

The --prefix argument affects the make install command, such that it places any binaries built in our own directory, requiring no root permissions of us. Makes it easy to clean everything up later too.

Setup environment variables for fat binary compilation

WARNING If you have to go backwards in these steps, it is important that you unset these variables before trying to compile pkg-config again. Or use a different shell. We set these variables AFTER pkg-config is built for a reason.

We set the following 3 environment variables to influence the compile/link operations buried in subsequent commands, so that we build fat binaries, that can be run as far back as 10.4. If you open a new shell, you'll need to set these variables again.

   export CC="gcc-4.0"
   export LDFLAGS="-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk"
   export CFLAGS="-Os -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk"

Why are we setting CC? For 10.6 machines. You can skip it for 10.5/10.4. But for 10.6, the default gcc version is 4.2.0 which doesn't mix well with the 10.4 SDK.

What if you want 64 bit builds too? Then you have a choice to make. If you want 64 bit, you have to set 10.5 as your low limit. And you end up with the following variant of the above. You should do only one of the 2 sets, the above 3 commands, or the 3 below.

   export LDFLAGS="-arch ppc -arch i386 -arch ppc64 -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.5.sdk"
   export CFLAGS="-Os -arch ppc -arch i386 -arch ppc64 -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.5.sdk"

Build libpng

Build the libpng library, and install it in our local build directory.

   cd ${BuildDir}/libpng
   ./configure --prefix=${BuildDir} --disable-dependency-tracking
   make install

What's the --disable-dependency-tracking? We need that since we enabled all of the different -arch flags in our CFLAGS/LDFLAGS variables.

Why are we doing this? CoreGraphics supports png functions, but the APIs are not the same as the wide spread libpng APIs, which Cairo is written for. We could try and use the one from macports, or some other source, but you'll need to track down and deal with those dependencies in that case.

Build pixman

Build the pixman library, and install it locally. Pixman is used by the Cairo library for (among other things) all of its fallback operations and Image surface type operations.

   cd ${BuildDir}/pixman
   ./configure --prefix=${BuildDir} --disable-dependency-tracking
   make install

Build cairo

This builds the final library. It's not ready for widespread distribution yet, keep going after this step.

   cd ${BuildDir}/cairo
   ./configure --prefix=${BuildDir} --disable-xlib --disable-ft --disable-dependency-tracking
   make install

Why the --disable-xlib thing? The configure script will print a list of features that are enabled and disabled when it is done. It mostly guesses them right. It is actually possible to build a Cairo library with both X windows and Quartz backends enabled, but actually using the library then has some nuances. So for the goal of this particular build, to produce a Cairo framework to use with native Apple apps, we disable the X windows backend.

Package dylibs as a relocatable Frameworks staging directory

Private Frameworks for a relocatable .app bundle in OSX, are by convention stored in a Framworks directory which is placed next to the MacOS. So we'll build that directory here. First, we'll make that directory and copy the relevant dylibs there.

   mkdir ${BuildDir}/Frameworks
   cd ${BuildDir}/Frameworks
   cp ${BuildDir}/lib/libpng12.0.dylib .
   cp ${BuildDir}/lib/libpixman-1.0.dylib .
   cp ${BuildDir}/lib/libcairo.2.dylib .

Your freshly copied dylib's still have one problem. They have absolute filenames embedded in them, which look like our ${BuildDir} path. We use the install_name_tool to adjust the 'id' and 'dependency' fields found in the headers of the dylib files. The @ character is not a bug.

   install_name_tool -id @executable_path/../Frameworks/libpng12.0.dylib libpng12.0.dylib
   install_name_tool -id @executable_path/../Frameworks/libpixman-1.0.dylib libpixman-1.0.dylib
   install_name_tool -id @executable_path/../Frameworks/libcairo.2.dylib libcairo.2.dylib
   install_name_tool -change ${BuildDir}/lib/libpixman-1.0.dylib @executable_path/../Frameworks/libpixman-1.0.dylib libcairo.2.dylib
   install_name_tool -change ${BuildDir}/lib/libpng12.0.dylib @executable_path/../Frameworks/libpng12.0.dylib libcairo.2.dylib


You've got a Frameworks directory you can move around now with relocatable path entries in it. Place it in your .app bundle in the Contents directory, next to the MacOS directory. In your executable, you can load and bind Cairo by passing "@executable_path/../Frameworks/libcairo.2.dylib" to dlopen().