This is the first part of a tutorial in which I’d like to introduce you to one of my favorite open-source projects, The Cocotron (imagine the ominous silence of a Soviet nuclear reactor as the backdrop for pronouncing this name).
Cocotron lets you take a Cocoa-based Mac app and port it to Windows without ever leaving Xcode — just add a Windows target to your project and you’re set. Sounds too good to be true? Well, as usual, some limitations apply. Cocoa is a large and rapidly evolving framework, and it would be very difficult for a loose team of open-source volunteers to achieve 100% compatibility with Apple’s latest and greatest. But Cocotron can take you most of the way, and if you have the possibility to plan ahead for compatibility, the odds of being able to make use of Cocotron are further increased.
The Cocotron is not just a vapor framework outlining some kind of theoretical feasibility: it’s been publicly available since late 2006, and ports accomplished using Cocotron are out in the wild. Personally I have used it deploy Mac-developed custom apps on Windows, including a fairly complex GUI subtitling tool used by the Finnish Broadcasting Company for a popular TV game show. (Based on activity in the Cocotron mailing list, there is also a company with a well-known consumer-oriented Mac product who are nearing completion of a Windows port using Cocotron.)
Something that appeals to me in Cocotron is its strict, almost austere “no-frills” policy. Christopher Lloyd, the project’s initiator, runs a tight ship. Unlike some other cross-platform toolkits (* cough Gtk+ and Qt *), the framework is lean: Cocotron’s Windows DLLs for Foundation and AppKit only take about 6-7 MB. And it’s self-contained – there are no other dependencies by default. As fits this culture of independence, Cocotron is MIT-licensed, so you can basically do anything you please with the code as long as credit is given.
True cross-compiling on the Mac is a core part of Cocotron’s promise. As such, its fortunes are closely tied to Apple’s Xcode development environment which has undergone a lot of changes over the past five years. Luckily Xcode has consistently remained open enough to allow custom cross-compilers (perhaps largely thanks to the advent of iOS and Cocoa Touch on ARM CPUs, which has made cross-compiling an essential part of Xcode).
Xcode 4 was the biggest change in the product’s history. I had been long dreading making the leap to Xcode 4 for two reasons: the new UI would force me to re-learn many workflows, and I was worried that Cocotron might not work properly anymore. Now it has been a week since I finally made the change, and I can report that I’m pretty happy about it. That means you could be happy too with Cocotron and Xcode 4.3! This tutorial will tell you how.
What is The Cocotron?
Fundamentally, it’s two things:
- The Cocotron Developer Tools (CDT) – a cross-compiler and associated software that installs alongside Xcode and targets the platform(s) of your choice. (Not just Windows on x86 – you can also use Cocotron to build for Linux on ARM, Solaris on SPARC, and many other platforms. In this tutorial, I’m going to talk about Windows only because that’s what I’m familiar with.)
- An open-source implementation of the Cocoa frameworks. Foundation and AppKit are the most important, but there are also pieces of other Mac OS X APIs implemented, e.g. parts of ImageKit, CoreText, ScriptingBridge… The guiding principle is that people implement what they need for their port.
That’s the big picture. To better understand what actually goes on, we also need to be aware of some specific components that link the high-level blocks:
- The Objective-C runtime. The GNU Compiler Collection (GCC) has supported Obj-C for about 20 years. NeXT had to release their modifications to the C compiler, but they didn’t need to release the source code for their Obj-C runtime. So it happened that although GCC included support for the language, there was never a standard runtime to go with the compiler. For a dynamic language like Obj-C, this was a problem. NeXT became Apple and continued with their own proprietary runtime. The GNUStep project developed an open runtime for GCC, and it eventually diverged from Apple’s – different function names, etc. Because of this old incompatibility in open-source Obj-C land, Cocotron includes its own Objective-C runtime that is more compatible with the NeXT/Apple runtime. (Today this is largely a historical artifact. A new modern runtime has been introduced in GCC 4.6, and probably Cocotron will eventually adopt it, removing the need for a custom runtime altogether.)
- A modified GCC. The C/C++/Obj-C compiler installed in Cocotron is not quite stock GCC. It’s patched to use the Cocotron Obj-C runtime, and also to support some of Apple’s extensions to GCC which Xcode assumes are present (e.g. specific linker flags that are necessary on Mac OS X but don’t exist elsewhere – with a stock GCC, these would produce errors).
- A set of Xcode templates. These are installed into Xcode’s support files so that you can easily choose the Cocotron compiler for your Windows targets. This is the only modification made in Xcode by the Cocotron install. If you remove these, your Xcode will be just like it was before.
Now that we know what’s going to be installed, let’s get started.
This tutorial is written for Apple’s latest development platform: OS X 10.7 Lion and Xcode 4.3. You can also use Cocotron on older OS and Xcode versions, but you may need different versions of some software, and some workflow may be different. (I’ll try to include links to support files for older platforms, to the extent that I’m aware of issues.)
Xcode 4.3 was a watershed release because it no longer includes command-line development tools by default. (In other words, when you type gcc in the terminal, you’ll get “command not found”). Xcode doesn’t even install in /Developer anymore, like all previous versions – it is now just another app from the Mac App Store, although one with far greater privileges than 3rd party apps. This is really great for those of us that like to keep multiple versions of Xcode around, and it has also cut down on the size of the install quite a bit.
To make this install work, you’ll need to get those missing command-line tools, though. They are available as a package on Apple’s Mac developer site, or you can install them directly in Xcode (more information: What’s New in Xcode 4.3).
Building the cross-compiler
A few versions back, something big happened in Mac compiler land. After having used GCC practically ever since NeXT was a twinkle in the eyes of Steve Jobs and Avie Tevanian, Xcode switched away from GCC to a new compiler architecture called LLVM. This new compiler comes in two forms: there’s a whole new shiny front-end called Clang, and then there’s a another front-end called llvm-gcc which, unsurprisingly, is compatible with good old GCC. The idea is that you can feed llvm-gcc your existing projects, and it will build them just as GCC did.
For some projects, this doesn’t quite work. There’s software that either takes advantage of some rare GCC features not supported by llvm-gcc, or perhaps has been compiled only with GCC for so long that it has acquired non-obvious dependencies on the compiler. As it happens, GCC itself is one of these projects. This is problematic for Cocotron because its cross-compiler is a modified GCC, as outlined above. If we can’t compile GCC on the Mac, we can’t build the Cocotron Developer Tools.
In the long term, this can be solved by either fixing the issue that prevents llvm-gcc from building a working GCC, or by changing Cocotron to use Clang instead for its cross-compiler. (In fact this may already be possible, but it’s not supported by the default Cocotron installation, and I don’t know the details of what’s required to set up Clang for Cocotron.)
In the short term, we need a solution that’s more easily available. The High Performance Computing for Mac OS X project provides a regularly updated build of the latest GCC for the Mac, so I decided to use that. You can download readymade GCC binaries directly from their site (both Lion and Snow Leopard are supported).
For extra convenience, I’ve made an installer package of their GCC for Lion. Click this link to download it from my site:
HPC GCC 4.7 Installer for Lion (69.4 MB)
This installer doesn’t overwrite the Xcode command-line tools. It merely places GCC 4.7 in the folder /usr/hpc-gcc-4.7 and creates a symlink that allows you to access this compiler by the command gcc-4.7 in the Terminal.
Cocotron’s install script assumes that the compiler is named gcc, though. But if we look at the file /usr/bin/gcc, we see that it’s just a symlink that points to the real compiler executable elsewhere. Here’s the file listing on my system:
$ cd /usr/bin $ ls -l gcc* lrwxr-xr-x 1 root wheel 12 Feb 22 12:47 gcc -> llvm-gcc-4.2 lrwxr-xr-x 1 root wheel 24 Feb 22 12:32 gcc-4.7 -> /usr/hpc-gcc-4.7/bin/gcc
So, by default gcc points to llvm-gcc-4.2 (which itself is just another symlink). Let’s change gcc to point to the GCC 4.7 that we just installed. Don’t worry, this will be restored as soon as we’ve finished installing Cocotron’s tools:
$ sudo ln -sf gcc-4.7 gcc
(The $ sign is not part of the command, it just indicates the prompt.)
We can verify that the command gcc now indeed calls GCC 4.7:
$ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/hpc-gcc-4.7/bin/../libexec/gcc/x86_64-apple-darwin11.3.0/4.7.0/lto-wrapper Target: x86_64-apple-darwin11.3.0 Configured with: ../gcc-4.7-20120204/configure --enable-languages=c,c++,fortran Thread model: posix gcc version 4.7.0 20120204 (experimental) (GCC)
Now that we have a compiler that’s known to work for the job at hand, we can proceed with installing Cocotron. Download the latest installation package from here:
Install CDT Tools
(at the time of writing, the latest file was InstallCDT-2011-12-27.zip)
Unzip InstallCDT, and you have a folder containing a bunch of shell scripts. In the Terminal, go to the InstallCDT folder and enter:
$ sudo ./install.sh
(Now you may be asking: does this install script really need to run as root? I first tried it without sudo, but the installation failed at the last moment. Since the script takes quite a long time to run – it’s building a whole compiler, after all – I didn’t feel like experimenting to figure out what’s going wrong.)
After the script has finished and you’ve had a cup of coffee, you can check that the Cocotron cross-compiler was correctly installed:
$ /Developer/Cocotron/1.0/Windows/i386/gcc-4.3.1/bin/i386-mingw32msvc-gcc -v Using built-in specs. Target: i386-mingw32msvc Configured with: /Developer/Cocotron/1.0/Source/gcc-4.3.1/configure [...] Thread model: win32 gcc version 4.3.1 (GCC)
i386-mingw32msvc? What kind of platform is that? Well, this monster acronym is basically GNU-speak for “plain old Windows”. MinGW is the project that provides GNU-compatible tools for Windows that use Microsoft’s standard infrastructure to the maximum extent possible. (There’s another project, Cygwin, that provides a complete GNU userland running on Windows but is not as compatible as MinGW. You can’t mix the two.) After mingw, the platform identifier has the number 32, which indicates that we’re going to be building 32-bit binaries. Finally there’s msvc, which is short for “Microsoft Visual C” — this indicates that this compiler produces binaries that link against Microsoft’s default C runtime, which is available on all Windows versions out there. So this is all good: we can use this compiler to produce binaries that work on Windows without needing any extra DLLs or other baggage.
CDT is in place, so it’s time for the post-install cleanup! First, let’s put the gcc symlink back the way it was:
$ sudo ln -sf /usr/bin/llvm-gcc-4.2 /usr/bin/gcc
Secondly, let’s apply more reasonable permissions to the Cocotron folders. Because I ran the CDT installer with sudo, everything in Cocotron is owned by root. That would make life difficult because our projects will need to write into those folders e.g. when building in Xcode. Here’s the fix:
$ sudo chmod -R a+rwx /Developer/Cocotron/
Building the frameworks
With the Cocotron Developer Tools installed, we can move on to the genuine meat of this juicy cross-platform hamburger – the Cocotron frameworks. These are hosted on Google Code. (If you want to have a look around the source beforehand, try browsing. In my humble opinion Cocotron is very cleanly organized for such a major project, so it’s easy to just look around.)
Google Code uses Mercurial, a distributed version control system that’s rather similar to Git (but quite different from CVS and SVN). If you don’t have Mercurial installed, you’ll need to do that first. You can get it through a package manager like Homebrew or MacPorts, or you can just download binaries from here:
With Mercurial installed, you can “check out” your own local copy of the Cocotron source tree. Here are the instructions:
(If you’re new to Mercurial, have a look at their rather excellent Guide before going too crazy with commands – distributed version control may take some time to get used to the concepts and possibilities, but it’s definitely worth it.)
Once you’ve cloned the Cocotron source tree, you can build the projects. (Finally some Xcode action!)
Although there are a few dozen projects right under the “cocotron” root, don’t worry, you don’t need to build them all! Cocotron is organized so that most of the frameworks are just subprojects that are incorporated into one of the main projects that depend on their functionality. For example: building Foundation also builds CoreFoundation, and building AppKit also builds CoreText.
It’s easiest to start by building Foundation. Open Foundation.xcodeproj into Xcode. Click on the Targets button in the toolbar, and you’ll be presented with a rather bewildering array of targets to build:
(See? I told you that Cocotron supports lots of platforms.)
Pick Foundation-Windows-i386 in this list. Next, we need to change the build configuration. This is a part of Xcode that was completely revamped in version 4, for reasons that are somewhat mysterious to me… Previously it was very easy to switch between Debug and Release build modes. Now, it’s a bit more tricky. If one doesn’t pay attention, it’s easy to accidentally build with the wrong mode.
For Cocotron, we always want to use Release mode. Trust me on this. Maintaining two sets of binaries is too much of a pain, and there’s nothing much to be gained by having a debug build on Windows anyway. (By the way, you can use gdb, the GNU debugger, on Windows to debug Objective-C binaries.)
So, before building Foundation, ensure that it’s going to be built using the Release configuration. With the Foundation-Windows-i386 target selected, choose Edit Scheme from the same toolbar menu. This view will open:
Select Release under Build Configuration. Now you’re ready to build Foundation.
Congratulations – with Foundation in place, you now have a minimal Cocotron installation. You could take a Mac OS X command-line tool that uses Cocoa’s Foundation classes and build it for Windows. (Setting up the Windows target requires certain magic incantations, of course. I’ll get to that later.)
Next you can build AppKit using the same procedure as with Foundation: choose the right target, check the build configuration.
Building AppKit may require some setup if you want to include certain features. It’s possible to add support for things like JPEG writing and PDF compression, but these depend on external libraries (libjpeg and zlib, respectively).
Like I mentioned above, Cocotron has a policy of no dependencies: the base frameworks don’t depend on anything else. The same need not apply to your apps, of course. In practice most apps use libraries other than just Cocoa, so supporting installation of libraries is fairly important.
It’s pretty easy to make libraries available to Cocotron. I’ll show you how soon — but first, walk with me into the folder structure…
What lives in /Developer/Cocotron
As you may have noticed, Cocotron installed itself in the folder /Developer/Cocotron. As of version 4.3, Xcode itself doesn’t use /Developer anymore, but that doesn’t bother Cocotron – the folder was created by the InstallCDT script. (If you’re using an older Xcode, Cocotron will happily live alongside Xcode in /Developer/Cocotron.)
Under Cocotron’s folder, a deep structure unfolds. There’s a version-numbered folder, currently 1.0, which contains the Cocotron installation. Here are the downloaded files, sources, and utils. Platform-specific components are located under the platform’s name. So, my Cocotron Windows installation is in /Developer/Cocotron/1.0/Windows/i386:
I’ve got a bunch of libraries installed here. I’ll get to that in a moment, but first let’s take a look at Frameworks. Everything that we built from the Cocotron sources ends up here. There’s Foundation.framework, AppKit.framework, etc. These files are bundles, just like regular Mac OS X frameworks. The contents are a bit different:
As you can see, there’s a symlink to a Windows dynamic library file, AppKit.1.0.dll (the actual file is located under Versions). There’s also an import library, libAppKit.a, which provides the symbol definitions so that the GCC linker can link our Windows executables against this Windows AppKit DLL.
Going back a bit – what about those other libraries? The convention on Cocotron is that libraries are installed in the platform folder. Each library goes in its own folder which has include and lib subfolders. (The former of course contains the headers needed for compilation, while lib contains the import library for linking.)
When deploying your app on Windows, you’ll also need the DLL file for the library. Windows’s dynamic library loader is not able to do complicated guided lookups for DLLs, so it’s easiest to place it in the same folder as your executable. (This same operation needs to be done for the Foundation and AppKit DLLs, but Cocotron provides a tool called retargetBundle that can handle this. I’ll explain that later.)
Where do you get these libraries for Cocotron? If you have the headers + import lib + DLL at hand, you can just make the folder structure by hand. Alternatively, in the InstallCDT zip file, there are a number of scripts that install libraries. You can try these. (I tried to install libz this way, but for some reason the install script didn’t give me the right import library. I made a bug report; it includes the missing file, if you have this same problem.)
To be continued
I’m afraid this Part 1 must end here. We now have Cocotron fully installed, and an idea of how the system is set up. What we don’t know yet is how to build an actual app for Windows. Clearly that’s going to be the focus for Part 2, then.
You can of course explore ahead on your own. There are sample projects on the Cocotron site. These can be used as a model for setting up your own Windows targets.
Thank you very much for actually reading this far! Don’t hesitate to let me know if you have any comments or problems.