Win-win with Cocotron and Xcode 4.4 — code for Mac, build for Windows (Part 2)

At long last, this is part 2 of my Cocotron tutorial. Sorry to have kept you waiting for so long!

While I was dragging my feet with this tutorial, several milestones were reached in Cocoa-land. Xcode was upgraded to 4.4 and the OS itself was upgraded to 10.8. The instructions herein have been tested to work with these latest versions of the Apple development ecosystem. Hopefully you’ll find that to be the case as well – if not, please post in the comments and let’s try to work it out. (Personally I reached a milestone as well: I’m a father nowadays. We had a healthy and lively baby girl… Who sleeps so well that I’ve been able to find the time for some Cocoa hacking again.)

In Part 1 of this tutorial, we looked into what Cocotron is made of and how it’s installed into Xcode. We managed to build the basic Cocoa frameworks, Foundation and AppKit, for Windows, but didn’t yet create a Windows app that would actually use those libraries.

That will be the focus of this Part 2. We’ll start with a barebones Cocoa app created from the default Xcode template and tweak the magic settings in order to build a Windows app from the same code.

Before getting into that, let’s dwell a while longer in the Cocotron framework projects. AppKit’s configuration options provide us the opportunity to practice something that will eventually become useful in almost any larger project: how to link with existing Windows libraries from third parties.

Configuring AppKit’s dependencies

Like I mentioned in Part 1, Cocotron’s philosophy is one of self-containment – by default, there are no dependencies on external libraries. This is a good policy for a fundamental GUI framework, as both building and deploying apps becomes a lot easier when you don’t need to keep track of extra libraries. In principle, deploying your Cocotron app on Windows is simply a matter of ensuring that the app binary and the Foundation/AppKit DLLs are in the same place. This is tremendously simpler than is the case with apps based on e.g. GTK+, which consists of dozens of libraries.

As usual, practicality sometimes trumps purity. AppKit’s graphics I/O is one such case for many apps. Cocotron is an expansive project, but it doesn’t contain readers and writers for all the image file formats out there. If your app needs to write PNG or JPEG files, you’ll find that the Cocotron AppKit doesn’t have that capability by default.

Luckily these features can be enabled by linking to a couple of open-source projects – the wellknown libjpeg, libpng and zlib (which provides compression capabilities for libpng). The bindings are already written in Cocotron’s AppKit; we just need to enable them in AppKit’s Xcode project and make sure that Cocotron’s cross-compiler can find the libraries.

As was previously mentioned in passing in Part 1, the convention on Cocotron is that external libraries are installed into the platform-specific folder – for Windows targets, that’s /Developer/Cocotron/1.0/Windows/i386. Each library goes in its own folder which contains the headers and import libraries that are required by the C compiler and the linker respectively. In addition to the import library (which tells the linker what symbols the library contains) there is also the DLL which is the library binary which will be required at runtime for Windows to launch our app. (Usually the DLL would be installed alongside the header and import library in a “bin” subfolder, but Xcode doesn’t know about it so the DLL isn’t automatically included when packaging the app for running on Windows – we’ll need to bring the libraries’ DLLs along for the ride when we get to the point of deploying an app on Windows.)

The InstallCDT tools package (Cocotron’s tools installer) includes some scripts that can be used to get and build library dependencies: these files are named install_sqlite.sh, install_libjpeg.sh, and so on. However you don’t really need to use a specific installer to make libraries available to Cocotron: it’s enough to place the library files in the platform folder and add the necessary settings to the Xcode project.

To speed things up, I’ve made a file containing the libjpeg, libpng and zlib development files for Windows: Cocotron_win32_libjpeg_libpng_zlib.zip
You can just unzip the file into the aforementioned Cocotron Windows platform folder, and you’ll be set for the next step.

In the Cocotron AppKit Xcode project, select the “AppKit-Windows-i386” target and open the Build Settings tab. Find the Library Search Path settings and add those three library paths there, as shown in this screenshot:

Do the same for headers – these need to point to the ‘include’ folders instead of ‘lib’:

Also tell the linker that we’re using these libraries by adding their names to the “linker flags” setting:

As you can see from the linker flags above, the naming convention for Windows import libraries is a bit of a mess. The zlib import library is named simply “libz.a”, so the matching linker flag is “-lz”. The libpng library on the other hand is named “libpng14.dll.a” (to tell it apart from statics version of the same library, I guess). Since there’s no uniform convention, you need to check the name of the actual .a file to be sure. (To make things more complicated, some libraries don’t have a GCC-compatible import library with the .a file extension, but only a Microsoft Visual C++ style .lib file. There is a tool available in the Windows GCC utilities that can be used to convert import libraries from the Microsoft format – look into /Developer/Cocotron/1.0/Windows/i386/gcc-4.3.1/bin for the latest available utilities, they should have been installed as part of your cross-compiler.)

If some of the paths are wrong or linker flags are missing, you’ll probably get a cryptic numeric error from Xcode:

In this case, click on the oblong “multi-line” button in the right-hand corner of the Link command to reveal the actual details of the linker problem – usually there’s a list of missing symbols that will reveal which library is missing.

(This tedium of requiring multiple build settings for each library is precisely why NeXT’s operating system invented the concept of frameworks which Mac OS X inherited. Frameworks were never adopted by any other platform but Cocotron’s cross-compiler is modified to understand them, which makes it possible for us to use our own Windows build of Foundation.framework. It’s pretty awesome.)

There’s one more bit of boring configuration. We need to inform the AppKit source files that these libraries are now available. As is typical for C projects, this compile-time configuration happens via a bunch of #define macros. These are easiest to set in the Xcode build settings as follows:

With these macros in place, your imaging-superpowered AppKit is now ready.

The test project and its targets

Now we’ll get into building an actual app. You can download my test app here, but hopefully it should be easy to follow along with these instructions to create your own from scratch.

My test project was created from Xcode’s bog-standard template: File menu > New Project > OS X Application > Cocoa Application. I gave my app the class prefix “CTTest”. You should disable the “Automatic Reference Counting” setting, as this is not supported by the Cocotron’s GCC-based cross-compiler – you’ll need to stick to traditional Cocoa memory management for now. (ARC depends on the new Clang compiler. It’s possible to configure Clang for Cocotron, but this is out of my expertise… As for ARC, I’ve been doing releasing and autoreleasing for so long, I’m not sure whether I could break the habit anymore.)

Xcode creates a couple of files as part of the empty project: there’s the CTTestAppDelegate class and the MainMenu.xib UI definition. I edited MainMenu.xib to add a minimal interface, just one NSTextView and a button:

There are a couple of notable settings in the above Interface Builder screenshot. Firstly, you need to disable “Use Auto Layout” for any project that is intended for Cocotron. Auto-layout is a feature available in OS X Lion and higher, but it’s not yet supported by Cocotron.

In general, the changes you need to make to support Cocotron are similar to those you’d need to make to support an older version of Mac OS X, e.g. 10.4 or 10.5. Interface Builder provides a “Deployment target” setting, and it probably makes sense to set that to 10.5 (like in the above screenshot) as that will allow Xcode to warn if you’re using too recent features in your UI designs.

In the app delegate, I created an action to show an NSAlert dialog and connected the button to that action:

Note the #if directives. Those underscore-laden macros are always available, so you can use them to implement separate code paths for different platforms. (Be warned: programming with #ifdefs quickly gets out of hand and makes program flow impossible to follow. If you have more than a few lines of platform-specific code, you should spend a moment on a design that will allow you to cleanly separate those parts into separate files and perhaps separate classes with a unified interface. Cocotron’s Foundation and AppKit projects provide some tremendous practical examples of how you might go about that in Objective-C.)

With the window and the simple NSAlert-displaying action in place, my humble test app is now ready to run in its native Mac habitat:

 

Now let’s port it to Windows. Start by duplicating the target:

The new target can be renamed to something like CocotronTestApp-Win32 or whatever you prefer.

Xcode created a new build scheme for this target, but it’s not renamed automatically (at least on my system). Click on the Scheme list button in the project’s toolbar and choose Manage Schemes to edit the scheme’s name.

While there, you should also edit the scheme to set the build configuration to “Release” – managing separate Debug builds for Windows is too much of a pain for too little gain…

Configuring the Windows target

Now we come into the gritty details of forcing Xcode to spit out a Windows-compatible .exe file by customizing the newly created target.

Like a traditional makefile, an Xcode target is basically a command sequence that processes input files using a number of build tools. A target’s Build Rules define which tools build which source files; Build Settings define the settings passed to those build tools; and Build Phases defines additional operations that need to be done as part of the build. We’ll be touching on all these, but let’s start with the rules.

In the Build Rules tab for the Windows target, click on Add Build Phase and configure the newly created build phase like this:

Here we are instructing Xcode to use Cocotron’s cross-compiler for all C files in this project. This setting will also apply to Objective-C and C++ files unless otherwise specified.

Moving on to Build Settings… I’ll show the necessary settings changes in screenshots along with commentary as needed. The bolded settings in screenshots are the ones that have been modified from the Mac target.

There are quite a few settings here, but fortunately we only need to do this setup dance once per app. Many of these settings are simply compiler features that happen to be unsupported by GCC on Windows.

Setting the SDK to “Current OS X” allows the Frameworks path to be correctly overriden to point into Cocotron’s folder.

“User32” and “gdi32” are fundamental Win32 UI libraries. They’re not really needed right now, but you’ll need probably them as soon as you try to use some platform-specific features from <windows.h>, so I figured I would include them here.

These search paths are needed for the magic that makes Xcode link to our Windows frameworks.

These two user-defined settings determine where the Windows executable file goes. As you probably know, a Mac .app file is actually a bundle, that is, a folder containing not just the OS X executable but also other files like resources. By specifying the EXECUTABLE_FOLDER_PATH setting, we’ll tell Xcode to place our Windows build into Contents/Windows within the bundle, mirroring the location of the OS X binary in Contents/MacOS.

Next we need to tweak the target’s linked frameworks a bit. By default the new Cocoa app is linked only to Cocoa.framework. This is a slightly strange framework because it doesn’t contain any code itself – it’s just a linking convenience, an “umbrella framework” that covers Foundation, AppKit and CoreData. For Cocotron, it makes sense to link directly to Foundation and AppKit instead of Cocoa.framework. This doesn’t make a difference for OS X, so you can do it for all targets.

The individual frameworks are already available in the project’s “Other Frameworks” folder. You can just remove Cocoa.framework from the targets and add AppKit and Foundation in its place:

Retro flavors

At this point, the Windows target is theoretically ready to be built. However if you try it you will get a scattering of errors about property access and missing methods. The problem is that Xcode 4.4’s default project template makes use of some Objective-C compiler features that are very recent and have only been implemented in Apple’s Clang compiler. Since Cocotron is still on GCC, we need to roll back in time to a slightly older flavor of Cocoa. The changes are minimal – it’s not about functionality, just slightly more “boilerplate” code.

The first change is to CTTestAppDelegate.h and highlighted below. The original version of this class header did not include any {} brackets and instance variables because Clang is able to synthesize the variable based on the property definition. Older Objective-C compilers (up to about 2011) didn’t have this capability, and so we have to write to the old style.

In addition to the instance variable in the header, we also need to add a matching @synthesize declaration to the class implementation in CTTestAppDelegate.m:

Another change I did was to take out the NSAppDelegate protocol declaration from CTTestAppDelegate’s header. These delegate protocols were added to Cocoa in OS X Lion; before that they were just implicit protocols (basically methods defined as categories on NSObject). Older Objective-C compilers don’t understand optional methods in protocol headers, and so declaring the protocol as part of the class interface produces a lot of warnings about missing method implementations. Since Objective-C messages are always bound at runtime anyway, it makes no difference in practice whether our app delegate implements the NSAppDelegate protocol or not.

Bundling up

The app should now build successfully, producing something that looks like a Mac .app but actually contains a Windows .exe within the app bundle in the folder Contents/Windows.

To actually run it on Windows, there’s one more step: we need to provide the DLLs that the executable requires. The easiest way to do this is simply to copy the DLLs into the same folder where the .exe file is located.

Cocotron includes a utility that can do this processing for frameworks. It’s called retargetBundle and can be added as a new “Run Script” build step to the target. We just need to tell the tool which frameworks need to be copied:

With these settings, the Foundation and AppKit DLLs will be placed into the app bundle’s Contents/Windows folders, along with any resources used by these frameworks (e.g. icons used by AppKit).

(By the way, retargetBundle seems to be a bit buggy sometimes. It can fail to copy the frameworks unless I do a clean build first. I think this can be resolved simply by adding a line to the script that removes the DLLs and frameworks within the bundle before retargetBundle is run… If you find that retargetBundle is not working, you can of course replace it either by copying the files manually or by writing a script to copy the files.)

Here is what our app bundle’s Windows folder looks like when copied over to a Windows 7 system:

Well – you may note something here that I didn’t mention yet. In addition to AppKit and Foundation DLLs, there are three DLLs for the libjpeg/libpng/zlib libraries that we previously linked into AppKit. You’ll need to copy these DLLs manually as retargetBundle only supports frameworks. (“No big deal”, as Steve Jobs used to say…)

Here is the app in its full glory on Windows:

Future of Cocotron

Despite that pompous heading, I can’t really claim to know anything useful about Cocotron’s future. At its heart The Cocotron is a practical project driven by the porting needs of a few individuals. There is no ideological group that would be in charge of deciding where Cocotron needs to be; instead interesting stuff just happens along the way as people work on their apps.

That means that if you want to see Cocotron moving forward, there is no room for politics – the onus is on the individual to get her hands dirty and code up something. There’s plenty of work to be done, for sure. During this tutorial we already encountered multiple cases of missing platform pieces as the evolution of Mac OS X has speeded along in recent years.

Getting the Clang compiler included in Cocotron would be a major step forward. As far as my limited understanding goes, a lot of work has been done for that already and Clang seems to work for some people – perhaps it’s just a matter of getting it packaged neatly into the standard Cocotron installer? Any work done towards that would certainly get you my great respect.

Personally I’m somewhat scared to tackle the compiler and tool chain at such a deep level, but there’s a lot that can be done in Cocotron on the higher levels of the stack. Even small fixes to AppKit can have a huge influence on how the ported apps look and feel on other platforms. That would be a great place for anyone to start improving Cocotron.

For example, in the above screenshot of the NSAlert dialog box, the ‘Ok’ button’s text is not correctly centered and the whole box seems a bit “off”. Fixing these issues is really as simple as opening the AppKit project in Cocotron and hacking away. Leaving your mark in AppKit’s implementation is something that is obviously impossible in a regular old Mac Cocoa project… And that’s why I find Cocotron to be so much fun for an old Cocoa-hand.

This entry was posted in Mac-related, Programming. Bookmark the permalink.

11 Responses to Win-win with Cocotron and Xcode 4.4 — code for Mac, build for Windows (Part 2)

  1. Thanks! This was an outstanding tutorial!!!!

  2. Jiaren Wu says:

    Hi Pauli,

    Thank you for this excellent tutorial, I’ve been googling for ages! It helped to understand cocotron.

    However, I just couldn’t install the cocotron CDT, every time when I went to the CDT folder, and enter
    $sudo ./install.h

    I always get this error message,

    checking for C compiler default output file name…
    configure: error: in `/Developer/Cocotron/1.0/build/Windows/i386/binutils-2.21-20111025′:
    configure: error: C compiler cannot create executables
    See `config.log’ for more details.

    I’ve done everything without luck!

    Please help me.

    Thanks in advance.

    Regards,

    Jiaren

    • pauli says:

      Hi, sorry for taking so long to reply to your comment.

      Are you on Mountain Lion? The HPC GCC download that’s included in my tutorial is perhaps not compatible with OS X 10.8, as it was not out yet when I wrote this tutorial.

      There is a Mountain Lion specific GCC on the HPC project’s web site. You could try if that works. (I’ll try to update my HPC GCC installer when I have the time.)

  3. Aaron Schubert says:

    Hi!
    Thanks for this awesome tutorial, I just encountered a small problem towards the end after finishing the “Retro Flavors” section and hitting “run” Xcode comes up with loads of errors mainly complaining that Cocoa.h doesn’t exist and that there is no such file or directory and that basically means the whole project is full of errors.

    Would you know how I could fix this? Or could I get a copy of your project (There is no link at the top)

    Kind Regards,

    Aaron

    • pauli says:

      Hi Aaron,

      that sounds like I’ve missed a step somewhere… Do you have Cocoa.framework in /Developer/Cocotron/1.0/Windows/i386/Frameworks?

      If not, then that’s the problem. Although the app doesn’t need to link against Cocoa.framework (since it’s just an shim for Foundation and AppKit), the Cocoa.h header file needs to be available.

      You can build Cocoa.framework the same way that Cocotron’s Foundation and AppKit were built: just open the Xcode project in cocotron/Cocoa and run Build.

      • Aaron Schubert says:

        Hi Pauli,
        Yep that was the problem, thanks so much!

        Also just for completeness sake, in the Build Settings under “Debug Information Format” for Release I had to change it from “DWARF with dSYM File” to just” DWARF” for it to build.

        Thanks again,
        Aaron

  4. Atsushi says:

    Hi Pauli,
    Thanks for the great tutorials.
    I just went through but stuck at the very last step – building the example app for windows.
    The first error was “GL/gl.h” not found.
    I added “/Developer/Cocotron/1.0/Windows/i386/gcc-4.3.1/i386-mingw32msvc/include” for the HEADER_SEARCH_PATH. and got rid of the error.
    But now “Unknown type name ‘__MINGW_IMPORT’ at the line “__MINGW_IMPORT char *tzname[2];” in the time.h.
    The time.h is referenced from Foundation.h in the cocotron repository’s directory, ../system/i386-mingw32msvc/Frameworks/Foundation.framework/Headers/Foundation.h

    If I could get help on this, it would be appreciated.

    By the way, I found some different situations while going through this tutorial (part 1 & 2).
    In my system (OS X 10.7.5, Xcode 4.3.3, Cocotron 1.0), building Foundation, AppKit and Cocoa doesn’t create the Framework under /Developer/Cocotron/1.0/Windows/i386 but I found them in ~/Documents/cocotron/system/i386-mingw32msvc, so I specified this path to the FRAMEWORK_SEARCH_PATH in the build settings. This seems working OK to me but maybe something had been already wrong.

    Thanks in advance for your help.
    Best regards,
    Atsushi

  5. Manuel Gebele says:

    Just a note to all people who have trouble installing Cocotron Developer Tools.
    Instead of using HPC’s GCC version you should use brew to install Apple’s
    GCC-4.2. That ensures that you always get a version which is compatible to your
    underlying OS X Version (Mountain Lion, Maverics etc.) See Jiaren Wu’s question above…
    Here are the steps:

    $ brew install apple-gcc42 && cd /usr/bin && sudo mv gcc gcc-backup && sudo ln -s /usr/local/Cellar/apple-gcc42/4.2.1-5666.3/bin/gcc-4.2 gcc

    Hope that helps!

    • pauli says:

      Thanks for the note Manuel!

      I really should update this tutorial for Mavericks + Xcode 5. As soon as I have a couple of hours…

      • Manuel Gebele says:

        That would be great. I also made a Xcode 5 Compiler Plugin to force Xcode to use i386-mingw32msvc-gcc. Just let me know if you need it!

  6. Manuel Gebele says:

    BTW – Thanks for this great Tutorial :))

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>