Kyng Chaos
“The beast is actively interested only in now, and, as it is always now and always shall be, there is an eternity of time for the accomplishment of objects.”
- the wisdom of Tarzan

Universal and 64bit

Universal builds are new with Tiger. 64bit builds are new with Leopard (though there is a limited 64bit capability in Tiger).

Architectures and SDKs

By default, GCC builds for the architecture you are building on, and always 32bit, even on 64bit processors.

The basic method of telling the compiler and linker to build for a different architecture is the -arch flag. The 4 architectures you can build for are ppc, i386, ppc64 and x86_64. The first two are 32bit, and last two are 64bit.

GCC accepts multiple -arch flags, and it will build a "fat" binary of all the architectures specified. This will build a quad-arch binary that will run on any Mac and system (within limits, as noted later):

-arch ppc -arch i386 -arch ppc64 -arch x86_64

One additional flag that may be needed is the -isysroot flag. You specify a SDK path. This basically changes the whole "root" of paths where all libraries and header files are found. There are SDKs for each system version. This flag is required when building ON a PPC architecture FOR an Intel architecture and the system is Tiger, because the system is not universal on PPC OSX Tiger. So, in this case this is needed:

-isysroot /Developer/SDKs/MacOSX10.4u.sdk

Another case where you would need it is when building on Leopard, but you want a 32bit binary that is compatible with Tiger.

SDK Trap

It doesn't hurt to use the SDKs, but in Tiger (Xcode 2.4 and earlier) there is a bit of a problem – since the SDK effectively changes the root path, "/" translates to "/Developer/SDKs/MacOSX10.4u.sdk/", anything not in the SDK will not be found during compilation and linking. /usr/local is not in the SDKs. /Library/Frameworks is not in the SDKs. To fix this, you need to create symlinks in the SDKs to these paths, and any others you may use to install software (ie I use /Users/Shared/unix for static libraries).

ie, for the 10.4u SDK (substitute 10.3.9 for that SDK):

sudo ln -sf /usr/local /Developer/SDKs/MacOSX10.4u.sdk/usr/local
sudo mkdir -p /Developer/SDKs/MacOSX10.4u.sdk/Library
sudo ln -s /Library/Frameworks /Developer/SDKs/MacOSX10.4u.sdk/Library/Frameworks

Apple mostly fixed this in the Leopard Xcode 3.0, and in Xcode 2.5. /usr/local/lib and /Library/Frameworks are in the SDKs. But, /usr/local/include is not, so this must be symlinked into the SDKs or some compilation may fail.

sudo ln -sf /usr/local/include /Developer/SDKs/MacOSX10.4u.sdk/usr/local/include
sudo ln -sf /usr/local/include /Developer/SDKs/MacOSX10.5.sdk/usr/local/include

Where to Add Flags

Most of the time, with configure-based packages, these flags can easily be added using environment variables common to autotools-based configure scripts, and most others as well. First, and possibly the only one needed, is CFLAGS. If there is C++ source, CXXFLAGS may be needed, but often CFLAGS is used for C++ source. Sometimes a separate LDFLAGS may be needed, but usually CFLAGS is also reused here.

Before running configure, just set these in the Terminal:

export CFLAGS="-arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk"

or:

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

An important configure flag is the –disable-dependency-tracking flag. Always use this if it is available – those "dependency-tracking" flags can't handle multiple archs.

A potential problem In Tiger with Xcode 2.4 and earlier is that LD doesn't accept multiple -isysroot flags. In autotools-based software, CFLAGS and LDFLAGS are often both added to link commands, so testing in configure fails (and compilation would fail also). This is not a problem in Xcode 2.5.

One nice trick to automate the arch/isysroot flags is this MacOSXHints.com hint. Most recent configure scripts now handle the CFLAGS/LDFLAGS method of adding the flags, so use ccub only when CFLAGS/LDFLAGS fails.

The ccub trick is very customizable - you can edit the source and easily create versions for different combinations of arch and isysroot flags (just remember that any 64bit archs cannot use the 10.3 or 10.4u SDKs). I currently have these (including the c++ub versions) - cleaned up ccub source here:

  • ccub_t – universal 32bit with the Tiger SDK
  • ccub_l – universal 32bit with the Leopard SDK
  • ccub – symlink to ccub_l
  • ccub_3264 – all-out quad-arch
  • ccub_64 – universal 64bit-only

Common Problems

Endianess

Configured builds often have a configured endianess, instead of using runtime endian detection. For a single-arch build this is not a problem. But with multi-arch builds this is a problem, because configure detects the architecture you are building on, so the opposite arch will build with the wrong endianess. This can be handled by editing the resulting config.h (or whatever it is named) and conditionalizing the endian setting on __BIG_ENDIAN__ or __LITTLE_ENDIAN__ – these are compiler-defined depending on the arch flag used, and it works because headers and source files are processed once per architecture. WORDS_BIGENDIAN is the usual definition used, but it may be something else. This is what the new setting would look like (I use __BIG_ENDIAN__ because Macs started on big-endian processors):

#ifdef __BIG_ENDIAN__
  #define WORDS_BIGENDIAN 1
#else
  #undef WORDS_BIGENDIAN
#endif

You can also conditionalize other configured definitions this way that change depending on the PPC or Intel processor used. Though it would be more proper to use the processor definitions (the same arch names encased in double-undersores, ie __i386__).

Some sources are ancient and assume that OSX = PPC.

64bits

There is only minimal support for 64bit builds in Tiger – only libSystem is 64bit-enabled, so only basic CLI programs can run in 64bit mode on Tiger. It's not worth the hassle, so I ignore it.

64bits has an effect on a few basic data types, so must be considered. These are also commonly configured items, and can be handled in a 32+64bit build in the same way as endianess. The compiler definition to use here is __LP64__. Affected data types include long, size_t, ssize_t, register_t, vm_offset_t and vm_size_t. In all cases, they are 4 bytes on 32bit architectures and 8 bytes on 64bit archs.

For example, long (and its partner unsigned long) might be handled like so:

#ifdef __LP64__
  #define SIZEOF_LONG 8
  #define SIZEOF_UNSIGNED_LONG 8
#else
  #define SIZEOF_LONG 4
  #define SIZEOF_UNSIGNED_LONG 4
#endif

In many cases data type sizes are not handled by configure (and sometimes by both), but directly in the source. These must be found and dealt with. ie a variable definition that must be 32bits, regardless of the processor, is currently defined using long: instead, either use int, or conditionalize the definition.

64bit Carbon

… Does not exist. Carbon is deprecated in Leopard. This is understandable, since Carbon is really a transition API from Mac OS 9. But Apple seems to have a mixed definition of "deprecated" in this case – instead of (roughly) "existing and operational, but discouraged from use, and going away in the future", here it's "the normal deprecated, for 32bits, but non-existent in 64bits because it never existed in the first place". Apple should have deprecated it in Tiger.

Any software that uses Carbon will not build 64bits in Leopard until the developers update to use Cocoa or POSIX APIs. This includes Qt (used in Qgis and OSSIM), Python and Perl. There is a 64bit Qt in development. For Python and Perl, actually the core libraries are 64bit, but the executables are 32bit because the GUI extensions depend on Carbon. You could build just the executables 64bit and run 64bit python scripts, as long as they don't use any Carbonized extensions.

LD

Most packages use GCC for linking the final executable or library. It takes care of passing on the linking to LD, and splitting multiple architectures to link and rejoining them.

LD can't handle multiple architectures, that is, it can't create a fat binary. There are rare packages that use LD directly for linking (ie one componenet of MySQL) or intermediate object files (ie Postgres). There are two possibilities.

libtool – if LD is used only for linking an library, libtool can be substituted for LD. This is /usr/bin/libtool, not the libtool commonly included with source packages. The basic usage is:

/usr/bin/libtool --dynamic|static -o [output.lib] [object files...]

Choose dynamic or static depending on the library being created.

lipo – any other case must use lipo. This means building binaries for each arch (either with copies of the source or with out-of-source builds), and running lipo on each binary created to merge them together:

lipo -create [arch1 binary] [arch2 binary] [...] -output [merged binary]

Backward OS Compatibility

64bit builds are not backwards compatible with earlier OSX versions (the limited 64bit support in Tiger doesn't count), so there is no need to worry about that case. But the system will attempt to run a 64bit binary on Tiger if there is a 64bit processor (ALL PPC processors, Intel Core 2 Duo or Xeon).

For 32bits, the main control of backward compatibility is the SDK used. The simplest way to make a build compatible with an earlier OSX version is to use that SDK version. (Thus my two different ccub's mentioned above – ccub_t and ccub_l.)

It is possible to use the current SDK to make something backward compatible, but it's more of a hassle for little or no gain.

If you are aiming for maximum features and performance for each OSX version, your best bet is to build separate binaries for each version. The Tiger-Leopard divide is a good place to do this, because of the new 64bit capability, and because of the UNIX Conformance that is only partially present in Tiger and completed in Leopard.

macosx/notes/universal64.txt (2365 views) · Last modified: 2009/02/03 08:46 by kyngchaos
Copyright © 2004-2009 William Kyngesburye Driven by DokuWiki Recent changes RSS feed