I recently came across a requirement to build a project involving C++ code on Android. I've done this before with small C projects, for example, patching the JNI code in Lumicall.
Google supplies a toolchain dubbed the Native Development Kit (NDK) for Android. Using the NDK typically involves using it's custom build system, usually duplicating all Makefile content into a file called Android.mk in each subdirectory.
For large projects, that duplication doesn't sound like fun to implement and it definitely doesn't sound like fun keeping it up to date.
reSIProcate is quite a large project involving multiple shared libraries, some useful demonstration apps, a SIP proxy (repro) and a TURN server (reTurn). There are 39 Makefiles and numerous dependencies. There are numerous test cases for each library and sub-project too.
Sounds like a perfect fit for Android
The Android NDK documentation pack describes a process for bypassing Android.mk files, invoking the toolchain (compiler, linker, etc) directly to build standalone apps with their own Makefile system
I felt this was worth a go
Two approaches are described. The first approach is to simply reference toolchain binaries (e.g. arm-linux-androideabi-gcc) using the well-known environment variables such as CC and LD. The documentation warns that in this case, support for the Standard Template Library (STL) is extremely limited.
Nonetheless, I decided to try this first and found that I couldn't easily link binaries with the full STL feature set required by reSIProcate. I moved on to the second approach.
The second approach involves calling the script ${NDK_HOME}/build/tools/make-standalone-toolchain.sh to export a custom toolchain directory tree with full STL support. Using a little more disk space, but not so hard.
Initially I had NDK version r7 on my system and I found that make-standalone-toolchain.sh was not working reliably. It appeared to complete successfully (exit status = 0) but in reality it was not copying the STL components to the custom directory. I upgraded to version r8e and the problem was resolved.
Finally, I wrote a little wrapper script that can do everything: build the custom toolchain, set up all those environment variables for the tools and then invoke the configure script with some of the special settings that are required for a cross compile.
To gain some confidence in this progress, I had already run the full reSIProcate build, including test cases, on all hosts in the Debian build farm, including some ARM hosts. This helped confirm that the code is correct for such CPUs as well as helping to identify some previously unknown errors on the more typical platforms.
Nonetheless, some minor problems appeared with the build for Android:
After fixing a few of these minor issues, everything fell into place and the build was successful.
The reSIProcate project has a strong tradition of providing cross-platform support. The code quality is quite good and well documented. Despite the project's large size and complicated build system, there was already a good chance of success.
This may not be the case for every C/C++ project, especially those making extensive use of other shared libraries.
For the core features I wanted to use, I only needed one major dependency, the OpenSSL project.
libcrypto and libssl already appear in Android. Nonetheless, their headers are not present in the NDK tree and therefore their API (and compile-time flags) can not be considered 100% stable across all handsets past and future.
The solution is to build a standalone libcrypto and libssl and ship them with your own app.
Fortunately this has been done before: I grabbed the code from Guardian Project's OpenSSL in github and found that it built immediately with my NDK version.
To use it, simply make sure that CPPFLAGS and LDFLAGS include the necessary references to OpenSSL, as demonstrated in my script
I found that make -i check would enable me to build all test cases on the build host. Naturally it would fail to run any of them as they are ARM binaries. Using -i in the make command line makes it possible to build them all anyway, ignoring the failures, for running them later.
Copying the test* binaries from the .libs directories to the phone, I was able to run them immediately
There are three further steps in this work: