Difference between revisions of "Build LDC for Android"

From D Wiki
Jump to: navigation, search
(update to latest ldc)
(Update to use ltsmaster branch and NDK 11)
Line 1: Line 1:
 
This page will show you how to build a ldc cross-compiler for Android/ARM on linux, along with how to build and run both the druntime/phobos tests and an Android D app using the cross-compiler.
 
This page will show you how to build a ldc cross-compiler for Android/ARM on linux, along with how to build and run both the druntime/phobos tests and an Android D app using the cross-compiler.
  
Almost all the druntime/phobos unit tests pass on Android/ARM.  One of the native OpenGL sample apps from the Android NDK has been ported to D, I'll port some more soon.  Remaining work to be done is listed last.
+
Almost all the druntime/phobos unit tests pass on Android/ARM.  Two of the native OpenGL sample apps from the Android NDK have been ported to D, I'll port some more soon.  Remaining work to be done is listed last.
  
 
You can also try out [[Build DMD for Android|dmd for Android/x86]].
 
You can also try out [[Build DMD for Android|dmd for Android/x86]].
Line 12: Line 12:
 
* Common development tools, such as CMake and git, and ldc uses libconfig++
 
* Common development tools, such as CMake and git, and ldc uses libconfig++
 
* ldc/druntime/phobos source
 
* ldc/druntime/phobos source
** Get the source using git, as these Android patches were tested on the master branch of each repo.
+
** Get the source using git, as these Android patches were tested on the ltsmaster branch of each repo.
* llvm 3.6 source, either from the official release or git
+
* llvm 3.8 source, either from the official release or git  
** llvm 3.7 or later will work too, but you'll have to modify the small llvm patch so it still applies.
 
 
* Android native toolchain, [http://developer.android.com/ndk/index.html the NDK] and optionally [http://developer.android.com/sdk/index.html the SDK]
 
* Android native toolchain, [http://developer.android.com/ndk/index.html the NDK] and optionally [http://developer.android.com/sdk/index.html the SDK]
 
** The SDK is necessary if you want to package a GUI app; the NDK is enough if you just want to build a command-line binary, such as a test runner.  If you get the SDK, all that's needed is the "SDK Tools only" version, as long as you don't plan on using their IDE integration.  I will only write about using the command-line tools.  The SDK requires JDK 7: follow their instructions to make sure it's installed right.
 
** The SDK is necessary if you want to package a GUI app; the NDK is enough if you just want to build a command-line binary, such as a test runner.  If you get the SDK, all that's needed is the "SDK Tools only" version, as long as you don't plan on using their IDE integration.  I will only write about using the command-line tools.  The SDK requires JDK 7: follow their instructions to make sure it's installed right.
Line 22: Line 21:
 
==Compile llvm==
 
==Compile llvm==
  
Get the source for llvm, either [http://llvm.org/releases/download.html#3.6.2 the last official 3.6.2 release] or [https://android.googlesource.com/toolchain/llvm/ a git repository like the official Android llvm], which has some modifications but shouldn't really change much.  [https://gist.github.com/joakim-noah/1fb23fba1ba5b7e87e1a Download the patch for llvm], apply it, and then [http://llvm.org/docs/GettingStarted.html#getting-started-quickly-a-summary build llvm as you would normally], with the ARM target:
+
Get the source for llvm, either [http://llvm.org/releases/download.html#3.8.0 the latest official 3.8.0 release] or [https://github.com/llvm-mirror/llvm a git repository like this llvm mirror].  [https://gist.github.com/joakim-noah/1fb23fba1ba5b7e87e1a Download the patch for llvm], apply it, and then [http://llvm.org/docs/GettingStarted.html#getting-started-quickly-a-summary build llvm as you would normally], with the ARM target:
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
curl -O http://llvm.org/releases/3.6.2/llvm-3.6.2.src.tar.xz
+
curl -O http://llvm.org/releases/3.8.0/llvm-3.8.0.src.tar.xz
tar xvf llvm-3.6.2.src.tar.xz
+
tar xvf llvm-3.8.0.src.tar.xz
cd llvm-3.6.2.src/
+
cd llvm-3.8.0.src/
curl -O https://gist.githubusercontent.com/joakim-noah/1fb23fba1ba5b7e87e1a/raw/edb8005d8e972b2c258cd4699e6ad1b8315a8af7/android_tls
+
curl -O https://gist.githubusercontent.com/joakim-noah/1fb23fba1ba5b7e87e1a/raw/ff54ecbe824b5f45669ea3a86f136ded16b1dd91/android_tls
 
git apply android_tls
 
git apply android_tls
  
Line 39: Line 38:
 
==Build ldc for Android/ARM==
 
==Build ldc for Android/ARM==
  
Clone the ldc repository, [https://gist.github.com/joakim-noah/63693ead3aa62216e1d9 apply the Android patch], set the NDK environment variable to the location of your NDK and NDK_ARCH to its architecture, either x86 or x86_64, and [[Building LDC from source|build ldc as usual]]:
+
Clone the ldc repository, check out the last fully C++ version of ldc from the ltsmaster branch, [https://gist.github.com/joakim-noah/63693ead3aa62216e1d9 apply the Android patch], set the NDK environment variable to the location of your NDK and NDK_ARCH to its architecture, either x86 or x86_64, and [[Building LDC from source|build ldc as usual]]:
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
Line 45: Line 44:
 
git clone --recursive https://github.com/ldc-developers/ldc.git
 
git clone --recursive https://github.com/ldc-developers/ldc.git
 
cd ldc/
 
cd ldc/
 +
git checkout -b lts origin/ltsmaster
 
git submodule update
 
git submodule update
curl -O https://gist.githubusercontent.com/joakim-noah/63693ead3aa62216e1d9/raw/dc9301d793209dd35d23955077eaf5d29ef91ee4/ldc_android_arm
+
curl -O https://gist.githubusercontent.com/joakim-noah/63693ead3aa62216e1d9/raw/1896c4cd8fad0783268cfafb9c359e85ebdc089d/ldc_android_arm
 
git apply ldc_android_arm
 
git apply ldc_android_arm
  
 
mkdir build
 
mkdir build
 
cd build/
 
cd build/
export NDK=/path/to/your/android-ndk-r10e
+
export NDK=/path/to/your/android-ndk-r11c
export NDK_ARCH=x86
+
export NDK_ARCH=x86_64
cmake .. -DLLVM_CONFIG=../../llvm-3.6.2.src/build/bin/llvm-config
+
cmake .. -DLLVM_CONFIG=../../llvm-3.8.0.src/build/bin/llvm-config
 
make ldc2 -j5
 
make ldc2 -j5
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 61: Line 61:
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
 
cd ../runtime/druntime/
 
cd ../runtime/druntime/
curl -O https://gist.githubusercontent.com/joakim-noah/d936d6a339426ad1fac3/raw/9486de62c72a64111e079bda9d7a7b58b6729909/druntime_ldc_arm
+
curl -O https://gist.githubusercontent.com/joakim-noah/d936d6a339426ad1fac3/raw/fdc56c6cb8b349662028afa13c31abd398140d10/druntime_ldc_arm
 
git apply druntime_ldc_arm
 
git apply druntime_ldc_arm
  
 
cd ../phobos/
 
cd ../phobos/
curl -O https://gist.githubusercontent.com/joakim-noah/5c03801fa6c59b1e90df/raw/8ef824365d5dcc46fb47fabf7681e5425d81cc32/phobos_ldc_arm
+
curl -O https://gist.githubusercontent.com/joakim-noah/5c03801fa6c59b1e90df/raw/20d20775e63cc4398c8297961952557588b41af7/phobos_ldc_arm
 
git apply phobos_ldc_arm
 
git apply phobos_ldc_arm
  
Line 79: Line 79:
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
./bin/ldc2 -mtriple=armv7-none-linux-androideabi -relocation-model=pic
+
./bin/ldc2 -mtriple=armv7-none-linux-androideabi
 
           -c ../tests/d2/dmd-testsuite/runnable/sieve.d
 
           -c ../tests/d2/dmd-testsuite/runnable/sieve.d
  
$NDK/toolchains/llvm-3.6/prebuilt/linux-$NDK_ARCH/bin/clang -Wl,-z,nocopyreloc
+
$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang -Wl,-z,nocopyreloc
 
--sysroot=$NDK/platforms/android-9/arch-arm -lgcc
 
--sysroot=$NDK/platforms/android-9/arch-arm -lgcc
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-$NDK_ARCH
+
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH
 
-target armv7-none-linux-androideabi -no-canonical-prefixes -fuse-ld=bfd
 
-target armv7-none-linux-androideabi -no-canonical-prefixes -fuse-ld=bfd
 
-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro
 
-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro
-Wl,-z,now -mthumb -Wl,--export-dynamic -lc -lm sieve.o lib/libphobos2-ldc.a
+
-Wl,-z,now -fPIE -pie -mthumb -Wl,--export-dynamic -lc -lm sieve.o lib/libphobos2-ldc.a
 
lib/libdruntime-ldc.a -o sieve
 
lib/libdruntime-ldc.a -o sieve
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 93: Line 93:
 
The compiler and linker flags were taken from [http://wiki.dlang.org/Build_DMD_for_Android#Default_build_of_the_C_sample_app the output from running a NDK sample app's build scripts in verbose mode].
 
The compiler and linker flags were taken from [http://wiki.dlang.org/Build_DMD_for_Android#Default_build_of_the_C_sample_app the output from running a NDK sample app's build scripts in verbose mode].
  
Now we run this program on an Android device or emulator.  I've solely run on actual Android devices, with either [https://play.google.com/store/apps/details?id=com.termux&hl=en a terminal app] or [https://play.google.com/store/apps/details?id=berserker.android.apps.sshdroid&hl=en an SSH server app].  Once you have either of those apps installed, copy the sieve program to the device, go to the app's local directory by typing 'cd' at its command-line, copy the program there, and run it:
+
Run this program on an Android device or emulator.  I've solely run on actual Android devices, with either [https://play.google.com/store/apps/details?id=com.termux&hl=en a terminal app] or [https://play.google.com/store/apps/details?id=berserker.android.apps.sshdroid&hl=en an SSH server app].  Once you have either of those apps installed, copy the sieve program to the device, go to the app's local directory by typing 'cd' at its command-line, copy the program there, and run it:
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
Line 124: Line 124:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The tests take about 40 seconds to run on my dual Cortex-A15 device: all of them pass.  A handful of tests across four modules were disabled, either because they fail or, in the case of rt.lifetime, pass but cause problems for subsequent tests.  One module, core.sync.semaphore, is not included in the list of modules, [https://github.com/D-Programming-Language/druntime/pull/784#issuecomment-42777328 because sem_destroy works differently in bionic] and triggers a segfault on the next GC run after its tests pass, which doesn't matter because that test assumes sem_destroy works in a certain way.
+
The tests take about 40 seconds to run on my dual Cortex-A15 device: all of them pass.  A handful of tests across two modules were disabled, either because they fail or, in the case of rt.lifetime, pass but cause problems for subsequent tests.  One module, core.sync.semaphore, is not included in the list of modules, [https://github.com/D-Programming-Language/druntime/pull/784#issuecomment-42777328 because sem_destroy works differently in bionic] and triggers a segfault on the next GC run after its tests pass, which doesn't matter because that test assumes sem_destroy works in a certain way.
  
 
==Build a sample OpenGL Android app ported to D==
 
==Build a sample OpenGL Android app ported to D==
Line 137: Line 137:
 
You can find more info about building using the NDK in [[Build DMD for Android#Build_an_Android_sample_app|my earlier instructions for Android/x86]].  This is just the essence, redone for ARM.  You will build a pure native apk without any Java source, ie pure D along with the basic C glue/wrapper that comes with the NDK.
 
You can find more info about building using the NDK in [[Build DMD for Android#Build_an_Android_sample_app|my earlier instructions for Android/x86]].  This is just the essence, redone for ARM.  You will build a pure native apk without any Java source, ie pure D along with the basic C glue/wrapper that comes with the NDK.
  
First, you need to edit the C wrapper from the NDK, so that it initializes the D runtime properly for a shared library.  Go to the sample app and copy android_native_app_glue.h and android_native_app_glue.c from the NDK:
+
First, you need to edit the C wrapper from the NDK, so that it initializes the D runtime properly for a shared library.  Go to the native-activity sample app and copy android_native_app_glue.h and android_native_app_glue.c from the NDK:
  
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
Line 158: Line 158:
 
<syntaxhighlight lang=bash>
 
<syntaxhighlight lang=bash>
 
../../../ldc/build/bin/ldc2 -mtriple=armv7-none-linux-androideabi
 
../../../ldc/build/bin/ldc2 -mtriple=armv7-none-linux-androideabi
                             -relocation-model=pic -I../../ -c jni/main.d
+
                             -I../../ -c jni/main.d
  
 
../../../ldc/build/bin/ldc2 -mtriple=armv7-none-linux-androideabi
 
../../../ldc/build/bin/ldc2 -mtriple=armv7-none-linux-androideabi
                             -relocation-model=pic -I../../ -c ../../android/sensor.d
+
                             -I../../ -c ../../android/sensor.d
  
$NDK/toolchains/llvm-3.6/prebuilt/linux-$NDK_ARCH/bin/clang
+
$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-$NDK_ARCH
+
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH
 
-fpic -ffunction-sections -funwind-tables -fstack-protector-strong
 
-fpic -ffunction-sections -funwind-tables -fstack-protector-strong
 
-Wno-invalid-command-line-argument -Wno-unused-command-line-argument
 
-Wno-invalid-command-line-argument -Wno-unused-command-line-argument
Line 176: Line 176:
 
mkdir -p libs/armeabi-v7a/
 
mkdir -p libs/armeabi-v7a/
  
$NDK/toolchains/llvm-3.6/prebuilt/linux-$NDK_ARCH/bin/clang -Wl,-soname,libnative-activity.so
+
$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang -Wl,-soname,libnative-activity.so
 
-shared --sysroot=$NDK/platforms/android-9/arch-arm main.o sensor.o
 
-shared --sysroot=$NDK/platforms/android-9/arch-arm main.o sensor.o
 
../../../ldc/build/lib/libphobos2-ldc.a ../../../ldc/build/lib/libdruntime-ldc.a
 
../../../ldc/build/lib/libphobos2-ldc.a ../../../ldc/build/lib/libdruntime-ldc.a
 
android_native_app_glue.o -lgcc
 
android_native_app_glue.o -lgcc
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-$NDK_ARCH
+
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH
 
-no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi
 
-no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi
 
-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now
 
-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now
Line 209: Line 209:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This assumes that the ldc and android repositories are in the same directory, as show in these instructions.  If not, modify ANDROID_DIR in the CMake build script to use the path you want.
+
This assumes that the ldc and android repositories are in the same directory, as shown in these instructions.  If not, modify ANDROID_DIR in the CMake build script to use the path you want.
  
 
Finally, package the test runner apk:
 
Finally, package the test runner apk:
Line 223: Line 223:
  
 
==Directions for future work==
 
==Directions for future work==
 
* Tests fail when llvm optimizations are applied to std.random and inlining enabled for two modules, std.exception and std.internal.math.errorfunction.  The sources of these optimization issues have to be tracked down.
 
 
* One custom range in std.datetime that is defined only for the unit tests, Rand3Letters, causes a memory leak when called, so it is disabled in the above phobos patch.  Ldc's code generation for this range will need to be investigated.
 
 
* Exceptions thrown across fiber switches are not properly chained, that'll need to be looked into.
 
  
 
* A couple test cases related to underflow/overflow in std.math are disabled, I haven't looked into why they don't work.
 
* A couple test cases related to underflow/overflow in std.math are disabled, I haven't looked into why they don't work.
Line 234: Line 228:
 
* Port the NDK support libraries and more of the sample apps to D, including apps that require using JNI, ie interfacing with Java.
 
* Port the NDK support libraries and more of the sample apps to D, including apps that require using JNI, ie interfacing with Java.
  
* I have not tried cross-compiling the ldc compiler test suite and running it.  I'd like to get to that eventually.
+
* When compiled natively, most of the ldc compiler test suite passes, with tests failing across 10 modules.  Six are related to floating-point math and the remaining four have to do with linking against C and C++ files.  I'm looking into those.
  
 
* [http://wiki.dlang.org/Build_DMD_for_Android#Directions_for_future_work All the further work mentioned for the earlier Android/x86 port also apply here].
 
* [http://wiki.dlang.org/Build_DMD_for_Android#Directions_for_future_work All the further work mentioned for the earlier Android/x86 port also apply here].

Revision as of 07:09, 21 April 2016

This page will show you how to build a ldc cross-compiler for Android/ARM on linux, along with how to build and run both the druntime/phobos tests and an Android D app using the cross-compiler.

Almost all the druntime/phobos unit tests pass on Android/ARM. Two of the native OpenGL sample apps from the Android NDK have been ported to D, I'll port some more soon. Remaining work to be done is listed last.

You can also try out dmd for Android/x86.

Prerequisites

  • linux host, where you'll build and run ldc
    • You can use a virtual machine like VirtualBox/VMware, with at least 512 MB of memory and 1 GB of swap, particularly if building the phobos unit tests, and 10 GB of disk space.
  • C++ compiler and toolchain, to build ldc
  • Common development tools, such as CMake and git, and ldc uses libconfig++
  • ldc/druntime/phobos source
    • Get the source using git, as these Android patches were tested on the ltsmaster branch of each repo.
  • llvm 3.8 source, either from the official release or git
  • Android native toolchain, the NDK and optionally the SDK
    • The SDK is necessary if you want to package a GUI app; the NDK is enough if you just want to build a command-line binary, such as a test runner. If you get the SDK, all that's needed is the "SDK Tools only" version, as long as you don't plan on using their IDE integration. I will only write about using the command-line tools. The SDK requires JDK 7: follow their instructions to make sure it's installed right.
  • Android/ARM, whether a device or emulator
    • The SDK comes with an emulator. I use actual hardware, so that's what I'll discuss.

Compile llvm

Get the source for llvm, either the latest official 3.8.0 release or a git repository like this llvm mirror. Download the patch for llvm, apply it, and then build llvm as you would normally, with the ARM target:

curl -O http://llvm.org/releases/3.8.0/llvm-3.8.0.src.tar.xz
tar xvf llvm-3.8.0.src.tar.xz
cd llvm-3.8.0.src/
curl -O https://gist.githubusercontent.com/joakim-noah/1fb23fba1ba5b7e87e1a/raw/ff54ecbe824b5f45669ea3a86f136ded16b1dd91/android_tls
git apply android_tls

mkdir build
cd build/
cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=ARM
make -j5

Build ldc for Android/ARM

Clone the ldc repository, check out the last fully C++ version of ldc from the ltsmaster branch, apply the Android patch, set the NDK environment variable to the location of your NDK and NDK_ARCH to its architecture, either x86 or x86_64, and build ldc as usual:

cd ../../
git clone --recursive https://github.com/ldc-developers/ldc.git
cd ldc/
git checkout -b lts origin/ltsmaster
git submodule update
curl -O https://gist.githubusercontent.com/joakim-noah/63693ead3aa62216e1d9/raw/1896c4cd8fad0783268cfafb9c359e85ebdc089d/ldc_android_arm
git apply ldc_android_arm

mkdir build
cd build/
export NDK=/path/to/your/android-ndk-r11c
export NDK_ARCH=x86_64
cmake .. -DLLVM_CONFIG=../../llvm-3.8.0.src/build/bin/llvm-config
make ldc2 -j5

Download and apply the patch for druntime and the patch for phobos before building them:

cd ../runtime/druntime/
curl -O https://gist.githubusercontent.com/joakim-noah/d936d6a339426ad1fac3/raw/fdc56c6cb8b349662028afa13c31abd398140d10/druntime_ldc_arm
git apply druntime_ldc_arm

cd ../phobos/
curl -O https://gist.githubusercontent.com/joakim-noah/5c03801fa6c59b1e90df/raw/20d20775e63cc4398c8297961952557588b41af7/phobos_ldc_arm
git apply phobos_ldc_arm

cd ../../build/
make druntime-ldc phobos2-ldc -j5

More info about the Android/ARM patches can be found with their release.

Build a command-line executable

Now that we have a D cross-compiler and cross-compiled the standard library for Android/ARM, let's try building a small program, the classic Sieve of Eratosthenes single-core benchmark:

./bin/ldc2 -mtriple=armv7-none-linux-androideabi
           -c ../tests/d2/dmd-testsuite/runnable/sieve.d

$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang -Wl,-z,nocopyreloc
--sysroot=$NDK/platforms/android-9/arch-arm -lgcc
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH
-target armv7-none-linux-androideabi -no-canonical-prefixes -fuse-ld=bfd
-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro
-Wl,-z,now -fPIE -pie -mthumb -Wl,--export-dynamic -lc -lm sieve.o lib/libphobos2-ldc.a
lib/libdruntime-ldc.a -o sieve

The compiler and linker flags were taken from the output from running a NDK sample app's build scripts in verbose mode.

Run this program on an Android device or emulator. I've solely run on actual Android devices, with either a terminal app or an SSH server app. Once you have either of those apps installed, copy the sieve program to the device, go to the app's local directory by typing 'cd' at its command-line, copy the program there, and run it:

cd
cp /sdcard/sieve .
./sieve foobar

The program requires an argument, which is ignored. If it runs correctly, you'll see the following output, saying it ran 10 times and found 1899 primes in the first 8191 integers:

10 iterations
1899 primes

Run the druntime and phobos unit tests

Go back to the linux host and build the tests for druntime and phobos (don't add the -j5 flag to build in parallel unless you have GBs of memory available, as compiling some of the phobos modules' tests takes a fair amount of RAM):

make test-runner

Copy the test-runner and this list of druntime and phobos modules to your device and run it. I use the SSH server app on a random port, here's what I'd do (replace 192.168.35.7 with the IP address of your device and 20345 with the port you configured for the SSH service):

scp -P 20345 test.list runtime/test-runner jo@192.168.35.7:
ssh -p20345 jo@192.168.35.7
./test-runner

The tests take about 40 seconds to run on my dual Cortex-A15 device: all of them pass. A handful of tests across two modules were disabled, either because they fail or, in the case of rt.lifetime, pass but cause problems for subsequent tests. One module, core.sync.semaphore, is not included in the list of modules, because sem_destroy works differently in bionic and triggers a segfault on the next GC run after its tests pass, which doesn't matter because that test assumes sem_destroy works in a certain way.

Build a sample OpenGL Android app ported to D

Clone my android repository, which contains several headers and a C/OpenGL app from the NDK, translated to D:

cd ../../
git clone https://github.com/joakim-noah/android.git

You can find more info about building using the NDK in my earlier instructions for Android/x86. This is just the essence, redone for ARM. You will build a pure native apk without any Java source, ie pure D along with the basic C glue/wrapper that comes with the NDK.

First, you need to edit the C wrapper from the NDK, so that it initializes the D runtime properly for a shared library. Go to the native-activity sample app and copy android_native_app_glue.h and android_native_app_glue.c from the NDK:

cd android/samples/native-activity/
cp $NDK/sources/android/native_app_glue/android_native_app_glue.* .

Open android_native_app_glue.c in an editor and find the function android_main, then insert rt_init() and rt_term() around it, so it looks like this:

    rt_init();
    android_main(android_app);
    rt_term();

Compile the D source and the file you just modified, then link them into a shared library and place it in the directory that the SDK expects:

../../../ldc/build/bin/ldc2 -mtriple=armv7-none-linux-androideabi
                            -I../../ -c jni/main.d

../../../ldc/build/bin/ldc2 -mtriple=armv7-none-linux-androideabi
                            -I../../ -c ../../android/sensor.d

$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH
-fpic -ffunction-sections -funwind-tables -fstack-protector-strong
-Wno-invalid-command-line-argument -Wno-unused-command-line-argument
-no-canonical-prefixes -fno-integrated-as -target armv7-none-linux-androideabi
-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Os -g -DNDEBUG
-fomit-frame-pointer -fno-strict-aliasing -I$NDK/sources/android/native_app_glue
-DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
-I$NDK/platforms/android-9/arch-arm/usr/include
-c ./android_native_app_glue.c -o ./android_native_app_glue.o

mkdir -p libs/armeabi-v7a/

$NDK/toolchains/llvm/prebuilt/linux-$NDK_ARCH/bin/clang -Wl,-soname,libnative-activity.so
-shared --sysroot=$NDK/platforms/android-9/arch-arm main.o sensor.o
../../../ldc/build/lib/libphobos2-ldc.a ../../../ldc/build/lib/libdruntime-ldc.a
android_native_app_glue.o -lgcc
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-$NDK_ARCH
-no-canonical-prefixes -fuse-ld=bfd -target armv7-none-linux-androideabi
-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now
-mthumb -L$NDK/platforms/android-9/arch-arm/usr/lib -llog -landroid -lEGL -lGLESv1_CM
-lc -lm -o libs/armeabi-v7a/libnative-activity.so

Package the app as the SDK directs. I use the older Ant approach, which is being deprecated, replace it with the Gradle command from a newer SDK if needed. Set the path to your SDK, then package the apk using these commands:

export SDK=/path/to/your/android-sdk-linux
$SDK/tools/android update project -p . -s --target 1
ant debug

Transfer the resulting bin/NativeActivity-debug.apk to your device, go to Settings->Security and allow installation of apps from unknown sources, ie outside the Play Store, then install it. Go to your app folder and run the app named NativeActivity: it'll show a black screen and start flashing a bunch of colors upon a touch.

Run the druntime and phobos unit tests in an apk

Copy and edit the C wrapper and create the libs/armeabi-v7a/ directory as shown in the last section, then download and apply the small patch to have the sample app invoke the test runner, and rebuild:

curl -O https://gist.githubusercontent.com/joakim-noah/8ba3cd4958266f357295/raw/a52fcf1e63715f8b1bd3527afaa85872087b0f30/native_ldc_arm
git apply native_ldc_arm

cd ../../../ldc/build/
make test-runner-apk

This assumes that the ldc and android repositories are in the same directory, as shown in these instructions. If not, modify ANDROID_DIR in the CMake build script to use the path you want.

Finally, package the test runner apk:

cd ../../android/samples/native-activity/
ant debug

Transfer the resulting bin/NativeActivity-debug.apk to your device, and install it as before. Also, copy the list of modules to test to the /sdcard/ directory. The app will append its results to /sdcard/test.log, so if you happen to have a file with that name, move it.

This time, it should show a black screen for about a minute, while all the tests run. A touch after that and it should start flashing a bunch of colors. If not, look at the output in /sdcard/test.log and check if the app hung after any particular tested module. You can remove that module from test.list and try running again.

Directions for future work

  • A couple test cases related to underflow/overflow in std.math are disabled, I haven't looked into why they don't work.
  • Port the NDK support libraries and more of the sample apps to D, including apps that require using JNI, ie interfacing with Java.
  • When compiled natively, most of the ldc compiler test suite passes, with tests failing across 10 modules. Six are related to floating-point math and the remaining four have to do with linking against C and C++ files. I'm looking into those.