Compile OCaml for iOS (iPhone, iPad)
For many years I’ve been maintaining a set of patches that transform the OCaml compiler into a cross compiler for iOS devices. Recently there has been some work (with the kind help of Gerd Stolpmann) to improve these changes and incorporate them into the official INRIA OCaml release.
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.
I will definitely plan some kind of celebration for when iOS support makes it into the official OCaml release. In the meantime, the modifications are available as a branch in the OCaml GitHub repository. I’ve checked out the branch and built cross compilers for the 32-bit and 64-bit versions of iOS.
You can download OCamliOS compiler packages from these links:
- OCamliOS 4.02.3 for 32-bit iOS (updated Jan 23, 2016)
- OCamliOS 4.02.3 for 64-bit iOS (updated Jan 23, 2016)
These are builds of OCaml 4.02.3 that run under OS X 10.11 (El Capitan)
and generate code to run under iOS versions 7.0 and later (tested under
iOS 9.2). The packages install OCamliOS under /usr/local/ocamlios32
and /usr/local/ocamlios64
. They were built under El Capitan using
Xcode 7.2.
If you want more control over the installation and iOS versions, you can build OCamliOS from sources as described at the end of this page under Building from Sources.
The OCamliOS compiler can be used to produce real-world iOS apps. At Psellos we used earlier versions of OCamliOS 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 OCaml compilers for iOS, for earlier versions of OS X and Xcode, can be found in the OCaml Programming Archives.
Test
After installing, you might want to compile a tiny test program as follows.
$ cat > hello.ml
let main () = Printf.printf "Hello, world!\n"
let () = main ()
^D
$ /usr/local/ocamlios64/bin/ocamlopt -o hello hello.ml
$ otool -hv hello
hello:
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds ...
MH_MAGIC_64 ARM64 ALL 0x00 EXECUTE 16 1544 ...
If the compiler produces a 64-bit ARM executable binary as shown here, it is almost certainly working correctly.
To produce a 32-bit ARM executable, use ocamlios32
in place of
ocamlios64
on the compile line.
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.
In recent years I’ve also been maintaining patches for a modification of OCaml that compiles OCaml apps to run in the iOS Simulator. This is a simpler way to get started, as it doesn’t require an iOS device or registration with Apple. Support for the iOS Simulator is also being prepared for inclusion in the OCaml mainline, and is available from the same branch of the OCaml GitHub repository.
I’ve built compilers for 32-bit and 64-bit simulator apps. You can find them in the companion note Compile OCaml for iOS Simulator
If you have any questions or comments about the OCaml iOS compiler, feel free to leave them below, or email me at jeffsco@psellos.com.
Appendix: Building from Sources
For maximum flexibility, you can build OCamliOS from sources on an OS X
system. It’s a little more complicated than a standard OCaml build, but
there is a script named build.sh
that handles many of the details.
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. 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 OCamliOSSim, described in [Compile OCaml for iOS Simulator][compile-to-iossim.html]. 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 OCamliOS.
The commands for building the 32-bit and 64-bit compilers are similar.
To simplify the discussion, I’ll use the variable BITS
to represent
the target width. If you’re following along at the keyboard, you can
actually set BITS
to 32 or 64, or you can just remember to substitute
the right number.
$ BITS=64 # (Or 32)
Create Native Compiler of Proper Width
Recent versions of OCaml have built-in support for cross compilation. One limitation is that the cross compiler must be built using an OCaml compiler of the same width as the target architecture and of the identical version. If you think about it, this makes sense. The runtime of the cross compiler is shared with the compiler used to build it, so they have to match.
You are building OCaml 4.02.3 for iOS, so you need a native OS X OCaml
4.02.3 compiler of target width BITS
. In my opinion the best way to
make sure of this is to build it yourself.
So, check out the tagged sources of OCaml 4.02.3 from the OCaml repository:
$ git clone -b 4.02.3 https://github.com/ocaml/ocaml ocaml-4.02.3-$BITS
(Git will tell you you are in ‘detached HEAD’ state, which sounds gruesome but isn’t a problem.)
$ cd ocaml-4.02.3-$BITS
Now modify the file named VERSION
, adding +ios
to the end of the
first line. When you’re done you should see this:
$ head -1 VERSION
4.02.3+ios
If you’re making the 32-bit compiler, configure as follows:
$ ./configure -prefix $(pwd)/release -no-shared-libs \
-host i386-apple-darwin$(uname -r) \
-cc 'clang -arch i386' \
-as 'clang -arch i386 -c' \
-aspp 'clang -arch i386 -c' \
-lib -Wl,-no_pie
If you’re making the 64-bit compiler, configure as follows:
$ ./configure -prefix $(pwd)/release
Since your Mac is a 64-bit machine, configuring the 64-bit compiler is much simpler. (If it’s not a 64-bit machine, you’ll need to experiment with the options.)
Now build and install your custom compiler:
$ make world.opt
$ make install
This will install the new compiler to a directory named release
in the
work tree. For the next build, you want to make this your default OCaml
compiler (temporarily):
$ PATH=$(pwd)/release/bin:$PATH
Back to the starting directory:
$ cd ..
Build OCamliOS
Now you can build the cross compiler for iOS of your target width. First check out the modified sources from Gerd Stolpmann’s repository:
$ git clone -b gs-4.02.3+ios https://github.com/gerdstolpmann/ocaml gs-4.02.3+ios-$BITS
$cd gs-4.02.3+ios-$BITS
Make the same modification to VERSION
as above (add +ios
at the end of
the first line).
$ head -1 VERSION
4.02.3+ios
Modify the Makefile
as follows. If you see this at around line 315:
echo "#!`which ocamlrun`" > boot/camlheader
change it to this instead:
echo "#!$(BINDIR)/ocamlrun" > boot/camlheader
Now you’re going to run the script build.sh
, but you need to make some
changes first.
If you’re building for 32 bits, the arch
line near the beginning of
build.sh
should say arch=armv7
. If you’re building for 64 bits, it
should say arch=arm64
.
Add a line after the arch
line that defines the minimum iOS version
that you want to allow for your iOS apps. If you set this to an early
version, your apps won’t be able to use more recent features of iOS. On
the other hand, if you set it to a recent version of iOS, people with
older iOS versions won’t be able to run your apps. My apps don’t demand
any recent iOS features, so I specify version 7.0:
minver=7.0
Comment out the sdk
line, so it looks something like this:
# sdk=8.4
Modify the SDK line so it looks like this:
SDK=/Developer/SDKs/${platform}.sdk
(This works because the SDK directory name no longer changes with each
version. You can find the version in SDKSettings.plist
. This is a
tremendous simplification.)
Now change all the gcc
lines to use minver
. All the gcc
lines
should have this value for the -miphoneos-version-min
flag:
-miphoneos-version-min=$minver
In the current build.sh
script there are three lines to change.
Now choose an install location for the compiler. The binaries that I
made use /usr/local/ocamlios$BITS
. Build and install the cross
compiler like this:
$ T=/usr/local/ocamlios$BITS
$ ./build.sh -prefix $T -target-bindir $T/bin
$ sudo make install
Now you’re ready to test the compiler as described above under Test.