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