Bom dia, Habr!
Hoje, quero compartilhar minha experiência de desenvolvimento de mini computadores no Linux (RPI, BBB e outros) na linguagem de programação D. Sob o corte, instruções completas sobre como fazer isso sem dor. Bem, ou quase ... =)

Por que d?
No trabalho, a tarefa era escrever um sistema de monitoramento para o ARM, mesmo sendo um grande fã de D, duvidava que ele fosse considerado a principal ferramenta. Em geral, eu não sou uma pessoa extravagante e estou no D há muito tempo, então pensei que valia a pena tentar e ... nem tudo é tão simples. Por um lado, não houve problemas especiais (exceto um não totalmente claro que saiu com a chegada da nova versão do compilador); por outro lado, as pessoas que estão desenvolvendo para o ARM podem pensar constantemente que o kit de ferramentas não está pronto a partir da palavra. Depende de você.
Toolkit
Eu posso aconselhar o Visual Studio Code
com o plug-in D Programming Language
do camarada. WebFreak (Jan Jurzitza). Nas configurações, você pode definir a configuração Beta Stream
para sempre ter a versão mais recente do serve-d
. O próprio plugin instala o software necessário.
Estrutura geral do projeto
Em geral, ficou bastante confuso (em comparação com o projeto usual em D), mas, como me parece, é bastante flexível e 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 necessárias para que nosso aplicativo funcione (compilado sob o braço)
docker-ctx
- contexto para montar uma imagem do docker
entry.sh
- executará algumas ações sobre cada lançamento posterior do contêiner, sobre o qual posteriormente
dub.sdl
- arquivo de projeto em D, permite incluir bibliotecas de terceiros e muito mais
build-docker
- script de construção de contêiner (essencialmente 1 linha, mas ainda)
ddb
- docker D builder - script de inicialização de contêiner (também uma linha, mas na verdade mais conveniente)
ldc
- um script que permite chamar ldc com todos os parâmetros necessários
makefile
- contém receitas de compilação para arm e x86 e ações adicionais
source/app.d
- fontes do projeto
Algumas palavras sobre arm-lib
.
Existem os arquivos necessários para o vibe funcionar. Adicionar arquivos binários ao repositório é uma forma incorreta. Mas aqui, para simplificar sua vida, é mais fácil fazer exatamente isso. Você pode adicioná-los dentro do contêiner, mas, para formar totalmente a receita de montagem do contêiner, será necessário armazenar a pasta arm-lib
no dockert-ctx
. O sabor e a cor ...
Algoritmo de montagem geral
./ddb make
ddb
inicia o contêiner, executa o script entry.sh
entry.sh
configura dub
pouco o dub
para que ele use a pasta da biblioteca dentro do contêiner, que estará localizada no diretório atual, o que permitirá que você não extraia e colete as bibliotecas usadas no projeto ao reiniciar o assemblyentry.sh
acaba passando o controle para o comando input ( make
no nosso caso)- por sua vez, lê
makefile
- todos os sinalizadores para compilação cruzada e diretório de compilação são armazenados no
makefile
, uma linha de chamada de dub
é formada - quando chamado para
dub
script ldc
do diretório atual é passado como um compilador e as variáveis de ambiente são definidas - bibliotecas de tempo de execução são definidas como dependências de composição no
makefile
, que, se estiverem ausentes, são coletadas pelo programa ldc-build-runtime
- variáveis são passadas para o script
ldc
e para os parâmetros dub.sdl
O conteúdo dos arquivos principais
Dockerfile
Como escreveremos sob o RPI3, selecionamos a imagem do sistema debian:stretch-slim
básico debian:stretch-slim
, em que o gcc-arm-linux-gnueabihf
usa a mesma versão do glibc
que a distribuição oficial raspbian (houve um problema no fedora, onde o mantenedor de compilador cruzado usava uma versão muito nova do 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" ]
O compilador ldc
do github
, onde é compilado com base no llvm
atual.
entry.sh
#!/bin/bash if [ ! -d ".dpack" ]; then mkdir .dpack fi ln -s $(pwd)/.dpack /root/.dub exec $@
Tudo é simples aqui: se não houver .dpack
pasta .dpack
, crie, use .dpack
para criar um link simbólico para /root/.dub
.
Isso permitirá que você armazene os pacotes baixados pelo dub
na pasta do projeto.
build-docker, ddb, ldc
Estes são três arquivos simples de linha única. Dois deles são opcionais, mas convenientes, mas escritos para linux (bash). Para o Windows, você precisará criar arquivos semelhantes no script local ou apenas executá-lo manualmente.
build-docker
inicia a construção do contêiner (chamado uma vez, apenas para linux)
#!/bin/bash docker build -t dcross docker-ctx
ddb
lança o contêiner para montagem e passa parâmetros (somente linux):
#!/bin/bash docker run -v `pwd`:/workdir -t --rm dcross $@
Observe que o nome do contêiner é usado dcross
(o nome em si não importa, mas deve corresponder nos dois arquivos) e o comando pwd
é usado para Dockerfile
diretório atual em /workdir
(o diretório é especificado como WORKDIR
no Dockerfile
) (no win, ao que parece, você precisa usar %CD%
).
ldc
inicia o ldc
, por ldc
pareça, enquanto usa variáveis de ambiente (apenas linux, mas começa no contêiner, portanto, não precisa ser alterado para compilar sob win):
#!/bin/bash $LDC $LDC_FLAGS $@
dub.sdl
Por exemplo, será bastante simples:
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
é obtido da variável de ambiente porque dub
não pode especificar os campos da receita de montagem por plataforma (por exemplo, lflags "-L.libs" platform="arm"
adicionará um sinalizador ao vinculador apenas ao construir sob o braço).
makefile
E aqui é o mais interessante. De fato, o make
não make
usado para construir como tal, chama um dub
para isso, e o próprio dub
monitora o que precisa ser remontado e o que não. Porém, com a ajuda de um makefile
todas as variáveis de ambiente necessárias são formadas, comandos adicionais são executados em casos mais complexos (construção de bibliotecas em C, compactação de arquivos de atualização etc.).
O conteúdo do makefile
maior que o restante:
# 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
Esse makefile
permite criar um projeto sob arm e x86 com quase um comando:
./ddb make ./ddb make arch=x86 # x86 make arch=x86 # host ldc
Os arquivos para arm entram no build-arm
, para x86, no build-x86
.
app.d
Bem, para aperitivo, para a imagem completa, o 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(); }
Todo mundo agora precisa da web =)
Conclusão
Em geral, nem tudo é tão complicado quanto parece à primeira vista, é apenas que uma abordagem universal ainda não está pronta. Pessoalmente, passei muito tempo tentando make
sem make
. Com ele, tudo foi de alguma forma mais simples e mais variado.
Mas você precisa entender que D não é Go, em D é habitual usar bibliotecas externas e você precisa ter cuidado com suas versões.
A maneira mais fácil de obter uma biblioteca para arm é copiá-la de um dispositivo em funcionamento.
Referências
Aqui está o código fonte do exemplo. Neste repositório, a comunidade de língua russa está gradualmente coletando informações, exemplos, links.
Há informações adicionais aqui, como construir para o YoctoLinux.
Feed de notícias em VK