Support for compiling Pony programs on ARM and 32-bit x86 landed recently. This allows compiling and running Pony on Raspberry Pi and other ARM devices. I was curious if it would be possible to compile Pony programs to run on Android and this post outlines how I got a "Hello World" example working.

The Pony compiler, ponyc, does not currently support cross compilation. It uses the C pre-processor to generate code for the platform it is running on. This has hardcoded assumptions for byte size (32 vs 64 bit) and processor support (ARM vs x86). Note that this is a proof of concept and hacks the compiler and runtime source to get things working. From this I hope to learn more elegant ways of supporting cross compiling.

Ponyc Android Cross Compiler

The first step was to build a version of ponyc that would generate Android compatible ARM code without any host platform specific code being included. I built ponyc for 32-bit x86, modified to not include any x86 specific code generation and to allow selecting the non-native LLVM backends. This ensures that the hard coded assumptions for the 32-bit size matches my target, 32-bit Android.

The changes for this are in the android_ponyc_cross branch of my github repository. The changes were:

Modify the Makefile to use 32-bit flags to the compiler. Add some LLVM initialization to allow selection of non-native backends so ARM can be generated on x86 hosts. Comment out some PLATFORM_IS_X86 preprocessor statements to prevent x86 specific code being generated.

For a real cross compiler these would be changed to be runtime selectable in some way. For the proof of concept it suffices to create a hardcoded compiler specific for this example.

For me it was necessary to build a version of LLVM for 32-bit as I'm on a 64-bit host. This was done by doing the following:

$ sudo apt-get install libicu-dev:i386 libncurses5-dev:i386 libxml2-dev:i386 $ tar xvf /tmp/llvm-3.6.2.src.tar.xz $ cd llvm-3.6.2.src/tools $ tar xvf /tmp/cfe-3.6.2.src.tar.xz $ mv cfe-3.6.2.src clang $ cd .. $ mkdir build $ cd build $ cmake -DLLVM_BUILD_32_BITS=ON -DCMAKE_INSTALL_PREFIX=/tmp/llvm .. $ make $ make install

Building the modified ponyc used these steps:

$ git clone -b android_ponyc_cross https://github.com/doublec/ponyc android_ponyc_cross $ cd android_ponyc_cross $ LLVM_CONFIG=/tmp/llvm/bin/llvm-config CXX="g++ -m32" make config=release ponyc

This generates a ponyc executable in the build/release directory.

Android compatible Pony runtime

An Android compatible libponyrt library is needed to link. The changes to build this are on the android_libponyrt branch. This must be built using an Android NDK standalone toolkit. A compatible standalone toolkit can be created with the following run from the NDK root directory:

$ ./build/tools/make-standalone-toolchain.sh --arch=arm \ --platform=android-21 --install-dir=/tmp/stk21

Add the resulting installation directory to the PATH :

$ export PATH=/tmp/stk21/bin:$PATH

Make sure a recent ponyc compiler is on the PATH . This should not be the cross compiler built previously but a ponyc compiler for the host platform. Build the runtime library with:

$ git clone -b android_libponyrt https://github.com/doublec/ponyc android_libponyrt $ cd android_libponyrt $ CC=arm-linux-androideabi-gcc make config=release libponyrt

The resulting library is in the directory build/release/libponyrt.a .

Compiling to Android

With the above tasks complete it's now possible to build an Android compatible binary. I tested with a "Hello World" example:

actor Main new create(env:Env) => env.out.print("Hello Android")

With that in a main.pony file in a hello directory, build with:

$ /tmp/android_ponyc_cross/build/release/ponyc --triple arm-linux-androideabi -rir hello ...ignore warnings about core-avx2 feature here... $ llvm-as hello.ll $ llc -mtriple=arm-linux-androideabi hello.bc -o hello.o -filetype=obj $ arm-linux-androideabi-gcc -fPIE -pie hello.o -o hello1 -L/tmp/android_libponyrt/build/release/ -lponyrt

The first command instructs our cross compiler to generate code for Android and to only produce the LLVM assembly listing. This is compiled to bytecode with llvm-as and then to an object file with llc . The Android version of gcc is used to link with the Android version of libponyrt that was created earlier. The hello binary that is produced can be copied to an Android device (I used a Note 3 running Lollipop) and run:

$ adb push hello1 /data/local/tmp/ $ adb shell $ cd /data/local/tmp $ ./hello1 Hello Android

Conclusion

I haven't tested any other features running on Android yet but this is a promising start. Using the Pony FFI and JNI it is hopefluly possible to write native Android applications.