Difference between revisions of "Programming in D tutorial on Embedded Linux ARM devices"

From D Wiki
Jump to: navigation, search
(1. introductions: Just making it read better)
(Tweaks)
 
(17 intermediate revisions by 3 users not shown)
Line 1: Line 1:
= Programming in D tutorial on Embedded Linux ARM devices =
+
= Introduction =
  
== 1. Introduction ==
+
D is a great systems-programming language with clean syntax and great modeling power. Traditionally, Linux-based embedded devices are programmed using C or C++. Python and Java are more popular today, but fail due to large runtime size and resource requirements. Programming in D will be comfortable, since it is like a dynamic language but has native code performance, and has full ABI compatibility with C, making it very suitable as a “Linux systems-programming language”.
  
D is a great systems-programming language with clean syntax and great modelling power. Traditionally, Linux based embedded devices are programmed using C or C++. Python and Java are more populator today, but fail due to large runtime size and resource requirements. Programming in D will be comfortable, since it is like a dynamic language, but has native code performance, and has full ABI compatibility with C, making it very suitable as a “Linux systems-programming language”.
+
This simple tutorial will introduce you to programming in D on Embedded ARM Linux step by step.
  
This simple tutorial will introduce to programming in D on Embedded ARM Linux step by step.
+
= Preparing your ARM GCC toolchain =
  
== 2. preparing your GCC ARM toolchain ==
+
The very first thing is to prepare your toolchain. Although clang has supported ARM for a very long time, GCC is still the premier choice for compiling to ARM Linux systems.
  
The very first thing is to make your arm gcc toolchain prepared. Although clang supports ARM for a very long time, GCC is still the first choice for compiling your ARM Linux system.
+
If you are a ARM Linux application programmer, you probably already have ARM GCC installed, and tested some "hello world" programs on your ARM board.
  
If you are a arm linux application programmer, you would probably already have ARM GCC installed, and did a compiling and tested for your target board.
+
However, if you haven't installed ARM GCC, it is quite easy.
  
However, install ARM GCC is quite easy, the following is the instructions to install arm toolchain
+
Arch Linux:
 +
<pre>$ pacman -S arm-linux-gnueabihf-gcc</pre>
 +
Debian:
 +
<pre>$ apt install gcc-arm-linux-gnueabihf</pre>
 +
Fedora:
 +
<pre>$ dnf install arm-linux-gnueabihf-{binutils,gcc,glibc}</pre>
  
<pre>ArchLinux : $ pacman -S arm-linux-gnueabihf-gcc
+
''For a Fedora/Redhat-based OS like CentOS, if you find the armhf toolchain is not quite easy to install, you can just install the arm target instead of armhf, by running <code>$ yum install gcc-arm-linux-gnu </code>, and then use <code>arm-linux-gcc</code> rather than <code>arm-linux-gnueabihf-gcc</code>.''
Debain    : $ apt install gcc-arm-linux-gnueabihf</pre>
 
after the toolchain installed, you need to make a simple test make sure that ARM GCC is generating arm executable properly.
 
  
<pre>$ vim test.c
+
After the toolchain is installed, you can simply test to make sure that ARM GCC is generating ARM executables properly.
#include &lt;stdio.h&gt;
+
Create a file, test.c, that contains the following, using your favorite text editor:
 +
 
 +
<syntaxhighlight lang="C">
 +
#include <stdio.h>
 
int main()
 
int main()
 
{
 
{
 
     float a = 3.14;
 
     float a = 3.14;
 
     int b = 2;
 
     int b = 2;
     printf(&quot;hello world! 3.14 * 2 = %f\n&quot;, a*b);
+
     printf("hello world! 3.14 * 2 = %f\n";, a*b);
 
     return 0;
 
     return 0;
 
}
 
}
$ arm-linux-gnueabihf-gcc test.c -o test
 
$ scp test user@armboard:/home/user
 
  
# on ARM target run the test executable.
+
</syntaxhighlight>
ARM $ ./test
+
Then compile using:
 +
<pre>$ arm-linux-gnueabihf-gcc test.c -o test</pre>
 +
Copy the compiled program to your ARM target board:
 +
<pre>$ scp test user@armboard:/home/user</pre>
 +
 
 +
On the ARM target, run the test executable:
 +
<pre>ARM $ ./test
 
hello world! 3.14*2 = 6.280000
 
hello world! 3.14*2 = 6.280000
 
</pre>
 
</pre>
also, if your arm gcc is a toolchain compiled by yocto, you may calling GCC using $CC after you sourced the environment setup script.
+
<div style="background:#ffd; padding: 0.5ex 0.5ex;">
 +
<span style="font-size: 1.4em; color: #ca4;">&#9888;</span>If your ARM GCC toolchain was compiled by yocto, you may need to call GCC using $CC after you sourced the environment setup script.
 +
</div>
  
== 3. install the LDC2 D compiler ==
+
= Installing LDC2 =
  
D has 3 compiler implementations. Currently, only GDC and LDC support ARM and other cpus rother than x86 target.
+
D has 3 compiler implementations. Currently, only GDC and LDC support ARM and CPUs other than x86.
  
GDC support for arm can be enabled if you compiling your toolchain by yourself by passing –enable-languages=c,d,cpp when configuring. Or, you can download a arm GDC from GDC project home page.
+
GDC support for arm can be enabled if you are compiling your toolchain by yourself, by passing <code>–enable-languages=c,d,cpp</code> when configuring (i.e. <code>./configure -–enable-languages=c,d,cpp</code>. Or you can download an ARM enabled GDC build from the [https://www.gdcproject.org/ GDC project home page].
  
But at least when I am writing this tutorial, GDC only released 6.3.0 version which is not doing better support for the ARM target than LDC2 do.
+
As of the writing of this tutorial, the latest GDC release (6.3.0) is not as good as LDC2, for ARM targets.
  
LDC has better support for ARM target, and is more actively developed that GDC. And, as based on LLVM architecture, LDC does not need a cross-compiling toolchain prepared for an individual target.
+
LDC has better support for ARM targets, and is more actively developed that GDC. And, as it is based on LLVM, LDC does not need a cross-compiling toolchain prepared for an individual target.
  
So, what you need to download is right the LDC2 compiler for your x86 host.
+
The recommended way to install LDC is by using the install script (works on POSIX and POSIX like systems):
 +
<pre>curl https://dlang.org/install.sh | bash -s ldc</pre>
  
For better support for the D language, we recommend to download the latest LDC2 from github release page: https://github.com/ldc-developers/ldc/releases
+
You can also search your operating system's packages for the LDC package.
  
For ArchLinux or Gentoo Linux user, you can install the almost latest LDC compiler just using <code>pacman</code> or <code>emerge</code>
+
Verify that LDC was installed correctly:
 
 
If you download LDC2 standalone release from github page, say: ['''ldc2-1.8.0-linux-x86_64.tar.xz'''](https://github.com/ldc-developers/ldc/releases/download/v1.8.0/ldc2-1.8.0-linux-x86_64.tar.xz)
 
 
 
we need a little bit tricky thing to do:
 
 
 
<pre>mkdir your-devel-toolchain-dir
 
tar xf ldc2-x.y.z-linux-x86_64.tar.xz -C your-devel-toolchain-dir</pre>
 
The compiler will be installed to a dir named <code>ldc2-1.8.0-linux-x86_64</code> which has it’s own bin , etc , lib sub directories. As it’s a standalone distribution, you need to add <code>ldc2-1.8.0-linux-x86_64/bin</code> to your PATH env.
 
 
 
<pre>export PATH=${your-devel-toolchain-dir}/ldc2-1.8.0-linux-x86_64/bin:${PATH}</pre>
 
Now, you could invoke ldc2 directly:
 
  
 
<pre>$ ldc2 --version
 
<pre>$ ldc2 --version
Line 78: Line 80:
 
     arm64      - ARM64 (little endian)
 
     arm64      - ARM64 (little endian)
 
   ......</pre>
 
   ......</pre>
As you can see from the ldc2 version output, arm64 is a registered target, but it’s not completely supported right now. It’s a little bit disappointed, but don’t worry, we have arm well supported when the target is gcc+glibc+linux. And ARM and ARMhf executable can directly run on an arm64 target when arm64 has multilib support.
+
As you can see from the ldc2 version output, arm64 is a registered target, but it’s not completely supported right now. It’s a little bit disappointing, but don’t worry, we have ARM well supported when the target is gcc+glibc+linux. And ARM and ARMhf executable can directly run on an arm64 target when arm64 has multilib support.
 
 
== 4. compiling the D runtime for ARM target. ==
 
  
before doing this, you need to make sure cmake is installed on your host.
+
= Compiling the D runtime for the ARM target =
  
<pre>sudo apt install cmake</pre>
+
Before doing this, you need to make sure <code>CMake</code> is installed on your host. If not, please install it.
Then, just type the commands bellow to make your D arm runtime:
+
Then, just type the commands below to compile your D runtime:
  
 
<pre>$ CC=arm-linux-gnueabihf-gcc ldc-build-runtime --dFlags=&quot;-w;-mtriple=arm-linux-gnueabihf&quot; --targetSystem=&quot;Linux;UNIX&quot;
 
<pre>$ CC=arm-linux-gnueabihf-gcc ldc-build-runtime --dFlags=&quot;-w;-mtriple=arm-linux-gnueabihf&quot; --targetSystem=&quot;Linux;UNIX&quot;
Line 91: Line 91:
 
Creating build directory: ldc-build-runtime.tmp
 
Creating build directory: ldc-build-runtime.tmp
 
Downloading LDC source archive: https://github.com/ldc-developers/ldc/releases/download/v1.8.0/ldc-1.8.0-src.zip
 
Downloading LDC source archive: https://github.com/ldc-developers/ldc/releases/download/v1.8.0/ldc-1.8.0-src.zip
Invoking: [&quot;cmake&quot;, &quot;-DLDC_EXE_FULL=/media/Devel/Changhong/IOT/imx6u-devel/toolchain/ldc2-1.8.0-linux-x86_64/bin/ldc2&quot;, &quot;-DD_VERSION=2&quot;, &quot;-DDMDFE_MINOR_VERSION=0&quot;, &quot;-DDMDFE_PATCH_VERSION=78&quot;, &quot;-DLDC_TARGET_PRESET=&quot;, &quot;-DTARGET_SYSTEM=Linux;UNIX&quot;, &quot;-DD_FLAGS=-w;-mtriple=arm-linux-gnueabihf&quot;, &quot;-DRT_CFLAGS=&quot;, &quot;-DLD_FLAGS=&quot;, &quot;/media/Devel/Changhong/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime/ldc-build-runtime.tmp/ldc-src/runtime&quot;]
+
Invoking: [&quot;cmake&quot;, &quot;-DLDC_EXE_FULL=/media/Devel/IOT/imx6u-devel/toolchain/ldc2-1.8.0-linux-x86_64/bin/ldc2&quot;, &quot;-DD_VERSION=2&quot;, &quot;-DDMDFE_MINOR_VERSION=0&quot;, &quot;-DDMDFE_PATCH_VERSION=78&quot;, &quot;-DLDC_TARGET_PRESET=&quot;, &quot;-DTARGET_SYSTEM=Linux;UNIX&quot;, &quot;-DD_FLAGS=-w;-mtriple=arm-linux-gnueabihf&quot;, &quot;-DRT_CFLAGS=&quot;, &quot;-DLD_FLAGS=&quot;, &quot;/media/Devel/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime/ldc-build-runtime.tmp/ldc-src/runtime&quot;]
 
......</pre>
 
......</pre>
The ldc-build-runtime tool will download ldc compiler source and making the build.
+
The ldc-build-runtime tool will download ldc compiler source and cross-compile the runtime.
  
'''''NOTE:''''' ''yocto built toolchain need to use a different manner:''
+
'''''NOTE:''''' ''yocto-built toolchain needs to use a different manner:''
  
 
<pre>CC=arm-poky-linux-gnueabi-gcc ldc-build-runtime --dFlags=&quot;-w;-mtriple=arm-poky-linux-gnueabi;-float-abi=hard;-mcpu=cortex-a7&quot; --targetSystem=&quot;Linux;UNIX&quot;</pre>
 
<pre>CC=arm-poky-linux-gnueabi-gcc ldc-build-runtime --dFlags=&quot;-w;-mtriple=arm-poky-linux-gnueabi;-float-abi=hard;-mcpu=cortex-a7&quot; --targetSystem=&quot;Linux;UNIX&quot;</pre>
Line 102: Line 102:
 
<pre>export SDKTARGETSYSROOT=your-target-root-fs-dir
 
<pre>export SDKTARGETSYSROOT=your-target-root-fs-dir
 
${POKYGCCBINPATH}/arm-poky-linux-gnueabi-gcc -march=armv7ve -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=$SDKTARGETSYSROOT $@</pre>
 
${POKYGCCBINPATH}/arm-poky-linux-gnueabi-gcc -march=armv7ve -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=$SDKTARGETSYSROOT $@</pre>
After build, you will get result like bellow:
+
After the build, you will get something like below:
  
 
<pre>$ ls  
 
<pre>$ ls  
Line 109: Line 109:
 
CMakeCache.txt  cmake_install.cmake  ldc-src      lib      objects        objects-debug-shared
 
CMakeCache.txt  cmake_install.cmake  ldc-src      lib      objects        objects-debug-shared
 
CMakeFiles      dummy.c              ldc-src.zip  Makefile  objects-debug  objects-shared</pre>
 
CMakeFiles      dummy.c              ldc-src.zip  Makefile  objects-debug  objects-shared</pre>
The runtime lib is right sit in <code>ldc-build-runtime.tmp/lib</code> , the dir name <code>ldc-build-runtime.tmp</code> is temp, you can rename it to a <code>ldc_arm_runtime</code> or something you like.
+
The runtime lib is sitting in <code>ldc-build-runtime.tmp/lib</code>. The directory named <code>ldc-build-runtime.tmp</code> is temporary, you can rename it to a <code>ldc_arm_runtime</code> or something you like.
  
Then, we will make a simple custom command using alias for future comfortable.
+
Then, we will make a simple alias for ease of use.
  
 
<pre>alias ldcarm='ldc2 -mtriple=arm-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -L=-L${LDC2ARMRUNTIME}/lib'</pre>
 
<pre>alias ldcarm='ldc2 -mtriple=arm-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -L=-L${LDC2ARMRUNTIME}/lib'</pre>
${LDC2ARMRUNTIME} is the runtime dir you just prepared before.
+
where <code>${LDC2ARMRUNTIME}</code> is the runtime directory you prepared earlier.
  
== 5. the D hello world on ARM ! ==
+
= The D hello world on ARM =
  
<pre>$ vim test.d
+
With your favourite text editor, just like before with C, make a file called test.d. It should contain the following code:
 +
<syntaxhighlight lang="D">
 
import std.stdio;
 
import std.stdio;
 
void main()
 
void main()
 
{
 
{
     writeln(&quot;hello world from D!&quot;);
+
    float a = 3.14;
     writeln(&quot;3.14 * 2 = &quot;, 3.14*2);
+
    int b = 2;
}</pre>
+
     writeln("hello world from D!";);
 +
     writeln("3.14 * 2 = ", a * b);
 +
}
 +
</syntaxhighlight>
 +
 
 
Then compile the test program like this:
 
Then compile the test program like this:
  
 
<pre>ldcarm test.d -of testd</pre>
 
<pre>ldcarm test.d -of testd</pre>
<code>-of</code> tells ldc to output the compiled executable using <code>testd</code> filename rother than default <code>test</code>.
+
<code>-of</code> tells ldc to output the compiled executable with the file name <code>testd</code> instead of the default, which would derive the name from the input file.
  
Now, copy the testd to your arm target, type <code>./testd</code> and make a breath.
+
Now, copy the testd file you just created to your arm target, and take a deep breath before executing it.
  
 
<pre>$ ./testd
 
<pre>$ ./testd
 
hello world from D!
 
hello world from D!
 
3.14 * 2 = 6.28</pre>
 
3.14 * 2 = 6.28</pre>
Wow! we finally reach the point! Congratulations!
+
Wow! We finally reach the point! Congratulations!
  
== 6. the dub managed project ==
+
= Letting Dub manage the project =
  
For complex testing, we will use dub to init a vibe.d hello world project.
+
For testing, we will use dub to initialize a vibe.d hello world project.
  
 
<pre>$ dub init vibeex --type=vibe.d
 
<pre>$ dub init vibeex --type=vibe.d
Line 149: Line 154:
 
Copyright string [Copyright © 2018, dbh]:  
 
Copyright string [Copyright © 2018, dbh]:  
 
Add dependency (leave empty to skip) []:  
 
Add dependency (leave empty to skip) []:  
Successfully created an empty project in '/media/Devel/Changhong/IOT/imx6u-devel/project/vibeex'.
+
Successfully created an empty project in '/media/Devel/IOT/imx6u-devel/project/vibeex'.
 
Package successfully created in vibeex</pre>
 
Package successfully created in vibeex</pre>
The dub tool initialized a vibe.d example project, but the vibe.d dependency version is outdated. we need to tweak it.
+
The dub tool initialized a vibe.d example project, but the vibe.d dependency version is outdated. we need to tweak it. Edit the file <code>vibeex/dub.json</code> with your favourite text editor, changing the dependency version from <code>0.7.30</code> to <code>0.8.3</code>(This is the latest version when writing this tutorial). The file should look something like this:
 
+
<syntaxhighlight lang="json">
<pre>$ vim vibeex/dub.json
 
 
{
 
{
     &quot;name&quot;: &quot;vibeex&quot;,
+
     "name": "vibeex",
     &quot;authors&quot;: [
+
     "authors": [
         &quot;dbh&quot;
+
         "dbh"
 
     ],
 
     ],
     &quot;dependencies&quot;: {
+
     "dependencies": {
         &quot;vibe-d&quot;: &quot;~&gt;0.7.30&quot;
+
         "vibe-d": "~>0.8.30"
 
     },
 
     },
     &quot;description&quot;: &quot;A simple vibe.d server application.&quot;,
+
     "description": "A simple vibe.d server application.",
     &quot;copyright&quot;: &quot;Copyright © 2018, dbh&quot;,
+
     "copyright": "Copyright © 2018, dbh",
     &quot;license&quot;: &quot;proprietary&quot;
+
     "license": "proprietary"
}</pre>
+
}
update 0.7.30 to 0.8.3.
+
</syntaxhighlight>
  
when building using dub, the alias method setup ldc-arm will not work. we need to turn this into a shell script adapter:
+
When building using dub, the alias we created (ldc-arm) will not work. We need to turn this into a shell script adapter:
  
<pre>#!/bin/sh
+
<syntaxhighlight lang="sh">#!/bin/sh
 +
LDC2ARMRUNTIME=/media/Devel/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime
 +
ldc2 -mtriple=arm-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -L=-L${LDC2ARMRUNTIME}/lib $@
 +
</syntaxhighlight>
 +
''For the yocto toolchain, you will have an adapter like below:''
  
LDC2ARMRUNTIME=/media/Devel/Changhong/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime
+
<syntaxhighlight lang="sh">
ldc2 -mtriple=arm-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -L=-L${LDC2ARMRUNTIME}/lib $@</pre>
+
$ cat ldc-yocto-arm
''for yocto toolchain, you will have an adapter like bellow:''
+
#!/bin/sh
  
<pre>$ cat ldc-yocto-arm
+
LDC2ARMRUNTIME=/media/Devel/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime
#!/bin/sh
+
ARMTARGETROOT=/media/Devel/IOT/imx6u-devel/toolchain/yocto/targetroot
 +
ldc2 -mtriple=arm-poky-linux-gnueabi -float-abi=hard -mcpu=cortex-a7 -gcc=arm-poky-linux-gnueabi-gcc -L=-L${LDC2ARMRUNTIME}/lib -Xcc=--sysroot=$ARMTARGETROOT $@
 +
</syntaxhighlight>
  
LDC2ARMRUNTIME=/media/Devel/Changhong/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime
 
ARMTARGETROOT=/media/Devel/Changhong/IOT/imx6u-devel/toolchain/yocto/targetroot
 
ldc2 -mtriple=arm-poky-linux-gnueabi -float-abi=hard -mcpu=cortex-a7 -gcc=arm-poky-linux-gnueabi-gcc -L=-L${LDC2ARMRUNTIME}/lib -Xcc=--sysroot=$ARMTARGETROOT $@</pre>
 
'''NOTE''': if your toolchain does not have some library for the arm target present, dub will fail like this:
 
  
 
Then compile the project using commands below:
 
Then compile the project using commands below:
Line 208: Line 214:
 
vibeex ~master: building configuration &quot;application&quot;...</pre>
 
vibeex ~master: building configuration &quot;application&quot;...</pre>
  
however, sometime we would have no luck, and got something like this:
+
However, sometimes we just get unlucky. If your toolchain does not have some library for the ARM target present, dub will fail, with errors like this:
  
 
<pre>$ dub build --compiler=ldc-arm
 
<pre>$ dub build --compiler=ldc-arm
Line 238: Line 244:
 
ldc-arm failed with exit code 1.
 
ldc-arm failed with exit code 1.
 
</pre>
 
</pre>
This means that all library path does not includes libssl, libcrypto, libz. You need to find or build a toolchain with extra lib built-in, or, just cross-compiling the desired c library and install to your toolchain sysroot path.
 
  
Let’s run the vibe.d example on arm target:
+
This means that the library path does not include <code>libssl</code>, <code>libcrypto</code> and <code>libz</code>. You need to find or build a toolchain with the extra libraries built-in or just cross-compile the desired C library and install it to your toolchain's <code>sysroot</code> path.
 +
(The concept of toolchain sysroot can be referenced by [https://elinux.org/images/1/15/Anatomy_of_Cross-Compilation_Toolchains.pdf elinux.org Anatomy_of_Cross-Compilation_Toolchains-Page 21])
 +
 
 +
Let’s run the vibe.d example on ARM target:
  
 
<pre>root@armhost:~# ls -lh
 
<pre>root@armhost:~# ls -lh
Line 252: Line 260:
 
Vibe was run as root, and no user/group has been specified for privilege lowering. Running with full permissions.</pre>
 
Vibe was run as root, and no user/group has been specified for privilege lowering. Running with full permissions.</pre>
  
 
+
Yeah! we finally did it!
Yeah! we finally did that!
 

Latest revision as of 07:04, 12 May 2018

Introduction

D is a great systems-programming language with clean syntax and great modeling power. Traditionally, Linux-based embedded devices are programmed using C or C++. Python and Java are more popular today, but fail due to large runtime size and resource requirements. Programming in D will be comfortable, since it is like a dynamic language but has native code performance, and has full ABI compatibility with C, making it very suitable as a “Linux systems-programming language”.

This simple tutorial will introduce you to programming in D on Embedded ARM Linux step by step.

Preparing your ARM GCC toolchain

The very first thing is to prepare your toolchain. Although clang has supported ARM for a very long time, GCC is still the premier choice for compiling to ARM Linux systems.

If you are a ARM Linux application programmer, you probably already have ARM GCC installed, and tested some "hello world" programs on your ARM board.

However, if you haven't installed ARM GCC, it is quite easy.

Arch Linux:

$ pacman -S arm-linux-gnueabihf-gcc

Debian:

$ apt install gcc-arm-linux-gnueabihf

Fedora:

$ dnf install arm-linux-gnueabihf-{binutils,gcc,glibc}

For a Fedora/Redhat-based OS like CentOS, if you find the armhf toolchain is not quite easy to install, you can just install the arm target instead of armhf, by running $ yum install gcc-arm-linux-gnu , and then use arm-linux-gcc rather than arm-linux-gnueabihf-gcc.

After the toolchain is installed, you can simply test to make sure that ARM GCC is generating ARM executables properly. Create a file, test.c, that contains the following, using your favorite text editor:

#include <stdio.h>
int main()
{
    float a = 3.14;
    int b = 2;
    printf("hello world! 3.14 * 2 = %f\n";, a*b);
    return 0;
}

Then compile using:

$ arm-linux-gnueabihf-gcc test.c -o test

Copy the compiled program to your ARM target board:

$ scp test user@armboard:/home/user

On the ARM target, run the test executable:

ARM $ ./test
hello world! 3.14*2 = 6.280000

If your ARM GCC toolchain was compiled by yocto, you may need to call GCC using $CC after you sourced the environment setup script.

Installing LDC2

D has 3 compiler implementations. Currently, only GDC and LDC support ARM and CPUs other than x86.

GDC support for arm can be enabled if you are compiling your toolchain by yourself, by passing –enable-languages=c,d,cpp when configuring (i.e. ./configure -–enable-languages=c,d,cpp. Or you can download an ARM enabled GDC build from the GDC project home page.

As of the writing of this tutorial, the latest GDC release (6.3.0) is not as good as LDC2, for ARM targets.

LDC has better support for ARM targets, and is more actively developed that GDC. And, as it is based on LLVM, LDC does not need a cross-compiling toolchain prepared for an individual target.

The recommended way to install LDC is by using the install script (works on POSIX and POSIX like systems):

curl https://dlang.org/install.sh | bash -s ldc

You can also search your operating system's packages for the LDC package.

Verify that LDC was installed correctly:

$ ldc2 --version
LDC - the LLVM D compiler (1.8.0):
  based on DMD v2.078.3 and LLVM 5.0.1
  built with LDC - the LLVM D compiler (1.8.0)
  Default target: x86_64-unknown-linux-gnu
  Host CPU: skylake
  http://dlang.org - http://wiki.dlang.org/LDC

  Registered Targets:
    aarch64    - AArch64 (little endian)
    aarch64_be - AArch64 (big endian)
    arm        - ARM
    arm64      - ARM64 (little endian)
  ......

As you can see from the ldc2 version output, arm64 is a registered target, but it’s not completely supported right now. It’s a little bit disappointing, but don’t worry, we have ARM well supported when the target is gcc+glibc+linux. And ARM and ARMhf executable can directly run on an arm64 target when arm64 has multilib support.

Compiling the D runtime for the ARM target

Before doing this, you need to make sure CMake is installed on your host. If not, please install it. Then, just type the commands below to compile your D runtime:

$ CC=arm-linux-gnueabihf-gcc ldc-build-runtime --dFlags="-w;-mtriple=arm-linux-gnueabihf" --targetSystem="Linux;UNIX"
-------------------------------------------------------------------
Creating build directory: ldc-build-runtime.tmp
Downloading LDC source archive: https://github.com/ldc-developers/ldc/releases/download/v1.8.0/ldc-1.8.0-src.zip
Invoking: ["cmake", "-DLDC_EXE_FULL=/media/Devel/IOT/imx6u-devel/toolchain/ldc2-1.8.0-linux-x86_64/bin/ldc2", "-DD_VERSION=2", "-DDMDFE_MINOR_VERSION=0", "-DDMDFE_PATCH_VERSION=78", "-DLDC_TARGET_PRESET=", "-DTARGET_SYSTEM=Linux;UNIX", "-DD_FLAGS=-w;-mtriple=arm-linux-gnueabihf", "-DRT_CFLAGS=", "-DLD_FLAGS=", "/media/Devel/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime/ldc-build-runtime.tmp/ldc-src/runtime"]
......

The ldc-build-runtime tool will download ldc compiler source and cross-compile the runtime.

NOTE: yocto-built toolchain needs to use a different manner:

CC=arm-poky-linux-gnueabi-gcc ldc-build-runtime --dFlags="-w;-mtriple=arm-poky-linux-gnueabi;-float-abi=hard;-mcpu=cortex-a7" --targetSystem="Linux;UNIX"

for yocto toolchain the above arm-poky-linux-gnueabi-gcc is just a shell script adapter.

export SDKTARGETSYSROOT=your-target-root-fs-dir
${POKYGCCBINPATH}/arm-poky-linux-gnueabi-gcc -march=armv7ve -mfpu=neon  -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=$SDKTARGETSYSROOT $@

After the build, you will get something like below:

$ ls 
ldc-build-runtime.tmp
$ ls ldc-build-runtime.tmp/
CMakeCache.txt  cmake_install.cmake  ldc-src      lib       objects        objects-debug-shared
CMakeFiles      dummy.c              ldc-src.zip  Makefile  objects-debug  objects-shared

The runtime lib is sitting in ldc-build-runtime.tmp/lib. The directory named ldc-build-runtime.tmp is temporary, you can rename it to a ldc_arm_runtime or something you like.

Then, we will make a simple alias for ease of use.

alias ldcarm='ldc2 -mtriple=arm-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -L=-L${LDC2ARMRUNTIME}/lib'

where ${LDC2ARMRUNTIME} is the runtime directory you prepared earlier.

The D hello world on ARM

With your favourite text editor, just like before with C, make a file called test.d. It should contain the following code:

import std.stdio;
void main()
{
    float a = 3.14;
    int b = 2;
    writeln("hello world from D!";);
    writeln("3.14 * 2 = ", a * b);
}

Then compile the test program like this:

ldcarm test.d -of testd

-of tells ldc to output the compiled executable with the file name testd instead of the default, which would derive the name from the input file.

Now, copy the testd file you just created to your arm target, and take a deep breath before executing it.

$ ./testd
hello world from D!
3.14 * 2 = 6.28

Wow! We finally reach the point! Congratulations!

Letting Dub manage the project

For testing, we will use dub to initialize a vibe.d hello world project.

$ dub init vibeex --type=vibe.d
Package recipe format (sdl/json) [json]: 
Name [vibeex]: 
Description [A simple vibe.d server application.]: 
Author name [dbh]: 
License [proprietary]: 
Copyright string [Copyright © 2018, dbh]: 
Add dependency (leave empty to skip) []: 
Successfully created an empty project in '/media/Devel/IOT/imx6u-devel/project/vibeex'.
Package successfully created in vibeex

The dub tool initialized a vibe.d example project, but the vibe.d dependency version is outdated. we need to tweak it. Edit the file vibeex/dub.json with your favourite text editor, changing the dependency version from 0.7.30 to 0.8.3(This is the latest version when writing this tutorial). The file should look something like this:

{
    "name": "vibeex",
    "authors": [
        "dbh"
    ],
    "dependencies": {
        "vibe-d": "~>0.8.30"
    },
    "description": "A simple vibe.d server application.",
    "copyright": "Copyright © 2018, dbh",
    "license": "proprietary"
}

When building using dub, the alias we created (ldc-arm) will not work. We need to turn this into a shell script adapter:

#!/bin/sh
LDC2ARMRUNTIME=/media/Devel/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime
ldc2 -mtriple=arm-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -L=-L${LDC2ARMRUNTIME}/lib $@

For the yocto toolchain, you will have an adapter like below:

$ cat ldc-yocto-arm
#!/bin/sh

LDC2ARMRUNTIME=/media/Devel/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime
ARMTARGETROOT=/media/Devel/IOT/imx6u-devel/toolchain/yocto/targetroot
ldc2 -mtriple=arm-poky-linux-gnueabi -float-abi=hard -mcpu=cortex-a7 -gcc=arm-poky-linux-gnueabi-gcc -L=-L${LDC2ARMRUNTIME}/lib -Xcc=--sysroot=$ARMTARGETROOT $@


Then compile the project using commands below:

$ dub build --compiler=ldc-arm
Performing "debug" build using ldc-arm for arm, arm_hardfloat.
taggedalgebraic 0.10.9: building configuration "library"...
eventcore 0.8.30: building configuration "epoll"...
stdx-allocator 2.77.0: building configuration "library"...
vibe-core 1.4.0: building configuration "epoll"...
vibe-d:utils 0.8.3: building configuration "library"...
vibe-d:data 0.8.3: building configuration "library"...
vibe-d:crypto 0.8.3: building configuration "library"...
diet-ng 1.4.5: building configuration "library"...
vibe-d:stream 0.8.3: building configuration "library"...
vibe-d:textfilter 0.8.3: building configuration "library"...
vibe-d:inet 0.8.3: building configuration "library"...
vibe-d:tls 0.8.3: building configuration "openssl"...
vibe-d:http 0.8.3: building configuration "library"...
vibe-d:mail 0.8.3: building configuration "library"...
vibe-d:mongodb 0.8.3: building configuration "library"...
vibe-d:redis 0.8.3: building configuration "library"...
vibe-d:web 0.8.3: building configuration "library"...
vibe-d 0.8.3: building configuration "vibe-core"...
vibeex ~master: building configuration "application"...

However, sometimes we just get unlucky. If your toolchain does not have some library for the ARM target present, dub will fail, with errors like this:

$ dub build --compiler=ldc-arm
Performing "debug" build using ldc-arm for arm, arm_hardfloat.
taggedalgebraic 0.10.9: building configuration "library"...
eventcore 0.8.30: building configuration "epoll"...
stdx-allocator 2.77.0: building configuration "library"...
vibe-core 1.4.0: building configuration "epoll"...
vibe-d:utils 0.8.3: building configuration "library"...
vibe-d:data 0.8.3: building configuration "library"...
vibe-d:crypto 0.8.3: building configuration "library"...
diet-ng 1.4.5: building configuration "library"...
vibe-d:stream 0.8.3: building configuration "library"...
vibe-d:textfilter 0.8.3: building configuration "library"...
vibe-d:inet 0.8.3: building configuration "library"...
vibe-d:tls 0.8.3: building configuration "openssl"...
vibe-d:http 0.8.3: building configuration "library"...
vibe-d:mail 0.8.3: building configuration "library"...
vibe-d:mongodb 0.8.3: building configuration "library"...
vibe-d:redis 0.8.3: building configuration "library"...
vibe-d:web 0.8.3: building configuration "library"...
vibe-d 0.8.3: building configuration "vibe-core"...
hellovibe ~master: building configuration "application"...
/usr/lib/gcc-cross/arm-linux-gnueabihf/6/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lssl
/usr/lib/gcc-cross/arm-linux-gnueabihf/6/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lcrypto
/usr/lib/gcc-cross/arm-linux-gnueabihf/6/../../../../arm-linux-gnueabihf/bin/ld: cannot find -lz
collect2: error: ld returned 1 exit status
Error: /usr/bin/arm-linux-gnueabihf-gcc failed with status: 1
ldc-arm failed with exit code 1.

This means that the library path does not include libssl, libcrypto and libz. You need to find or build a toolchain with the extra libraries built-in or just cross-compile the desired C library and install it to your toolchain's sysroot path. (The concept of toolchain sysroot can be referenced by elinux.org Anatomy_of_Cross-Compilation_Toolchains-Page 21)

Let’s run the vibe.d example on ARM target:

root@armhost:~# ls -lh
total 25M
-rwxr-xr-x 1 root root  24M Mar 12 05:53 vibeex

root@armhost:~# ./vibeex 
[main(----) INF] Listening for requests on http://[::1]:8080/
[main(----) INF] Listening for requests on http://127.0.0.1:8080/
[main(----) INF] Please open http://127.0.0.1:8080/ in your browser.
Vibe was run as root, and no user/group has been specified for privilege lowering. Running with full permissions.

Yeah! we finally did it!