Difference between revisions of "Build LDC for Android"
(First draft of how to build ldc/druntime/phobos) |
(Build simple program and tests for command-line) |
||
Line 3: | Line 3: | ||
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. 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. | ||
− | You can also try out [ | + | You can also try out [[Build DMD for Android|dmd for Android/x86]]. |
==Prerequisites== | ==Prerequisites== | ||
− | * linux host, where you'll build ldc | + | * 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. | ** 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 | * C++ compiler and toolchain, to build ldc | ||
Line 16: | Line 16: | ||
** llvm 3.7 or later will work too, but you'll have to update the small llvm patch so it still applies. | ** llvm 3.7 or later will work too, but you'll have to update 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 only 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. | + | ** The SDK is only 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 | * Android/ARM, whether a device or emulator | ||
** The SDK comes with an emulator. I've only used actual hardware, so that's what I'll discuss. | ** The SDK comes with an emulator. I've only used actual hardware, so that's what I'll discuss. | ||
Line 22: | Line 22: | ||
==Build llvm== | ==Build llvm== | ||
− | Get the source for llvm, either [http://llvm.org/releases/ | + | 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 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 | ||
tar xvf llvm-3.6.2.src.tar.xz | tar xvf llvm-3.6.2.src.tar.xz | ||
cd llvm-3.6.2.src | cd llvm-3.6.2.src | ||
Line 38: | Line 39: | ||
==Build ldc for Android/ARM== | ==Build ldc for Android/ARM== | ||
− | Clone the ldc repository, check out the same commits that I built ldc/druntime/phobos with, [https://gist.github.com/joakim-noah/63693ead3aa62216e1d9 apply the Android patch], set the NDK environment variable to your NDK directory, and [ | + | Clone the ldc repository, check out the same commits that I built ldc/druntime/phobos with, [https://gist.github.com/joakim-noah/63693ead3aa62216e1d9 apply the Android patch], set the NDK environment variable to your NDK directory, and [[Building LDC from source|build ldc as usual]]: |
<syntaxhighlight lang=bash> | <syntaxhighlight lang=bash> | ||
Line 73: | Line 74: | ||
==Build a command-line executable== | ==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: | |
+ | <syntaxhighlight lang=bash> | ||
+ | ./bin/ldc2 -mtriple=armv7-none-linux-androideabi -relocation-model=pic | ||
+ | -c ../tests/d2/dmd-testsuite/runnable/sieve.d | ||
+ | |||
+ | $NDK/toolchains/llvm-3.6/prebuilt/linux-x86/bin/clang -Wl,-z,nocopyreloc | ||
+ | --sysroot=$NDK/platforms/android-9/arch-arm -lgcc | ||
+ | -gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86 | ||
+ | -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 -mthumb -Wl,--export-dynamic -lc -lm sieve.o lib/libphobos2-ldc.a | ||
+ | lib/libdruntime-ldc.a -o sieve | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 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 the NDK samples' build scripts in verbose mode]. | ||
+ | |||
+ | Now we run this program on an Android device or emulator. I've only run on an actual Android device, either [https://play.google.com/store/apps/details?id=jackpal.androidterm&hl=en in a terminal app] or by setting up [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> | ||
+ | cd | ||
+ | cp /sdcard/sieve . | ||
+ | ./sieve foobar | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 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: | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | 10 iterations | ||
+ | 1899 primes | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==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): | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | make test-runner | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Copy the test-runner and [https://github.com/joakim-noah/android/releases/download/runners/test.list 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): | ||
+ | |||
+ | <syntaxhighlight lang=bash> | ||
+ | scp -P 20345 test.list runtime/test-runner jo@192.168.35.7:/data/data/berserker.android.apps.sshdroid/home | ||
+ | ssh -p20345 jo@192.168.35.7 | ||
+ | ./test-runner | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | It takes about 40 seconds to run on my dual Cortex-A15 device and all tests pass. A handful of tests across six 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 that module's tests pass. | ||
+ | |||
+ | ==Build a sample OpenGL Android app ported to D== | ||
+ | |||
+ | Coming up next... | ||
+ | |||
[[Category:LDC]] | [[Category:LDC]] |
Revision as of 11:43, 4 November 2015
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.
You can also try out dmd for Android/x86.
Contents
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 have only been tested on the master branch of each repo.
- llvm 3.6 source, either from the official release or git
- llvm 3.7 or later will work too, but you'll have to update the small llvm patch so it still applies.
- Android native toolchain, the NDK and optionally the SDK
- The SDK is only 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've only used actual hardware, so that's what I'll discuss.
Build llvm
Get the source for llvm, either the last official 3.6.2 release or a git repository like the official Android llvm, which has some modifications but shouldn't really change much. Download the patch for llvm, apply it, and then build llvm normally with the ARM target:
curl -O http://llvm.org/releases/3.6.2/llvm-3.6.2.src.tar.xz
tar xvf llvm-3.6.2.src.tar.xz
cd llvm-3.6.2.src
curl -O https://gist.githubusercontent.com/joakim-noah/1fb23fba1ba5b7e87e1a/raw/4bc1439defd2bd962710e8710d3ac26d342f0b87/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 same commits that I built ldc/druntime/phobos with, apply the Android patch, set the NDK environment variable to your NDK directory, and build ldc as usual:
cd ../..
git clone --recursive https://github.com/ldc-developers/ldc.git
cd ldc
git checkout -b android c769251cc
git submodule update
curl -O https://gist.githubusercontent.com/joakim-noah/63693ead3aa62216e1d9/raw/0c5e1e976c9c1781dc898a5fe1509f690b0b4324/ldc_android_arm
git apply ldc_android_arm
mkdir build
cd build
export NDK=/path/to/your/android-ndk-r10e
cmake .. -DLLVM_CONFIG=../../llvm-3.6.2.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/ef212405065767e05c69dc6b67990ff56faf6a87/druntime_ldc_arm
git apply druntime_ldc_arm
cd ../phobos
curl -O https://gist.githubusercontent.com/joakim-noah/5c03801fa6c59b1e90df/raw/27b8bd0b6f43e805df59c56e3c4b6acf8e95541e/phobos_ldc_arm
git apply phobos_ldc_arm
cd ../../build
make druntime-ldc phobos2-ldc -j5
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 -relocation-model=pic
-c ../tests/d2/dmd-testsuite/runnable/sieve.d
$NDK/toolchains/llvm-3.6/prebuilt/linux-x86/bin/clang -Wl,-z,nocopyreloc
--sysroot=$NDK/platforms/android-9/arch-arm -lgcc
-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86
-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 -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 the NDK samples' build scripts in verbose mode.
Now we run this program on an Android device or emulator. I've only run on an actual Android device, either in a terminal app or by setting up 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:/data/data/berserker.android.apps.sshdroid/home
ssh -p20345 jo@192.168.35.7
./test-runner
It takes about 40 seconds to run on my dual Cortex-A15 device and all tests pass. A handful of tests across six 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 that module's tests pass.
Build a sample OpenGL Android app ported to D
Coming up next...