You've heard the news: Mac OS X is running on the Intel architecture, and the first Intel-based Macintoshes are already shipping. And the message for developers is clear: you should continue to provide PowerPC versions of your applications, while preparing Intel versions of the same applications.
To ease this transition while retaining and improving upon the remarkable performance of Mac OS X, Apple has introduced universal binaries, a format that places native code for both architectures in one package. Universal binaries allow applications to run natively on both PowerPC and Intel-based Macintoshes. Providing support for both architectures in your application is essential, because the Mac OS X platform will support both architectures for years to come. And the time to start making the move to universal binaries is now.
This is an exciting time to be a Mac OS X developer, because the benefits to be gained by moving to this new chip architecture are great, particularly in the area of performance.
If you haven't done so already (and many have), it's time to make the changes your application requires in order to take advantage of the new architecture. Application developers will find that the process of creating a universal binary is straightforward and, in some cases, trivial, depending on a number of factors. In general, applications written according to standard programming guidelines will be the easiest to convert to universal binaries.
This article provides an introduction to universal binaries on Mac OS X. It provides a high-level view of the process and issues, and also contains links to references that provide additional details to help you start writing universal binaries. If you develop on Mac OS X, or are thinking about developing on Mac OS X, read this article to get a clear understanding of what you need to do to be ready for the transition to Intel-based Macs.
Mac OS X and Macintosh Are Ready
Apple has done a quiet, yet thorough, job of preparing Mac OS X for the transition. Mac OS X has been building on Intel for the past five years, starting with Mac OS X v10.0 and continuing through the current shipping version, Mac OS X v10.4 Tiger. Mac OS X is thus cross-architecture by design, and much of the work on the transition has already been done. Prior to the Worldwide Developers Conference 2005, Apple put even more effort into making Mac OS X "sing" on Intel, bringing it on par with Mac OS X on PowerPC.
The operating system is ready, Apple is already shipping the first Intel-based Mac computers, so your job as a developer is to get your code ready. The way to do that is to make sure you are using Xcode and doing what is necessary to build your application as a universal binary.
Xcode, Apple's tool suite and integrated development environment (IDE), has been updated to support universal binaries. Xcode 2.2 and later includes the now famous "Architectures to build for:" checkboxes for selecting either or both Intel and PowerPC versions of an application. The executable code for both architectures resides in a single application bundle. At runtime, the operating system inspects the bundle and chooses the code that matches the underlying hardware. This is done transparently and quickly, so the user never needs to get involved.
Xcode is the fast and easy way for you to create applications for Mac OS X Tiger, and to take advantage of all the great technologies that ship in Mac OS X. Xcode is also the best way to ensure that your application is ready for the future. Xcode brings together the power of UNIX and a mix of high-performance development technologies; combine that with the ease of use of Mac OS X, and the result is an unparalleled tool suite for creating the best Mac OS X applications.
Xcode 2.2 and later includes such technologies as Fix and Continue, ZeroLink, predictive compilation, remote debugging, and distributed builds. Xcode also includes GCC 4, the industry-standard compiler that contains a number of advanced optimization techniques that are designed to make your applications fly. The most significant of these is the use of Tree SSA (Single Static Assignment), which is designed to be both language and target independent and allows a level of code analysis and optimization that was impossible with earlier versions of the compiler.
The biggest and by far the most important architectural difference between the PowerPC and Intel architectures is how multibyte data is stored. PowerPC stores the most significant byte first while x86 stores the least significant byte first. Byte ordering is also referred to as endian format; PowerPC uses big endian, and x86 uses little endian.
To account for these differences, you'll need to swap bytes or find an endian-neutral way to handle data. Most of the time byte ordering issues won't prevent you from compiling code, but they will produce unexpected results in your application. You might see incorrectly colored user interface elements or images, fonts that are wrong, numerical results that are way off, and so on.
These and other issues related to architectural differences are dealt with in more detail in the Universal Binary Programming Guidelines, Second Edition.
Do you need to make a universal version of your application? That depends on the type of application. Widgets, scripts, and Java applications will "just work" without any recompile, due to the nature of those applications: widgets and scripts are interpreted (not compiled), and Java applications exist already in a portable format. For details on some possible Java issues, see Technical Q&A QA1295 for details.
Cocoa applications, which are written using Xcode, may require a few tweaks before recompiling, but the overall effort will be small. While the Cocoa framework handles endianess issues transparently, you should be careful when using graphics APIs and external C/C++ APIs that may not be so forgiving.
Carbon applications developed with Xcode may require a few more tweaks, and a recompile, but should still build relatively easily. Carbon applications that use custom resources may need retooling to handle endianess.
Carbon applications written with Metrowerks CodeWarrior will first require a move to Xcode, then the tweaks and recompile mentioned previously. Read Porting CodeWarrior Projects to Xcode for guidelines on moving your application from CodeWarrior to Xcode. The porting guide covers numerous issues, such as handling pragma statements.
There are numerous other issues that you may encounter other than byte-ordering. But these tend to be specific—not everyone will need to address them. There is a detailed list of topics for you to refer to in the Universal Binary Programming Guidelines, Second Edition.
If you provide a plug-in API for your application, you should think about your plug-in developers and determine how your application will support both architectures.
These are only a few examples. Read the Universal Binary Programming Guidelines, Second Edition. for more information.
Creating A Universal Binary
The overall process for creating a universal binary for any application is straightforward: identify dependencies, get the application to build, then get it to run. The following are general, not detailed, steps. You should read the references included at the end of this article for detailed information.
First, identify any dependencies that your application has on third-party libraries, plug-ins, and kernel extensions. The fewer you have, the easier the process will probably be. If you can eliminate the dependencies or obtain updated versions, then that will make your job easier. Also, now is the time to abstract or remove hardware dependencies where possible. Use high-level APIs that shield you from low-level machine details.
Next, to get the application to build, you need to move the code to Xcode 2.2 or later and GCC 4. GCC is the open source GNU Compiler Collection used by Xcode to compile and link your application. If your code has dependencies on earlier versions of GCC (you know who you are) or CodeWarrior, then you may have some cleanup to do first. General CodeWarrior issues were discussed briefly above under "Applications."
The next step is to make the build environment ready for building universal binaries. If you already use a previous version of Xcode, then simply upgrading to Xcode 2.2 or later should be sufficient. If your project uses only GCC and not Xcode, then you may need to retool configure scripts, makefiles, and other custom build tools for the Intel-based build environment.
Note: You can create a universal binary on either a PowerPC or Intel-based Macintosh, but you will need both a PowerPC and an Intel-based Macintosh to thoroughly test the resulting application.
Your application should also adapt to a few small API changes. If your code accesses certain structure fields directly, without going through Apple-provided accessor functions, or if it performs bit manipulation, you may need to update those parts of the application. Details on this process are included in the Universal Binary Programming Guidelines, Second Edition.
Once the application builds, run it and see what breaks. Fix the errors, and you're done!
For More Information
Want to learn more? Here are additional resources to help you get started.
Developer Transition Resource Center
The articles, tech pubs and videos found on the Developer Transition Resource Center provide a lot of great content to help guide you through the transition process.
Watch Steve Jobs announce this exciting new direction for Apple during the Keynote address at Apple's Worldwide Developers Conference 2005.
Every developer needs to read the Universal Binary Programming Guidelines, Second Edition. This document contains detailed information about porting issues, architecture differences between PowerPC and Intel, data alignment, byte swapping, and vector-based coding. These are just a few of the topics covered.
CodeWarrior users should read Porting CodeWarrior Projects to Xcode for a better understanding of the differences between Xcode and CodeWarrior, how to import CodeWarrior projects into Xcode, and detailed information on many conversion issues.