Buen dia, Habr!
Hoy quiero compartir mi experiencia de desarrollo para minicomputadoras en Linux (RPI, BBB y otras) en el lenguaje de programación D. Debajo del corte, instrucciones completas sobre cómo hacer esto sin dolor. Bueno, o casi ... =)

¿Por qué d?
Cuando en el trabajo, la tarea era escribir un sistema de monitoreo para ARM, incluso como un gran fanático de D, dudaba si debería ser tomado como la herramienta principal. En general, no soy una persona caprichosa, y he estado en D durante mucho tiempo, así que pensé que valía la pena intentarlo y ... no todo es tan simple. Por un lado, no hubo problemas especiales (a excepción de uno que no quedó del todo claro con la llegada de la nueva versión del compilador), por otro lado, las personas que se están desarrollando para ARM pueden pensar constantemente que el kit de herramientas no está listo para nada. Depende de usted.
Kit de herramientas
Puedo aconsejar a Visual Studio Code
con el complemento D Programming Language
de camaradas. WebFreak (Jan Jurzitza). En la configuración, puede establecer la configuración de Beta Stream
para que siempre tenga la última versión de serve-d
. El complemento mismo instala el software necesario.
Estructura general del proyecto.
En general, resultó bastante confuso (en comparación con el proyecto habitual en D), pero, según me parece, es bastante flexible y conveniente.
. ├── 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
: bibliotecas necesarias para que nuestra aplicación funcione (compilada bajo arm)
docker-ctx
: contexto para ensamblar una imagen de docker
entry.sh
: realizará algunas acciones sobre cada lanzamiento posterior del contenedor, sobre el cual más tarde
dub.sdl
: archivo de proyecto en D, le permite incluir bibliotecas de terceros y mucho más
build-docker
: script de compilación del contenedor (esencialmente 1 línea, pero aún así)
ddb
- docker D builder - script de inicio de contenedor (también una línea, pero en realidad más conveniente)
ldc
: un script que le permite llamar a ldc con todos los parámetros necesarios
makefile
: contiene recetas de compilación para arm y x86 y acciones adicionales
source/app.d
- fuentes del proyecto
Algunas palabras sobre arm-lib
.
Existen los archivos necesarios para que vibe funcione. Agregar archivos binarios al repositorio es una mala forma. Pero aquí, para simplificar tu vida, es más fácil hacer eso. Puede agregarlos dentro del contenedor, pero luego, para formar completamente la receta de ensamblaje del contenedor, deberá almacenar la carpeta arm-lib
en dockert-ctx
. El sabor y el color ...
Algoritmo de montaje general
./ddb make
ddb
inicia el contenedor, ejecuta el script entry.sh
entry.sh
configura dub
poco para que use la carpeta de la biblioteca dentro del contenedor, que se ubicará en el directorio actual, lo que le permitirá no bombear y recopilar las bibliotecas utilizadas en el proyecto cuando reinicie el ensamblajeentry.sh
termina pasando el control al comando de entrada ( make
en nuestro caso)- a su vez lee
makefile
- todos los indicadores para el directorio de compilación cruzada y compilación se almacenan en el
makefile
, se forma una línea de llamada de dub
- cuando se llama en
dub
script ldc
del directorio actual se pasa como un compilador y se establecen variables de entorno - Las bibliotecas de tiempo de ejecución se configuran como dependencias de maquillaje en el
makefile
, que, si faltan, son recogidas por el programa ldc-build-runtime
- las variables se pasan al script
ldc
y a los parámetros dub.sdl
El contenido de los archivos principales.
Dockerfile
Como escribiremos bajo RPI3, seleccionaremos la imagen del sistema debian:stretch-slim
base debian:stretch-slim
, donde gcc-arm-linux-gnueabihf
usa la misma versión de glibc
que la distribución oficial de raspbian (hubo un problema con fedora, donde el mantenedor del compilador cruzado usó una versión demasiado nueva de 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" ]
El compilador ldc
desde github
, donde se compila en función del llvm
actual.
entry.sh
#!/bin/bash if [ ! -d ".dpack" ]; then mkdir .dpack fi ln -s $(pwd)/.dpack /root/.dub exec $@
Aquí todo es simple: si no hay .dpack
carpeta .dpack
, cree, use .dpack
para crear un enlace simbólico a /root/.dub
.
Esto le permitirá almacenar los paquetes descargados por el dub
en la carpeta del proyecto.
build-docker, ddb, ldc
Estos son tres archivos simples de una sola línea. Dos de ellos son opcionales, pero convenientes, pero están escritos para Linux (bash). Para Windows, tendrá que crear archivos similares en el script local o simplemente ejecutarlo a mano.
build-docker
inicia la compilación del contenedor (se llama una vez, solo para linux):
#!/bin/bash docker build -t dcross docker-ctx
ddb
lanza el contenedor para el ensamblaje y pasa parámetros (solo linux):
#!/bin/bash docker run -v `pwd`:/workdir -t --rm dcross $@
Tenga en cuenta que el nombre del contenedor se usa dcross
(el nombre en sí mismo no importa, pero debe coincidir en ambos archivos) y el comando pwd
se usa para Dockerfile
directorio actual en /workdir
(el directorio se especifica como WORKDIR
en el Dockerfile
) (en win, parece que necesita usar %CD%
).
ldc
inicia ldc
, por extraño que parezca, mientras usa variables de entorno (solo linux, pero comienza en el contenedor, por lo que no es necesario cambiarlo para compilar en win):
#!/bin/bash $LDC $LDC_FLAGS $@
dub.sdl
Por ejemplo, será bastante 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
se toma de la variable de entorno porque dub
no puede especificar los campos de la receta de ensamblaje por plataforma (por ejemplo, lflags "-L.libs" platform="arm"
agregará una bandera al enlazador solo cuando se construye bajo el brazo).
makefile
Y aquí está lo más interesante. De hecho, make
no se usa para construir como tal, llama a un dub
para esto, y el mismo dub
monitorea lo que necesita ser reensamblado y lo que no. Pero con la ayuda de un makefile
se forman todas las variables de entorno necesarias, se ejecutan comandos adicionales en casos más complejos (creación de bibliotecas en C, empaquetado de archivos de actualización, etc.).
El contenido del makefile
más grande que el resto:
# 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
Tal makefile
permite construir un proyecto bajo el brazo y x86 con casi un comando:
./ddb make ./ddb make arch=x86 # x86 make arch=x86 # host ldc
Los archivos para arm entran en build-arm
, para x86 en build-x86
.
app.d
Bueno, para aperitivo, para la imagen completa, el código 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(); }
Todos ahora necesitan web =)
Conclusión
En general, todo no es tan complicado como parece a primera vista, es solo que un enfoque universal aún no está listo. Personalmente, pasé mucho tiempo tratando de hacerlo sin make
. Con él, todo fue de alguna manera más simple y más variado.
Pero debe comprender que D no es Go, en D es habitual utilizar bibliotecas externas y debe tener cuidado con sus versiones.
La forma más fácil de obtener una biblioteca para armar es copiarla desde un dispositivo que funcione.
Referencias
Aquí está el código fuente para el ejemplo. En este repositorio, la comunidad de habla rusa está recopilando gradualmente información, ejemplos, enlaces.
Aquí hay información adicional, como cómo compilar para YoctoLinux.
Feed de noticias en VK