Como escrever D no ARM

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 

  1. ddb inicia o contêiner, executa o script entry.sh
  2. 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 assembly
  3. entry.sh acaba passando o controle para o comando input ( make no nosso caso)
  4. por sua vez, lê makefile
  5. 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
  6. quando chamado para dub script ldc do diretório atual é passado como um compilador e as variáveis ​​de ambiente são definidas
  7. 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
  8. 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.


informações adicionais aqui, como construir para o YoctoLinux.


Feed de notícias em VK

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


All Articles