www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.ldc - Building for ARM 32-bit embedded

reply Bonsaipanda <jukka.korhonen gmail.com> writes:
Hey guys, thought I'd throw some notes from testing out D for the 
first time. I'm a complete n00b when it comes to cross-platform 
buildchains and barely understand what crosslinking means and I 
thought I'd just create a small hello world test app and try to 
build it for amrhf to see if D would work for an app idea I have 
had for quite a while.

Now, for comparison, same hello world test app (when running 
Ubuntu 24.04) build using C is >>

```
arm-linux-gnueabihf-gcc test.c test_armhf
```
.. and out pops a 32-bit ARM executable.

So, in my head, I thought that there would be some kind of 
shorthand like this >>
```
ldc2 -armhf test.d
```
.. and that would give me a standalone executable, ready to be 
deployed on the ARM box and run.

The documentation says that you have to jump a bit through hoops 
to get LLVM working if you're not building on the platform that 
you're targeting, but the section in the documentation was almost 
incomprehensible to me - not that I'm stupid, I'm just not 
familiar with all that's required when linking stuff together, 
never done anything like that, never heard of any of these 
technologies before.

Apparently, if I want a static build (the target platform will 
not have anything available, optimal end-result is a standalone 
executable), I would need a pre-built armhf binary of the D 
runtime and Phobos, but I didn't understand why would I need 
those. When working with Vala, this was the thing that put me off 
- no matter how I would build the executable, on the embedded 
platform it would always nag about not finding all it's libraries 
and I couldn't figure out a way to make stuff static (there is a 
way, folks use it for embedded dev).

So I hopped on over to Raspberry Pi and made the test build 
there. Out pops a test.o which I have no idea what to do with. I 
know it's a 'shared object' but why would I want something like 
this. Testing out different build options I managed to get an 
executable out. Which then nags that it's in wrong format, it 
doesn't understand what to do, it wants to run for president 
etc., head scratching x 9000.

Diving into this forum, I found out that there's a problem with 
the linker - it connects stuff in the wrong order.

So the final build command, after ~5 hours of headscrathing (and 
trying ChatGPT to help out on understanding everything) is in the 
form of >>
```
ldc2 -mtriple=armv6-linux-gnueabihf -c test.d && 
arm-linux-gnueabihf-gcc test.o 
/usr/lib/arm-linux-gnueabihf/libphobos2-ldc.a 
/usr/lib/arm-linux-gnueabihf/libdruntime-ldc.a -lm -lz -o 
test_armhf
```
Yipe.

I mean, when you break it down, it's completely logical, but holy 
skagsack minion, that's verbose and I have a feeling I'm doing 
this completely wrong. Also, doing this on the Raspberry Pi is 
super cumbersome and slow.

And I don't mean this post to be any sort of rant, I'm just 
surprised on how the whole toolchain was so tricky to understand 
and set up. Was really hoping for the shorthand of sorts that 
would 'just work' on Ubuntu (or Windows), since LLVM itself is 
pretty agnostic about the platforms, or so I've understood.

The D language itself seems to fit perfectly for the stuff I had 
in mind, so I'm excited to dive deeper into it, but I have no 
idea if I'm able to build more complex code (like drawing 
directly into the framebuffer, some DSP stuff for ALSA).
Oct 11
next sibling parent reply kinke <noone nowhere.com> writes:
Hey! Seems like you (and ChatGPT) unfortunately haven't found 
https://wiki.dlang.org/Cross-compiling_with_LDC, it would 
hopefully have made the journey a bit easier. But I'm sure you 
learned a lot along the way! :)

Just to show that cross-compiling and -linking can work without 
too much effort too, with a vanilla LDC config and an already 
available C cross-toolchain (admittedly cheating via `-betterC`, 
which doesn't require druntime+Phobos library builds - but we 
have an `ldc-build-runtime` helper to make that easier, see the 
linked Wiki page), also on Ubuntu 24 (but targeting AArch64, as I 
had that cross-toolchain on disk already):
```
$ cat hello.d
import core.stdc.stdio;

extern(C) void main() {
     puts("Hello world");
}
$ ldc2 -mtriple=aarch64-linux-gnu -gcc=aarch64-linux-gnu-gcc 
-betterC hello.d
$ file hello
hello: ELF 64-bit LSB pie executable, ARM aarch64, version 1 
(SYSV), dynamically linked, interpreter 
/lib/ld-linux-aarch64.so.1, 
BuildID[sha1]=d78ca80f36c54f0b83565d7adcf29b6161567a2f, for 
GNU/Linux 3.7.0, not stripped
```

 The documentation says that you have to jump a bit through 
 hoops to get LLVM working if you're not building on the 
 platform that you're targeting
Which documentation are you referring to?
Oct 11
parent reply Bonsaipanda <jukka.korhonen gmail.com> writes:
On Friday, 11 October 2024 at 22:31:21 UTC, kinke wrote:
 Which documentation are you referring to?
 https://wiki.dlang.org/Cross-compiling_with_LDC
That exact page, section "Cross-linking executables and shared libraries". Coming from completely outside D language (and never having done any cross-compiling), that whole section is very confusing and took a while to digest. I was just so used to everything being super straightforward with C. Was scratching my head as to why it was done how it was done with D. I would probably use the kind of shorthand I was talking about as the default and if the user needs to tweak their chain, then point to a conf. CG was pretty good at explaining what is happening under the hood and what is expected from me when setting up the whole toolchain, which is why I fired up my Raspberry Pi just to get a build out of the oven, seemed much more straightforward. And yes, it's not a lot of effort, once it's up and running. But you really *really* need to have a hyperdeep understanding of buildsystems and toolchains to get it running. For a beginner like me, it was an interesting experience.
Oct 11
parent Bonsaipanda <jukka.korhonen gmail.com> writes:
As a quick example, this is the output when building for armhf 
from Ubuntu >>
```
$ ldc2 -mtriple=armv6-linux-gnueabihf 
-gcc=arm-linux-gnueabihf-gcc -betterC test.d
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(4362): Error: 
none of the overloads of template 
`std.stdio.File.LockingTextWriter.put` are callable using 
argument types `!()(string)`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3287):        
Candidates are: `put(A)(scope A writeme)`
   with `A = string`
   must satisfy one of the following constraints:
`       isSomeChar!(ElementType!A)
        is(ElementType!A : const(ubyte))`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3317):        
                 `put(C)(scope C c)`
   with `C = string`
   must satisfy one of the following constraints:
`       isSomeChar!C
        is(C : const(ubyte))`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3338): Error: 
none of the overloads of template `std.utf.decodeFront` are 
callable using argument types `!()(char[])`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(1245):        
Candidates are: `decodeFront(Flag useReplacementDchar = 
No.useReplacementDchar, S)(ref S str, out size_t numCodeUnits)`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(1285):          
               `decodeFront(Flag useReplacementDchar = 
No.useReplacementDchar, S)(ref scope S str, out size_t 
numCodeUnits)`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(1314):          
               `decodeFront(Flag useReplacementDchar = 
No.useReplacementDchar, S)(ref S str)`
   with `useReplacementDchar = Flag.no,
        S = char[]`
   must satisfy the following constraint:
`       isInputRange!S`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(2311): Error: 
cannot use `throw` statements with -betterC
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(2514): Error: 
template instance `std.utf._utfException!Flag.no` error 
instantiating
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3340):        
instantiated from here: `encode!Flag.no`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(4364):        
instantiated from here: `put!char`
test.d(3):        instantiated from here: `writeln!string`
```

If I didn't have CG to ask from, I wouldn't have had any idea 
what was happening and why are x86-64 components being called. I 
would even argue that it's LDC's job to make sure stuff, that the 
program is dependent on, gets built at the same time for the 
target. And I still don't understand why (core components?) like 
D runtime and Phobos are external libraries. But, such is life.
Oct 11
prev sibling parent reply f <f abc.com> writes:
On Friday, 11 October 2024 at 21:02:04 UTC, Bonsaipanda wrote:
 Hey guys, thought I'd throw some notes from testing out D for 
 the first time. I'm a complete n00b when it comes to 
 cross-platform buildchains and barely understand what 
 crosslinking means and I thought I'd just create a small hello 
 world test app and try to build it for amrhf to see if D would 
 work for an app idea I have had for quite a while.

 [...]
maybe this can help, an alternative, https://github.com/trikko/ldc2-rpi-build/releases/tag/v1.31.0 modify as you need, compile the ldc using buildkit, compile your project using docker. i am a n00b too. just may be..
Oct 14
parent Collin Daugherty <wilberthayes79 gmail.com> writes:
On Monday, 14 October 2024 at 13:14:58 UTC, f wrote:
 On Friday, 11 October 2024 at 21:02:04 UTC, Bonsaipanda wrote:
 Hey guys, thought I'd throw some notes from testing out D for 
 the first time. I'm a complete n00b when it comes to 
 cross-platform buildchains and barely understand what 
 crosslinking means and I thought I'd just create a small hello 
 world test app and try to build it for amrhf to see if D would 
 work for an app idea I have had for quite a while.

 [...]
maybe this can help, an alternative, https://github.com/trikko/ldc2-rpi-build/releases/tag/v1.31.0 https://basketballstars-game.io modify as you need, compile the ldc using buildkit, compile your project using docker. i am a n00b too. just may be..
Great approach! Using the prebuilt LDC for Raspberry Pi and Docker sounds like a practical way to handle cross-compilation. Docker ensures consistency, and buildkit simplifies the process. Perfect for a beginner-friendly environment—I'll give it a try, thanks!
Nov 15