Psellos
Contemporary Development With Functional Programming

Compile OCaml for iOS Simulator

September 7, 2014

OCamlXSim is a version of OCaml that compiles apps for the iOS Simulator, an iPhone and iPad simulator available from Apple as part of Xcode. The current version of OCamlXSim is 4.0, based on OCaml 4.01.0.

If you’re not familiar with OCaml, it’s a functional language in the same family as Haskell, Scala, and (especially) Standard ML. It’s powerful, flexible, and rigorous. I’ve found programming in OCaml for iOS to be productive and even delightful.

You can download a prebuilt OCamlXSim package for Mavericks (OS X 10.9) from this link:

The package installs OCamlXSim under /usr/local/ocamlxsim. It was built under Mavericks using Xcode 5.1.1, and builds against the iOS 7.1 SDK by default. You can use the compiler with a different SDK version by specifying the proper -ccopt flags, explained just below under Test.

If you want more control over the installation and the default SDK, you can build OCamlXSim from sources as described at the end of the page under Building from Sources.

The OCamlXSim compiler can be used to develop real-world iOS apps. I did almost all the development of the GUI for our released app Master Schnapsen/66 in the iOS Simulator. (The Schnapsen game engine was developed under Linux.)

 

Our OCaml Programming page has more resources for doing OCaml programming for iOS. For example, I’ve released sources for five illustrative apps (shown in miniature at left) that you can compile and run on an iOS device or in the iOS Simulator.

Previous versions of OCamlXSim, for earlier versions of OS X, iOS, and Xcode, can be found in the OCaml Programming Archives.

Test

After installing, you might want to compile a tiny test program as follows. For convenience I start by defining the variable BIN, used throughout this section.

$ BIN=/usr/local/ocamlxsim/bin
$ cat > statdo.ml
let main () =
    Printf.printf "%o\n" Unix.((stat "/bin/sh").st_perm)

let () = main ()
^D
$ $BIN/ocamlopt -o statdo unix.cmxa statdo.ml
$ file statdo
statdo: Mach-O executable i386

If your Simulator program is simple enough, you can actually run it from the OS X command line by controlling the set of dynamically loaded libraries. First, set shell variables PLT and SDK. You can cut and paste the definitions from the xsim-build script.

$ HIDEOUT=/Applications/Xcode.app/Contents/Developer
$ PLT=$HIDEOUT/Platforms/iPhoneSimulator.platform
$ SDK=/Developer/SDKs/iPhoneSimulator7.1.sdk

Set DYLD_ROOT_PATH to the SDK location, and run the program.

$ DYLD_ROOT_PATH=$PLT$SDK statdo
555

The program correctly shows the mode (permission settings) of the standard shell /bin/sh. If you run the program in the usual OS X environment, you’ll see that it behaves differently:

$ statdo
5555

In the OS X environment, the program runs but gets the wrong answer! The iOS Simulator environment is a copy of the environment of an iOS device, and thus is not exactly the same as OS X.

To test the camlp4 family of processors, try compiling with camlp4o.

$ $BIN/ocamlopt -pp $BIN/camlp4o -o statdo unix.cmxa statdo.ml
$ file statdo
statdo: Mach-O executable i386

If all these tests behave as shown here, you can be pretty sure your OCamlXSim installation is working correctly.

You might instead see the first test above fail as follows:

$ $BIN/ocamlopt -o statdo unix.cmxa statdo.ml
ld: library not found for -lcrt1.10.9.o
collect2: ld returned 1 exit status
File "caml_startup", line 1:
Error: Error during linking

This most likely indicates that OCamlXSim was built using an iOS SDK version that is not currently present on your system. This doesn’t necessarily indicate a problem. For example, the prebuilt OCamlXSim 4.0.1 compiler from Psellos is built using the iPhoneOS 7.1 SDK, but you may well be using a later Xcode version with a later SDK.

The solution is to specify explicit options to ocamlopt telling it which SDK version to use. Assuming you’ve set PLT and BIN as above, you can use the pre-built OCamlXSim 4.0.1 with iPhoneOS Simulator 8.0 SDK as follows:

$ SDK=/Developer/SDKs/iPhoneSimulator8.0.sdk
$ OCOPTS="-ccopt -isysroot -ccopt $PLT$SDK"
$ $BIN/ocamlopt $OCOPTS -o statdo unix.cmxa statdo.ml
$ file statdo
statdo: Mach-O executable arm

Although the SDK options are somewhat painful to specify, it’s probably best to think of them as part of using OCamlXSim with Apple’s ever-evolving iOS SDK. A solution that may be better in some cases is to rebuild OCamlXSim from sources, specifying the new SDK values in xsim-build.

Further Information

To run a program that actually does something interesting, you need the richer environment provided by the actual iOS Simulator. The pages linked here show how to build three different OCaml example apps and run them in the iOS Simulator:

Gamut: Explore Colors in iOS Simulator
Voronoi: Touchable Diagrams in iOS Simulator
IcosaBlue: OpenGL ES App for iOS

Gamut is a simple app that displays a small animation in all possible colors. Voronoi is a more complex app that displays dynamic colored Voronoi diagrams that you can manipulate through the touch interface. IcosaBlue shows how to use OpenGL ES—it is packaged to work both in the simulator and on real iOS devices.

If you’re interested in running OCaml apps on real iOS devices, see the accompanying note Compile OCaml for iOS. This note has links to two other sample apps that you can try.

See the OCaml Programming page for a full list of our OCaml resources.

If you have any questions, comments, or corrections, please leave them below, or email me at jeffsco@psellos.com.

Appendix: Building from Sources

For maximum flexibility, you can build OCamlXSim from sources. This is a little more complicated than a standard OCaml build, but I’ve created a shell script that does it for you.

An app running natively on an iOS device is an ARM program. However, an app running in the iOS Simulator runs as a 32-bit (i386 architecture) Mac OS X program. In other words, the Simulator doesn’t simulate an iOS device down to the hardware level. It provides a faithful copy of the iOS environment, reimplemented to run natively on the Mac.

This means that OCamlXSim needs to be a 32-bit compiler for the i386 architecture, much like the stock 32-bit OCaml compiler for OS X. In fact, the stock compiler very nearly works. The difficulty is that the stock OCaml compiler is a native compiler, i.e., it runs in the same environment as the programs that it compiles. The Simulator environment is different enough from the standard OS X environment that a native compiler doesn’t work correctly. So OCamlXSim needs to be built as a cross compiler. (The simple test statdo, above, shows the basic problem. I’ve written a blog post iOS Simulator Vs. OS X that discusses the issue a little more.)

The standard OCaml release isn’t designed to run as a cross compiler, so it takes a little extra work to build it as one. In essence, the OCaml build system doesn’t ordinarily need to distinguish between generating parts of the compiler itself, which we run on OS X, and generating the runtime, which we want to run in the iOS Simulator.

The trick I’ve used in OCamlXSim is to build two distinct copies of the OCaml runtime. The first copy is targeted at OS X, and powers the cross compiler itself. The second copy is targeted at the iOS Simulator, and powers the apps running in the iOS Simulator. By lucky chance, there are no parts of the runtime that absolutely need to work independently in both environments. As a result, the two runtimes can coexist inside a single OCaml release. (This would not be the case if, for example, I wanted to support the bytecode interpreter in Simulator apps. For now, I’m sticking with native code apps.)

Please note that a few of the command lines of the following discussion are too long to fit on a single line of a typical browser window. In a lot of cases there is no good place to split them into smaller lines, usually because of a long filename or URL. Take care that you enter them as a single line if you are typing them in by hand.

Preliminaries

To develop and run code on the iOS Simulator, you need an installation of Apple’s Xcode programming environment, which contains the iOS Simulator as one part. You can download Xcode (for free) from the Mac App Store. See Apple’s Xcode page for more details. Recent versions of Xcode contain the traditional Unix command-line tools, which you will also need to build OCamlXSim. (In earlier versions of Xcode, the command-line tools were downloaded separately.)

To simplify the command lines below, I’ll define some shell variables as abbreviations for some of the key paths of development tools. If you’re following along at the keyboard, you should define them yourself for later use:

$ HIDEOUT=/Applications/Xcode.app/Contents/Developer
$ TOOLDIR=$HIDEOUT/Toolchains/Xcodedefault.xctoolchain/usr/bin
$ PLT=$HIDEOUT/Platforms/iPhoneSimulator.platform

The OCaml native code compiler (ocamlopt) uses an external assembler to produce its final object files. In the same way, OCamlXSim uses the assembler of the iOS Simulator platform to produce object files. After you’ve installed Xcode, you should find an assembler for the iOS Simulator platform:

$ (cd $TOOLDIR; ls -l as)
-rwxr-xr-x 1 root wheel 27808 Mar 11 04:37 as

This is the assembler OCamlXSim will use, so it must be present. If it isn’t, check that you’ve installed Xcode (version 5.1 or later). If you’ve installed it in a non-standard location (other than /Applications), you’ll need to modify paths accordingly in the following discussion.

In recent years, Apple has switched from the GNU C compiler gcc to a new compiler clang, based on LLVM. If you want to build OCamlXSim from sources, you should have a native C compiler and a C cross compiler for the Simulator as follows:

$ (cd /usr/bin; ls -l clang)
-rwxr-xr-x 1 root wheel 14224 Oct 30 2013 clang
$ (cd $TOOLDIR; ls -l clang)
-rwxr-xr-x 1 root wheel 36874304 May 9 21:09 clang

If these compilers aren’t present, make sure you have installed Xcode as described above, and that your version of Xcode includes the command-line tools.

Create Sources from Patches

To create sources from the patches, you need to download the OCaml 4.01.0 release from INRIA and the patches from Psellos and then apply the patches.

Download the sources for the OCaml 4.01.0 release from INRIA:

$ curl -O -s http://caml.inria.fr/pub/distrib/ocaml-4.01/ocaml-4.01.0.tar.gz
$ ls -l ocaml-4.01.0.tar.gz
-rw-r--r-- 1 psellos staff 4397871 Sep 7 23:05 ocaml-4.01.0.tar.gz

Then download patches from Psellos:

$ curl -O -s http://psellos.com/pub/ocamlxsim/ocamlxsim-4.0.1.diff
$ ls -l ocamlxsim-4.0.1.diff
-rw-r--r-- 1 psellos staff 13711 Sep 7 21:09 ocamlxsim-4.0.1.diff

To save typing, you can download directly from your browser:

Unpack the OCaml sources and apply the patches.

$ tar -xf ocaml-4.01.0.tar.gz
$ cd ocaml-4.01.0
$ patch -p0 < ../ocamlxsim-4.0.1.diff
patching file configure
patching file VERSION
patching file asmcomp/i386/emit.mlp
patching file tools/make-package-macosx
patching file xsim-build
patching file Makefile
patching file asmrun/signals_osdep.h

Check Out Sources from Repository

You can also check out the sources for OCamlXSim 4.0.1 from Psellos’s public Subversion repository. This is simpler, but it doesn’t give you the opportunity to examine the patches separately before applying them.

Check out sources for OCamlXSim 4.0.1:

$ svn co svn://svn.psellos.com/tags/ocamlxsim-4.0.1 ocamlxsim-4.0
$ cd ocamlxsim-4.0

These sources are identical to what you get if you apply the 4.0.1 patches to the INRIA 4.01.0 release, as above.

Build OCamlXSim

Once you have the sources, you’re ready to build OCamlXSim. The file xsim-build is a shell script that does the building. You may want to modify the script before running it. At the beginning of the script are the following lines:

export HIDEOUT=/Applications/Xcode.app/Contents/Developer
export TOOLDIR=$HIDEOUT/Toolchains/XcodeDefault.xctoolchain/usr/bin
export PLT=$HIDEOUT/Platforms/iPhoneSimulator.platform
export SDK=/Developer/SDKs/iPhoneSimulator7.1.sdk
export XSIMTARGET=/usr/local/ocamlxsim
export OSXARCH=i386

The meanings are as follows:

HIDEOUT Secret hideout of Apple developer files (inside the Xcode app).
TOOLDIR Location of developer tools.
PLT Location of iOS Simulator platform directory.
SDK Desired iOS Simulator SDK version (a subdirectory of PLT—normally, the most recent available).
XSIMTARGET Where OCamlXSim should be installed.
OSXARCH Architecture for OS X executables (i386 or x86_64).

The value of XSIMTARGET is a directory where OCamlXSim should be installed. This location is compiled into the OCamlXSim tools; that is, they are aware of their own installation location. This is convenient for finding libraries and such, but it also means that the tools actually need to be installed in this one specific place. It’s not possible (or at least not convenient at all) to move them somewhere else later on.

Ordinarily, xsim-build will create 32-bit (i386 architecture) OS X executables. This is simplest, because they work on all Intel Macs. If you want to create 64-bit executables, change the value of OSXARCH to x86_64. In my initial testing, I’ve found that the 64-bit executables make compiles go a little faster—but only around 9%.

To build OCamlXSim all in one step:

$ sh xsim-build all > xsim-build.log 2>&1

If things go well, xsim-build.log will contain around 2424 lines of output, ending with something like this:

../../boot/ocamlrun ../../tools/ocamlmklib -ocamlc '../../ocamlcomp.sh -I ../unix' -o unix -linkall unix.cmo ../unix/unixLabels.cmo
make: Nothing to be done for `allopt'.

If there is a problem, it may be possible to figure out what went wrong by looking at the error messages in xsim-build.log. It’s also possible to build OCamlXSim in smaller stages—see the script for details.

Now install the cross compiler.

# sudo make install
Password:
    . . .
  install /usr/local/ocamlxsim/lib/ocaml/ocamlbuild/ocamlbuild.o
  install /usr/local/ocamlxsim/man/man1/ocamlbuild.1
$

The installation process produces around 278 lines of output.

Now you’re ready to test the compiler as described above under Test.

Comments

blog comments powered by Disqus