Compile OCaml for iOS (iPhone, iPad)
Note: This is an archived version of the OCamlXARM page, for those interested in earlier versions. The most recent version is at Compile OCaml for iOS.
OCamlXARM is a version of OCaml that cross-compiles to iOS devices. It runs on OS X, and builds apps for the iPhone, iPad, and iPod touch. The current version of OCamlXARM 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, efficient, and rigorous. I’ve found programming in OCaml for iOS to be productive and even delightful.
You can download an OCamlXARM package for Mavericks (OS X 10.9) or later from this link:
The package installs OCamlXARM under /usr/local/ocamlxarm
. It was
built under Mavericks using Xcode 5.1.1. The compiler builds against the
iOS 7.1 SDK by default, but you can build apps for a later iOS revision
using a small script ocamloptrev
that I give below.
If you want more control over the installation and the default SDK, you can build OCamlXARM from sources as described at the end of this page under Building from Sources.
To a large extent, OCamlXARM is a modification of the OCaml ARM code generator written by Benedikt Meurer. I modified the compiler to do cross compiling from the Mac to iOS, and modified Meurer’s code generator for the iOS toolchain and ABI.
The ARM architectures supported by OCamlXARM are dictated by those supported by the OCaml ARM code generator. OCamlXARM 4.0 builds apps for the arm7 architecture, which can run on all iOS devices introduced since September 2009. Support for arm64 should be available in the next major release (OCamlXARM 5.0), as support for 64-bit ARM is being added in OCaml 4.02.0.
The OCamlXARM compiler can be used to produce real-world iOS apps. At Psellos we used OCamlXARM to build our commercial iOS apps, Master Schnapsen/66 and Cassino, and it has been used to build iOS apps by other developers as well. For one example, see SEAiq, a suite of iPad apps for marine navigation (in current use on several of the seven seas).
Our OCaml Programming page has a short pitch of the strengths of OCaml, and more resources for doing OCaml programming for iOS. For example, I’ve released sources for five example apps (shown in miniature at left) that you can compile and run on an iOS device or in the iOS Simulator.
Previous versions of OCamlXARM, for earlier versions of OS X and Xcode, can be found in the OCaml Programming Archives.
Compile for iOS 8.1
To compile for iOS 8.1 (or any recent revision), you can use this script
(ocamloptrev
):
#!/bin/bash
#
# ocamloptrev ocamlopt for specified iOS revision
#
USAGE='ocamloptrev -rev M.N other-ocamlopt-options ...'
OCAMLDIR=/usr/local/ocamlxarm/v7
REV=''
declare -a ARGS
while [ $# -gt 0 ] ; do
case $1 in
-rev)
if [ $# -gt 1 ]; then
REV=$2
shift 2
else
echo "$USAGE" >&2
exit 1
fi
;;
*) ARGS[${#ARGS[*]}]="$1"
shift 1
;;
esac
done
if [ "$REV" = "" ]; then
echo "$USAGE" >&2
exit 1
fi
HIDEOUT=/Applications/Xcode.app/Contents/Developer
PLT=$HIDEOUT/Platforms/iPhoneOS.platform
SDK=/Developer/SDKs/iPhoneOS${REV}.sdk
OCAMLC=$OCAMLDIR/bin/ocamlopt
$OCAMLC -ccopt -isysroot -ccopt "$PLT$SDK" "${ARGS[@]}"
To use the script, copy/paste from this page or download from this link:
Supply the desired iOS revision as -rev M.N
. The script uses the
-ccopt
flag of ocamlopt
to set the name of the desired iOS SDK.
Test
After installing, you might want to compile a tiny test program using
the ocamloptrev
script. Here’s how to compile for iOS 8.1:
$ cat > hello.ml
let main () = Printf.printf "Hello, world!\n"
let () = main ()
^D
$ ocamloptrev -rev 8.1 -ccopt -Wl,-no_pie -o hello hello.ml
$ file hello
hello: Mach-O executable arm
If the compiler produces an ARM executable binary as shown here, it is almost certainly working correctly.
You might instead see the test fail as follows:
$ ocamloptrev -rev 7.1 -ccopt -Wl,-no_pie -o hello hello.ml
clang: warning: no such sysroot directory: '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk'
ld: library not found for -lSystem
clang: error: linker command failed with exit code 1 (use -v to see invocation)
File "caml_startup", line 1:
Error: Error during linking
This most likely indicates that you’re asking for a revision of iOS for which the SDK isn’t installed on your system.
Further Information
Installing 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.
I’ve also put together a compiler named OCamlXSim that can be used to compile OCaml apps and run them in the iOS Simulator. This is a simpler way to get started, as it doesn’t require an iOS device or registration with Apple. OCamlXSim is described in Compile OCaml for iOS Simulator. In fact the Simulator is invaluable while developing for actual iOS devices—it provides quicker turnarounds in a self-contained environment (with no need for an extra test device).
If you have any questions, comments, or corrections, feel free to leave them below, or email me at jeffsco@psellos.com.
Appendix: Building from Sources
For maximum flexibility, you can build OCamlXARM 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.
It’s complicated because the standard OCaml release is not designed to run as a cross compiler. In essence, the OCaml build system doesn’t ordinarily need to distinguish between generating the compiler itself, which we want to run on the Mac, and generating the runtime, which needs to run on iOS.
The trick I’ve used in OCamlXARM 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 iOS, and powers the iOS apps. By lucky chance, there are no parts of the runtime that absolutely need to work 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 iOS apps. For now, I’m sticking with native code apps.)
A second trick, which isn’t strictly necessary, is that I use the iOS Simulator execution environment (though not the actual simulator itself) to create the initial configuration settings for building the iOS runtime. The Simulator embodies a reasonably faithful copy of the properties of iOS (available system calls, sizes of integers, endianness, and so on), so it works as an environment for setting up most of the configuration automatically.
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 iOS devices, you need an installation of Apple’s Xcode programming tools and you need to register with Apple as an iOS developer. The Xcode tools are free, but there is a yearly fee to register as an iOS developer. If you want to get started without paying the fee, you can develop for the iOS Simulator using OCamlXSim, described in Compile OCaml for iOS Simulator. To register as an iOS developer, see Apple’s iOS Developer Program page.
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 OCamlXARM. (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/iPhoneOS.platform
The OCaml native code compiler (ocamlopt) uses an external assembler to produce its final object files. In the same way, OCamlXARM’s ocamlopt 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:
$ (cd $TOOLDIR; ls -l as)
-rwxr-xr-x 1 root wheel 27808 Mar 11 04:37 as
This is the assembler OCamlXARM 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 the shell variables accordingly.
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 OCamlXARM from
sources, you should have a native C compiler and a C cross compiler for
iOS 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 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 jeffsco staff 4397871 Aug 24 10:18 ocaml-4.01.0.tar.gz
Download patches from Psellos:
$ curl -O -s http://psellos.com/pub/ocamlxarm/ocamlxarm-4.0.2.diff
$ ls -l ocamlxarm-4.0.2.diff
-rw-r--r-- 1 jeffsco staff 55018 Aug 24 10:19 ocamlxarm-4.0.2.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 < ../ocamlxarm-4.0.2.diff
patching file configure
patching file VERSION
patching file asmcomp/arm/arch.ml
patching file asmcomp/arm/emit.mlp
patching file asmcomp/arm/proc.ml
patching file asmcomp/arm/selection.ml
patching file asmcomp/interf.ml
patching file tools/make-package-macosx
patching file 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 OCamlXARM 4.0.2 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 OCamlXARM 4.0.2:
$ svn co svn://svn.psellos.com/tags/ocamlxarm-4.0.2 ocamlxarm-4.0
$ cd ocamlxarm-4.0
These sources are identical to what you get if you apply the 4.0.2 patches to the INRIA 4.01.0 release, as above.
Build OCamlXARM
Once you have the sources, you’re ready to build OCamlXARM. 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 HIDEOUT=/Applications/Xcode.app/Contents/Developer
export TOOLDIR=$HIDEOUT/Toolchains/XcodeDefault.xctoolchain/usr/bin
export PLT=$HIDEOUT/Platforms/iPhoneOS.platform
export SDK=/Developer/SDKs/iPhoneOS7.1.sdk
export SIMPLT=$HIDEOUT/Platforms/iPhoneSimulator.platform
export SIMSDK=/Developer/SDKs/iPhoneSimulator7.1.sdk
export XARMTARGET=/usr/local/ocamlxarm
export OSXARCH=i386
The values are as follows:
HIDEOUT |
Secret hideout of Apple developer files (inside the Xcode app). |
TOOLDIR |
Location of developer tools. |
PLT |
Location of iOS platform directory. |
SDK |
Desired iOS SDK version (a subdirectory of PLT —normally, the most recent available). |
SIMPLT |
Location of iOS Simulator platform directory. |
SIMSDK |
Desired iOS Simulator SDK version (a subdirectory of SIMPLT —should be the same version number as SDK ). |
XARMTARGET |
Where OCamlXARM should be installed. |
OSXARCH |
Architecture for OS X executables (i386 or x86_64 ). |
The value of XARMTARGET
is a directory where OCamlXARM should be
installed. This location is compiled into the OCamlXARM 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.
OCamlXARM 4.0 will be installed in a subdirectory of XARMTARGET
named
v7
. Future releases will create more subdirectories for other ARM
architectures as necessary.
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 OCamlXARM all in one step:
$ sh xarm-build all > xarm-build.log 2>&1
If things go well, xarm-build.log
will contain around 2,840 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 xarm-build.log
. It’s also
possible to build OCamlXARM in smaller stages—see the script for
details.
Now install the cross compiler.
$ sudo make install
Password:
. . .
install /usr/local/ocamlxarm/v7/lib/ocaml/ocamlbuild/ocamlbuild.cmo
install /usr/local/ocamlxarm/v7/man/man1/ocamlbuild.1
$
The installation process produces around 285 lines of output.
Now you’re ready to test the compiler as described above under Test.