⬆️ ⬇️

How to write on D under ARM

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 ... =)





Why d?



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.



Tools



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.



General project structure



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



General assembly algorithm



 ./ddb make 


  1. ddb runs container, executes entry.sh script
  2. entry.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 assembly
  3. entry.sh ends up passing control to the input command ( make in our case)
  4. make in turn reads makefile
  5. all flags for cross-compilation and directories for building are stored in the makefile , the dub call line is formed
  6. when called in dub ldc script from the current directory is passed as a compiler and the environment variables are set
  7. runtime libraries are exposed as build dependencies in the makefile , which, if they are missing, are built by the ldc-build-runtime program
  8. variables are passed to the ldc script and to the dub.sdl parameters


Content of main files



Dockerfile



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



entry.sh



 #!/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.



build-docker, ddb, ldc



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


dub.sdl



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



makefile



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 .



app.d



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 =)



Conclusion



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.



Links



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.



News feed in VK



')

Source: https://habr.com/ru/post/428982/



All Articles