Psellos
Contemporary Development With Functional Programming

Compile OCaml for iOS (iPhone, iPad)

January 17, 2016

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:

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.

Comments

blog comments powered by Disqus