Usando o compilador nuitka para python3 no Alpine Linux ARMHF (musl)

Motivação

pidoctor é escrito em Python (pois não havia uma maneira fácil de fazer o Crystal funcionar em ARMHF / musl).

Isto significa uma dependência do Python - que acrescenta despesas gerais.

Eu suspeito que esta sobrecarga é a razão pela qual o pidoctor não irá funcionar com 256 MB de Raspberry Pi's - porque a RAM está esgotada por todos os pacotes que são necessários.

Além disso, é desejável que a distribuição seja o menor download possível! Isto significa que Python tem que ser removido da equação de alguma forma, o pacote deve produzir um binário (como seria o caso do Crystal).

Felizmente, há nuitka.

E - felizmente você - eu tenho trabalhado algumas dicas para usar nuitka no Alpine Linux, ARMHF (talvez funcione para x86 também).

Nuitka

nuitka é um software fantástico, que vai pegar um script Python e compilá-lo para um executável, acelerando-o assim dramaticamente.

nuitka faz todo o trabalho pesado, você nem precisa ajustar nada no seu código fonte (methinks). Você pode executá-lo assim:

python3 -m nuitka -follow-imports -standalone pidoctor.py

Note a bandeira -standalone, que compilará as bibliotecas compartilhadas necessárias em sua pasta .dist - assim a instalação será capaz de rodar sem qualquer dependência em Python!

Por favor note: o WordPress infelizmente fode os traços duplos e transforma-os em traços simples. o -m tem um único traço, o -seguinte-importações e o -standalone tem traços duplos.

Instalação de Nuitka no Alpine Linux (especificamente arhmf, pode funcionar em outros alvos)

apk adicionar python3-dev

apk add chrpath

pip3 instalar -U nuitka

opcionalmente, se você estiver rodando no modo diskless (para adicionar nuitka aos arquivos que devem ser persistidos):

lbu add /usr/bin/nuitka3-run

lbu add /usr/bin/nuitka3

lbu add /usr/lib/python3.6/site-packages/nuitka

lbu commit -d

Nota: a fonte de nuitka está em Python.

Verifica se a nuitka corre:

python3 -m nuitka

deve executar sem mostrando quaisquer erros de sintaxe.

Consertando nuitka para rodar com o armhf Linux Alpino

Importante: Infelizmente, o WordPress altera mal a sintaxe. Por favor, consulte a edição do GitHUB que abrionde também anexei os arquivos de patch para uma referência.

nuitka chama vários utilitários binários de suporte em seu sistema, e espera que eles retornem os resultados de uma certa forma. Isto é, onde a nuitka inicialmente tropeça.

Primeiro erro

pidoctor:/opt/pidoctor# python3 -m nuitka -follow-imports -standalone pidoctor.py

Traceback (última chamada):

Arquivo "/usr/lib/python3.6/site-packages/nuitka/__main__.py", linha 188, em

principal()

Arquivo "/usr/lib/python3.6/site-packages/nuitka/__main__.py", linha 182, em principal

MainControl.main()

Arquivo "/usr/lib/python3.6/site-packages/nuitka/MainControl.py", linha 846, na principal

Plugins.considerExtraDlls(dist_dir, módulo)

Arquivo "/usr/lib/python3.6/site-packages/nuitka/plugins/Plugins.py", linha 102, in considerExtraDlls

para extra_dll in plugin.considerExtraDlls(dist_dir, módulo):

Arquivo "/usr/lib/python3.6/site-packages/nuitka/plugins/standard/ImplicitImports.py", linha 372, in considerExtraDlls

uuid_dll_path = locateDLL("uuid")

Arquivo "/usr/lib/python3.6/site-packages/nuitka/utils/SharedLibraries.py", linha 71, in locateDLL

retornar dll_map[dll_name]

KeyError: 'libuuid.so.1.3.0'.

Motivo do erro: nuitka chama ldconfig -p e espera que ela liste uma lista de bibliotecas compartilhadas em cache, em um formato como este:

libICE.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libICE.so.6

no Alpine Linux, o ldconfig -p falha - pois não existe tal opção. O ldconfig no Alpine Linux é incapaz de produzir uma lista da forma que o Nuitka quer.

Consertar:

in /usr/lib/python3.6/site-packages/nuitka/utils/SharedLibraries.py

adicione o seguinte código imediatamente após as declarações de importação na parte superior:

def find_alpine(nome,caminhos):
     para o caminho nos caminhos:
         para root, dirs, arquivos em os.walk(path):
             se nome em arquivos:
                 return os.path.join(root,name)

e em locateDLL, antes do subprocesso de importação, adicione o seguinte código:

if os.path.isfile('/etc/alpine-release'):
     retornar find_alpine(dll_name,["/lib","/usr/lib","/usr/local/lib"])
senão..:
     subprocesso de importação

    (…)

O (...) acima indica que o restante do código de localização daDLL deve ser recuado sob o outro.

O que este código faz: se o arquivo /etc/alpine-release existe - indicando que estamos rodando no Alpine Linux, procuramos pela biblioteca compartilhada em três diretórios pré-definidos:

  • /lib
  • /usr/lib
  • /usr/local/lib

e devolver o primeiro ficheiro correspondente. Nota, não tratamos da situação caso o ficheiro não seja encontrado - neste caso nada é devolvido..

Segundo erro

Traceback (última chamada):
   Arquivo "/usr/lib/python3.6/site-packages/nuitka/__main__.py", linha 188, em
     principal()
   Arquivo "/usr/lib/python3.6/site-packages/nuitka/__main__.py", linha 182, em principal
     MainControl.main()
   Arquivo "/usr/lib/python3.6/site-packages/nuitka/MainControl.py", linha 859, na principal
     pontos_de_entrada_automática = pontos_de_entrada_automática
   Arquivo "/usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py", linha 1169, em copyUsedDLLs
     used_dlls = detectUsedDLLs(source_dir, standalone_entry_points)
   Arquivo "/usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py", linha 1062, em detectUsedDLLs
     assert os.path.isabs(dll_filename), dll_filename
AssertionError: ldd

Motivo do erro: nuitka chama ldd com cada biblioteca compartilhada ("dll_filename") para ser adicionado à sua pasta .dist. O propósito disto é encontrar as dependências destas bibliotecas compartilhadas. (Uma pesquisa recursiva se você quiser).

Por exemplo:

pidoctor:/opt/pidoctor# ldd /usr/lib/libexpat.so.1
         ldd (0x76f43000)
         libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x76efd000)
         libc.musl-armhf.so.1 => ldd (0x76f43000)

Como você vê, libexpat.so.1 tem duas dependências:

  • libgcc_s.so.1 -> para ser encontrado em /usr/lib/libgcc_s.so.1
  • libc.musl-armhf.so.1 -> para ser encontrado em ldd

e é precisamente aqui que a nuitka tem problemas. Ela quer copiar um arquivo com um caminho absoluto, e recebe o nome "ldd" em seu lugar. Mais uma vez, algo que é específico do Alpine Linux.

Portanto, a afirmação falha, e nuitka não continua.

Aqui está como descobrir qual arquivo ele realmente se refere:

pidoctor:~# que ldd
/usr/bin/ldd

pidoctor:~# ls -alh /usr/bin/ldd
lrwxrwxrwx 1 root root 28 Jan 1 1970 /usr/bin/ldd -> ../.../lib/ld-musl-armhf.so.1

Note que ../.../lib/ld-musl-armhf.so.1 é um caminho relativo, que na verdade se refere a /lib/ld-musl-armhf.so.1 no meu sistema. E no teu, provavelmente, também.

Consertar:

procurar libutil.so. no arquivo /usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py

Há uma linha com result.add(filename) abaixo dela. Apenas acima esta linha, insira:

if filename == 'ldd':
     filename = os.path.join(os.path.dirname('/usr/bin/ldd'),os.readlink('/usr/bin/ldd')) #e.g. '/lib/ld-musl-armhf.so.1' correcção para Alpine

Terceiro erro

Erro, precisa de 'chrpath' no seu sistema, devido a configurações de 'RPATH' em uso compartilhado

Consertar:

Você instalou o Christpath como aconselhado acima?

apk add chrpath

É isso - agora nuitka deve fazer magia sem soluços. Vou enviar ao autor de nuitka os adesivos e este artigo no blog, talvez ele possa incluí-los em nuitka.

Versão de fábrica

Atualização 2.2.2019: O autor de Nuitka adicionou meus patches (em uma forma ligeiramente modificada). Na atual versão "de fábrica", a compilação - independente funciona para mim. Por favor, consulte esta página do github para mais detalhes:

https://github.com/Nuitka/Nuitka/issues/237

http://nuitka.net/doc/factory.html