Psellos
Contemporary Development With Functional Programming

Compile OCaml for Early iOS

October 3, 2012

OCamlXARMv6 is a version of OCaml 4.00.0 that cross-compiles to the armv6 architecture under iOS. It runs on OS X, and builds apps for iOS devices that support armv6. Right now, in fact, all iOS devices can run armv6 binaries. However, as of Xcode 4.5, creation of armv6 binaries is no longer supported. This means that OCamlXARMv6 is useful only for special purposes. It works only with older versions of Xcode, but can build apps for the earliest (otherwise obsolete) iOS devices.

If you’re looking for a way to build OCaml apps on current iOS devices, you want OCamlXARM, described in Compile OCaml for iOS. OCamlXARMv6 is the same as OCamlXARM, except that it generates armv6 instructions. I added a small modification to the ARM code generator (written by Benedikt Meurer) to support the VFPv2 floating point instruction set of the early iOS devices. (This modification is available as a separate patch—see OCaml 4.00.0 Patches for VFPv2.)

You can download a prebuilt OCamlXARMv6 3.1 package from Psellos, which installs under /usr/local/ocamlxarm/v6 and builds against the SDK for iOS 5.1 by default.

If you want more control over the installation and the default SDK, you can build OCamlXARMv6 from sources. Below I describe how to download a set of patches to be applied to the official OCaml 4.00.0 release. You can also download sources for OCamlXARMv6 from our public Subversion repository.

I built and tested OCamlXARMv6 under OS X 10.7 (Lion) using the toolchain of Xcode 4.3.3 and the iOS 5.1 SDK. If you build it yourself, it should work with Xcode versions betwen 4.2 and 4.4 (inclusive). If you want to use the prebuilt package with a different version of the iOS SDK, you’ll need to specify the desired iOS SDK as described below under Test.

Preliminaries

To build and run apps with OcamlXARMv6, you need Xcode version 4.2, 4.3, or 4.4, and you need to be registered with Apple as an iOS developer. To get older versions of Xcode, go to Apple’s Developer Downloads page. I was pleasantly surprised to find that many earlier versions of Xcode are still available as of this writing. You’ll also need the associated “Command Line Tools”, which you can download through the Downloads page of Xcode’s Preferences.

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

$ PLT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
$ (cd $PLT/Developer/usr/bin; ls -lL as)
-rwxr-xr-x  1 root  wheel  55632 Jun 12 13:19 as

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

The simplest way to get OCamlXARMv6 is to download and install the previously mentioned prebuilt OCamlXARMv6 3.1.2 package from Psellos. This installs OCamlXARMv6 3.1.2 in /usr/local/ocamlxarm/v6. In that case, you can skip down to the Test section below to verify that your copy of OCamlXARMv6 is installed correctly. The intervening sections describe how to build the OCamlXARMv6 compiler yourself.

If you want to build OCamlXARMv6 from sources, you should have a native C compiler and a C cross compiler for iOS at the following paths:

$ (cd /usr/bin; ls -lL gcc)
-rwxr-xr-x  1 root  wheel  117152 Jun 12 13:27 gcc
$ PLT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
$ (cd $PLT/Developer/usr/bin; ls -lL gcc)
-rwxr-xr-x  1 root  wheel  106064 Jun 12 13:19 gcc

If these compilers aren’t present, make sure you have installed the Command Line Tools as described above.

Create Sources from Patches

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

Download the sources for the OCaml 4.00.0 release from INRIA:

$ curl -O -s http://caml.inria.fr/pub/distrib/ocaml-4.00/ocaml-4.00.0.tar.gz
$ ls -l ocaml-4.00.0.tar.gz
-rw-r--r-- 1 psellos staff 3394054 Sep 22 15:41 ocaml-4.00.0.tar.gz

Download patches from Psellos:

$ curl -O -s http://psellos.com/pub/ocamlxarm/ocamlxarmv6-3.1.2.diff
$ ls -l ocamlxarmv6-3.1.2.diff
-rw-r--r-- 1 psellos staff 71232 Sep 22 15:42 ocamlxarmv6-3.1.2.diff

To save typing, you can access these two links directly from your browser:

Unpack the OCaml sources and apply the patches.

$ tar -xzf ocaml-4.00.0.tar.gz
$ cd ocaml-4.00.0
$ patch -p0 < ../ocamlxarmv6-3.1.2.diff
patching file VERSION
patching file asmcomp/schedgen.ml
patching file asmcomp/arm/arch.ml
patching file asmcomp/arm/emit.mlp
patching file asmcomp/arm/proc.ml
patching file asmcomp/arm/scheduling.ml
patching file asmcomp/arm/selection.ml
patching file tools/make-package-macosx
patching file Makefile
patching file byterun/intern.c
patching file byterun/memory.c
patching file byterun/freelist.c
patching file byterun/freelist.h
patching file byterun/compact.c
patching file byterun/major_gc.c
patching file testsuite/tests/regression/pr5757/pr5757.ml
patching file testsuite/tests/regression/pr5757/pr5757.reference
patching file testsuite/tests/regression/pr5757/Makefile
patching file xarm-build
patching file asmrun/signals_osdep.h
patching file asmrun/arm.S
patching file asmrun/Makefile
$

Check out Sources from Repository

You can also check out the sources for OCamlXARMv6 3.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 OCamlXARMv6 3.1:

$ svn co svn://svn.psellos.com/trunk/ocamlxarmv6/3.1 ocamlxarmv6-3.1
$ cd ocamlxarmv6-3.1

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

Build OCamlXARMv6

Once you have the sources, you’re ready to build OCamlXARMv6. The file xarm-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 PLT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
export SDK=/Developer/SDKs/iPhoneOS5.1.sdk
export SIMPLT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform
export SIMSDK=/Developer/SDKs/iPhoneSimulator5.1.sdk
export XARMTARGET=/usr/local/ocamlxarm
export OSXARCH=i386

The values are as follows:

PLT Location of iOS platform directory (inside the Xcode app)
SDK Desired iOS SDK version (a subdirectory of PLT).
SIMPLT Location of iOS Simulator platform directory (inside the Xcode app).
SIMSDK Desired iOS Simulator SDK version (a subdirectory of SIMPLT—should be the same version number as SDK).
XARMTARGET Where OCamlXARMv6 should be installed.
OSXARCH Architecture for OS X executables (i386 or x86_64).

The value of XARMTARGET is a directory where OCamlXARMv6 should be installed. It is safe to use the same target as for OCamlXARM—OCamlXARMv6 will be installed in a subdirectory of XARMTARGET named v6.

As mentioned above, this location is compiled into the OCamlXARMv6 tools; that is, they are aware of their own installation location. This is convenient, 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, xarm-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 10%.

To build OCamlXARMv6 all in one step:

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

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

ln -s -f ../unix/unix.mli unix.mli
../../ocamlcomp.sh -I ../unix -warn-error A -c unix.ml
../../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 xarm-build.log. It’s also possible to build OCamlXARMv6 in smaller stages—see the script for details.

Now install the cross compiler.

$ sudo -s
Password:
# make install
    . . .
  install /usr/local/ocamlxarm/v6/lib/ocaml/ocamlbuild/ocamlbuild.cmo
  install /usr/local/ocamlxarm/v6/man/man1/ocamlbuild.1
# exit
$

The installation process produces around 280 lines of output.

Test

To verify the installation, compile a test program. For convenience I start by defining the shell variable BIN, used throughout this section.

$ BIN=/usr/local/ocamlxarm/v6/bin
$ cat > hello.ml
let main () = Printf.printf "Hello, world!\n"

let () = main ()
^D
$ $BIN/ocamlopt -o hello hello.ml
$ file hello
hello: Mach-O executable arm

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

$ $BIN/ocamlopt -pp $BIN/camlp4o -o hello hello.ml
$ file hello
hello: Mach-O executable arm

If the compiler produces ARM executable binaries as shown here, it is almost certainly working correctly.

You might instead see the first test fail as follows:

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

This most likely indicates that the iPhoneOS 5.1 SDK isn’t present on your system. If you want to use a different SDK version, the solution is to specify explicit options to ocamlopt telling it which SDK version to use. To use the (purely hypothetical) iPhoneOS 5.2 SDK, for example:

$ PLT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
$ SDK=/Developer/SDKs/iPhoneOS5.2.sdk
$ OCOPTS="-ccopt -isysroot -ccopt $PLT$SDK"
$ $BIN/ocamlopt $OCOPTS -o hello hello.ml
$ file hello
hello: Mach-O executable arm

Further Information

Building the OCaml cross compiler is only the first step. The accompanying notes Portland: Which Way Is Up on iOS? and Slide24: Sliding Tile Puzzle for iOS show how to build simple iOS apps and run them on an iOS device.

If you’re interested in running OCaml apps in the iOS Simulator, see the accompanying note Compile OCaml for iOS Simulator. This note has links to three other sample applications 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, feel free to leave them below, or email me at jeffsco@psellos.com.

Comments

blog comments powered by Disqus