Good day, Habr!
Today I want to share my development experience for minicomputers on linux (RPI, BBB and others) in the D programming language. Under the cat, complete instructions on how to do it without pain. Well, or almost ... =)
When at work there was a task to write a monitoring system for ARM, even being a big fan of D, I wondered whether it was worth taking it as the main tool. In general, I am not a whimsical person, and D has been around for a long time, so I thought that it was worth a try and ... not everything is so straightforward. On the one hand, there were no special problems (except for one that was not completely clear, which went away with the arrival of the new version of the compiler), on the other hand, people who develop ARM can constantly find that the toolkit is not completely ready for the word. You decide.
I can advise Visual Studio Code
with the plugin D Programming Language
from Comrade. WebFreak (Jan Jurzitza). In the settings you can set the Beta Stream
setting to always have the latest version of serve-d
. The plugin itself installs the necessary software.
In general, it turned out quite confused (in comparison with the usual project on D), but, as it seems to me, it is quite flexible and convenient.
. ├── arm-lib/ | ├── libcrypto.a | ├── libssl.a | └── libz.a ├── docker-ctx/ | ├── Dockerfile | └── entry.sh ├── source | └── app.d ├── .gitignore ├── build-docker ├── ddb ├── dub.sdl ├── ldc └── makefile
arm-lib
- the libraries necessary for the operation of our application (compiled under the arm)
docker-ctx
- context for building a docker image
entry.sh
- will perform some actions on each launch of the container, about which later
dub.sdl
- project file in D, allows you to include third-party libraries and much more
build-docker
- container build script (essentially 1 line, but still)
ddb
- docker D builder - container launch script (just one line, but actually more convenient)
ldc
- a script that allows to call ldc with all the necessary parameters
makefile
- contains build recipes for arm and x86 and additional actions
source/app.d
- project sources
A few words about arm-lib
.
There are files needed for the vibe to work. Adding binary files to the repository is a bad form. But here to simplify your life it is easier to do just that. You can add them to the inside of the container, but then, to completely form the container assembly recipe, you will need to store the arm-lib
folder in dockert-ctx
. The taste and color ...
./ddb make
ddb
runs container, executes entry.sh
scriptentry.sh
slightly configures the dub
so that the one inside the container will use the folder for the libraries, which will be located in the current directory, which will allow you not to reload and reuse the libraries used in the project when you restart the assemblyentry.sh
ends up passing control to the input command ( make
in our case)make
in turn reads makefile
makefile
, the dub
call line is formeddub
ldc
script from the current directory is passed as a compiler and the environment variables are setmakefile
, which, if they are missing, are built by the ldc-build-runtime
programldc
script and to the dub.sdl
parametersSince we will write under RPI3, we choose the image of the debian:stretch-slim
base system, there gcc-arm-linux-gnueabihf
uses the same version of glibc
as the official raspbian distribution (there was a problem with fedora, where the crosslink compiler maintainer used too fresh version of glibc
).
FROM debian:stretch-slim RUN apt-get update && apt-get install -y \ make cmake bash p7zip-full tar wget gpg xz-utils \ gcc-arm-linux-gnueabihf ca-certificates \ && apt-get autoremove -y && apt-get clean ARG ldcver=1.11.0 RUN wget -O /root/ldc.tar.xz https://github.com/ldc-developers/ldc/releases/download/v$ldcver/ldc2-$ldcver-linux-x86_64.tar.xz \ && tar xf /root/ldc.tar.xz -C /root/ && rm /root/ldc.tar.xz ENV PATH "/root/ldc2-$ldcver-linux-x86_64/bin:$PATH" ADD entry.sh /entry.sh RUN chmod +x /entry.sh WORKDIR /workdir ENTRYPOINT [ "/entry.sh" ]
The ldc
compiler ldc
from github
, where it is compiled based on the current llvm
.
#!/bin/bash if [ ! -d ".dpack" ]; then mkdir .dpack fi ln -s $(pwd)/.dpack /root/.dub exec $@
Everything is simple: if there is no .dpack
folder, then create, use .dpack
to create a symbolic link to /root/.dub
.
This will allow you to store the packages downloaded by the dub
in the project folder.
These are three simple single-line files. Two of them are optional, but convenient, but written for linux (bash). For windows, you will have to create similar files in the local script or just run it by hand.
build-docker
starts the container build (it is called once, only for linux):
#!/bin/bash docker build -t dcross docker-ctx
ddb
runs the container for the build and passes the parameters (only for linux):
#!/bin/bash docker run -v `pwd`:/workdir -t --rm dcross $@
Note that the dcross
container name is dcross
(the name itself is not essential, but it must be the same in both files) and the current command in the /workdir
(the directory is specified as WORKDIR
in the Dockerfile
) uses the pwd
(in win, it seems you need to use %CD%
).
ldc
runs ldc
, oddly enough, while using environment variables (only linux, but runs in a container, so it does not require a change for building under win):
#!/bin/bash $LDC $LDC_FLAGS $@
For example, it will be quite simple:
name "chw" description "Cross Hello World" license "MIT" targetType "executable" targetPath "$TP" dependency "vibe-d" version="~>0.8.4" dependency "vibe-d:tls" version="~>0.8.4" subConfiguration "vibe-d:tls" "openssl-1.1"
targetPath
is taken from the environment variable because dub
some fields of the build recipe cannot specify by platform (for example, lflags "-L.libs" platform="arm"
will add a flag to the linker only when building under arm).
And here is the most interesting. Essentially, make
not used for the build as such, it calls for this dub
, and the dub
itself takes care of what needs to be rebuilt and what is not. But with the help of the makefile
, all the necessary environment variables are formed, additional commands are executed in more complex cases (building C libraries, packing update files, etc.).
The content of the makefile
larger than the rest:
# arm arch = arm # target path -- , TP = build-$(arch) LDC_DFLAGS = -mtriple=armv7l-linux-gnueabihf -disable-inlining -mcpu=cortex-a8 # EMPTY := SPACE :=$(EMPTY) $(EMPTY) LDC_BRT_DFLAGS = $(subst $(SPACE),;,$(LDC_DFLAGS)) ifeq ($(force), y) # # , .. dub FORCE = --force else FORCE = endif ifeq ($(release), y) BUILD_TYPE = --build=release else BUILD_TYPE = endif DUB_FLAGS = build --parallel --compiler=./ldc $(FORCE) $(BUILD_TYPE) $(info DUB_FLAGS: $(DUB_FLAGS)) # LDC = ldc2 LDC_BRT = ldc-build-runtime # ldc, runtime ARM LDC_RT_DIR = .ldc-rt # gcc GCC = arm-linux-gnueabihf-gcc ifeq ($(arch), x86) LDC_FLAGS = else ifeq ($(arch), arm) LDC_FLAGS = $(LDC_DFLAGS) -LL./$(LDC_RT_DIR)/lib -LL./arm-lib -gcc=$(GCC) else $(error unknown arch) endif DUB = TP=$(TP) LDC=$(LDC) LDC_FLAGS="$(LDC_FLAGS)" dub $(DUB_FLAGS) # .PHONY: all clean rtlibs stat # all: rtlibs $(DUB) DRT_LIBS=$(addprefix $(LDC_RT_DIR)/lib/, libdruntime-ldc.a libdruntime-ldc-debug.a libphobos2-ldc.a libphobos2-ldc-debug.a) $(DRT_LIBS): CC=$(GCC) $(LDC_BRT) -j8 --dFlags="$(LDC_BRT_DFLAGS)" --buildDir=$(LDC_RT_DIR) \ --targetSystem="Linux;UNIX" BUILD_SHARED_LIBS=OFF # D runtime ARM rtlibs: $(DRT_LIBS) # stat: find source -name '*.d' | xargs wc -l clean: rm -rf $(TP) rm -rf .dub $(LDC_BRT) --buildDir=$(LDC_RT_DIR) --resetOnly
This makefile
allows you to build a project both under the arm and under x86 with almost one command:
./ddb make ./ddb make arch=x86 # x86 make arch=x86 # host ldc
Files for arm go into the build-arm
, for x86 to build-x86
.
Well, for a snack for the full picture code app.d
:
import vibe.core.core : runApplication; import vibe.http.server; void handleRequest(scope HTTPServerRequest req, scope HTTPServerResponse res) { if (req.path == "/") res.writeBody("Hello, World!", "text/plain"); } void main() { auto settings = new HTTPServerSettings; settings.port = 8080; settings.bindAddresses = ["::1", "0.0.0.0"]; auto l = listenHTTP(settings, &handleRequest); scope (exit) l.stopListening(); runApplication(); }
Everybody needs web =)
In general, everything is not as difficult as it seems at first glance, just a universal approach is not yet ready. Personally, I spent a lot of time trying to do without make
. With him everything went somehow easier and more varied.
But you need to understand that D is not Go, it is customary to use external libraries in D and you need to be careful with their versions.
The easiest way to get the library under the arm is to copy it from the working device.
Here is the source code of the example. In this repository, the Russian-speaking community is gradually collecting information, examples, links.
There is more information here, such as how to build for YoctoLinux.
Source: https://habr.com/ru/post/428982/