Programming in D tutorial on Embedded Linux ARM devices

From D Wiki
Revision as of 09:34, 14 March 2018 by Dangbinghoo (talk | contribs) (6. the dub managed project)
Jump to: navigation, search

Programming in D tutorial on Embedded Linux ARM devices

1. introductions

D is a great system-programming language with clean syntax and modeling power. Traditionally, Linux based embedded devices is programmed using C or C++, even python is more populator today, it fails about its huge runtime size and resources requirements, and also java and many others. D programming will be comfortable like a dynamic language but has C native code running performance, and has full compatibility with C, making it very suitable as a “Linux system programming language”.

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

2. preparing your GCC ARM toolchain

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 would probably already have ARM GCC installed, and did a compiling and tested for your target board.

However, install ARM GCC is quite easy, the following is the instructions to install arm toolchain

ArchLinux : $ pacman -S arm-linux-gnueabihf-gcc
Debain    : $ apt install gcc-arm-linux-gnueabihf

after the toolchain installed, you need to make a simple test make sure that ARM GCC is generating arm executable properly.

$ vim test.c
#include <stdio.h>
int main()
{
    float a = 3.14;
    int b = 2;
    printf("hello world! 3.14 * 2 = %f\n", a*b);
    return 0;
}
$ arm-linux-gnueabihf-gcc test.c -o test
$ scp test user@armboard:/home/user

# on ARM target run the test executable.
ARM $ ./test
hello world! 3.14*2 = 6.280000

also, if your arm gcc is a toolchain compiled by yocto, you may calling GCC using $CC after you sourced the environment setup script.

3. install the LDC2 D compiler

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

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.

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.

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.

So, what you need to download is right the LDC2 compiler for your x86 host.

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

For ArchLinux or Gentoo Linux user, you can install the almost latest LDC compiler just using pacman or emerge

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:

mkdir your-devel-toolchain-dir
tar xf ldc2-x.y.z-linux-x86_64.tar.xz -C your-devel-toolchain-dir

The compiler will be installed to a dir named ldc2-1.8.0-linux-x86_64 which has it’s own bin , etc , lib sub directories. As it’s a standalone distribution, you need to add ldc2-1.8.0-linux-x86_64/bin to your PATH env.

export PATH=${your-devel-toolchain-dir}/ldc2-1.8.0-linux-x86_64/bin:${PATH}

Now, you could invoke ldc2 directly:

$ 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 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.

4. compiling the D runtime for ARM target.

before doing this, you need to make sure cmake is installed on your host.

sudo apt install cmake

Then, just type the commands bellow to make your D arm 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/Changhong/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/Changhong/IOT/imx6u-devel/toolchain/ldc2Armhf_runtime/ldc-build-runtime.tmp/ldc-src/runtime"]
......

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

NOTE: yocto built toolchain need 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 build, you will get result like bellow:

$ 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 right sit in ldc-build-runtime.tmp/lib , the dir name ldc-build-runtime.tmp is temp, you can rename it to a ldc_arm_runtime or something you like.

Then, we will make a simple custom command using alias for future comfortable.

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

${LDC2ARMRUNTIME} is the runtime dir you just prepared before.

5. the D hello world on ARM !

$ vim test.d
import std.stdio;
void main()
{
    writeln("hello world from D!");
    writeln("3.14 * 2 = ", 3.14*2);
}

Then compile the test program like this:

ldcarm test.d -of testd

-of tells ldc to output the compiled executable using testd filename rother than default test.

Now, copy the testd to your arm target, type ./testd and make a breath.

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

Wow! we finally reach the point! Congratulations!

6. the dub managed project

For complex testing, we will use dub to init 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/Changhong/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.

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

update 0.7.30 to 0.8.3.

when building using dub, the alias method setup ldc-arm will not work. we need to turn this into a shell script adapter:

#!/bin/sh

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

for yocto toolchain, you will have an adapter like bellow:

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

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 $@

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:

$ 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, sometime we would have no luck, and got something 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 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:

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

root@chiot:~# ./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 that!