====== 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 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. ''/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 (FIXME verify this). One nice trick to automate the arch/isysroot flags is this [[http://www.macosxhints.com/article.php?story=20061025213851279|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|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.