Gebruik van nuitka compiler voor python3 op Alpine Linux ARMHF (musl)

Motivatie

pidoctor is geschreven in Python (omdat er geen gemakkelijke manier was om Crystal te laten werken op ARMHF / musl).

Dit betekent een afhankelijkheid van Python - wat overhead toevoegt.

Ik vermoed dat deze overhead de reden is dat pidoctor niet zal draaien op 256 MB Raspberry Pi's - omdat het RAM opgebruikt wordt door alle pakketten die nodig zijn.

Verder is het wenselijk dat de distributie een zo klein mogelijke download is! Dit betekent dat Python op de een of andere manier uit de vergelijking moet worden gehaald, het pakket zou een binair pakket moeten produceren (zoals de manier zou zijn met Crystal).

Gelukkig is er nuitka.

En - gelukkig voor jou - ik heb wat knikken in het gebruik van nuitka op Alpine Linux, ARMHF (misschien werkt het ook voor x86).

Nuitka

nuitka is een fantastisch stukje software, dat een Python script compileert tot een uitvoerbaar bestand, waardoor het drastisch versneld wordt.

nuitka doet al het zware werk, je hoeft niet eens iets in je broncode aan te passen (methinks). Je kunt het als volgt uitvoeren:

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

Let op de -standalone vlag, die de nodige gedeelde bibliotheken zal compileren in je .dist map - daardoor zal de installatie kunnen draaien zonder elke afhankelijkheid op Python!

Let op: WordPress verneukt helaas de dubbele streepjes en maakt er enkele streepjes van. de -m heeft een enkel streepje, de -follow-imports en de -standalone hebben dubbele streepjes.

Installatie van Nuitka op Alpine Linux (specifiek arhmf, zou ook op andere targets kunnen werken)

apk toevoegen python3-dev

apk toevoegen chrpath

pip3 install -U nuitka

optioneel, als u in schijfloze modus draait (om nuitka toe te voegen aan de bestanden die moeten worden bewaard):

lbu add /usr/bin/nuitka3-run

lbu add /usr/bin/nuitka3

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

lbu commit -d

Opmerking: nuitka's broncode is in Python.

Controleer of Nuitka loopt:

python3 -m nuitka

moet uitvoeren zonder met syntax fouten.

Herstellen van nuitka om te draaien met Alpine Linux armhf

Belangrijk: WordPress maakt helaas slecht gebruik van de syntaxis. Zie alstublieft de GitHUB kwestie die ik geopend hebwaar ik ook de patch bestanden als referentie heb bijgevoegd.

nuitka roept verschillende ondersteunende binaire hulpprogramma's op uw systeem aan, en verwacht dat ze de resultaten op een bepaalde manier teruggeven. Dit is, waar nuitka aanvankelijk struikelt.

Eerste fout

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

Traceback (meest recente oproep laatst):

Bestand "/usr/lib/python3.6/site-packages/nuitka/__main__.py", regel 188, in

main()

Bestand "/usr/lib/python3.6/site-packages/nuitka/__main__.py", regel 182, in main

MainControl.main()

Bestand "/usr/lib/python3.6/site-packages/nuitka/MainControl.py", regel 846, in main

Plugins.considerExtraDlls(dist_dir, module)

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

voor extra_dll in plugin.considerExtraDlls(dist_dir, module):

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

uuid_dll_path = locateDLL("uuid")

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

geef dll_map[dll_name] terug

KeyError: "libuuid.so.1.3.0

Reden achter de fout: nuitka roept ldconfig -p aan en verwacht een lijst van gedeelde bibliotheken in de cache, in een formaat zoals dit:

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

op Alpine Linux, ldconfig -p faalt - omdat er geen dergelijke optie is. ldconfig op Alpine Linux is niet in staat om een lijst te produceren van de vorm die Nuitka wil.

Fix:

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

voeg de volgende code toe onmiddellijk na de import statements bovenaan:

def find_alpine(naam,paden):
     voor pad in paden:
         voor root, dirs, files in os.walk(path):
             indien naam in bestanden:
                 geef os.path.join(root,naam) terug

en in locateDLL, voor het importeren van subproces, voeg de volgende code toe:

als os.path.isfile('/etc/alpine-release'):
     return find_alpine(dll_name,["/lib","/usr/lib","/usr/local/lib"])
anders:
     importeren subproces

    (...)

De (...) hierboven geeft aan dat de rest van de code van locateDLL moet worden ingesprongen onder de else.

Wat deze code doet: als het bestand /etc/alpine-release bestaat - wat aangeeft dat we draaien op Alpine Linux, zoeken we naar de gedeelde bibliotheek in drie voorgedefinieerde directories:

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

en geef het eerste overeenkomende bestand terug. Merk op dat we de situatie niet behandelen in het geval dat het bestand niet gevonden wordt - in dat geval wordt er niets teruggestuurd.

Tweede fout

Traceback (meest recente oproep laatst):
   Bestand "/usr/lib/python3.6/site-packages/nuitka/__main__.py", regel 188, in
     main()
   Bestand "/usr/lib/python3.6/site-packages/nuitka/__main__.py", regel 182, in main
     MainControl.main()
   Bestand "/usr/lib/python3.6/site-packages/nuitka/MainControl.py", regel 859, in main
     standalone_entry_points = standalone_entry_points
   Bestand "/usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py", regel 1169, in copyUsedDLLs
     used_dlls = detectUsedDLLs(source_dir, standalone_entry_points)
   Bestand "/usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py", regel 1062, in detectUsedDLLs
     assert os.path.isabs(dll_filename), dll_filename
AssertionError: ldd

Reden achter de fout: nuitka roept ldd aan bij elke gedeelde bibliotheek ("dll_filename") die aan uw .dist map moet worden toegevoegd. Het doel hiervan is om de dependencies van deze gedeelde bibliotheken zelf te vinden. (Een recursieve zoektocht als je wilt).

Bijvoorbeeld:

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)

Zoals je ziet, libexpat.so.1 heeft twee afhankelijkheden:

  • libgcc_s.so.1 -> te vinden in /usr/lib/libgcc_s.so.1
  • libc.musl-armhf.so.1 -> te vinden op ldd

en dit is precies waar nuitka problemen heeft. Het wil een bestand kopiëren met een absoluut pad, en krijgt in plaats daarvan de naam "ldd". Alweer, iets dat specifiek is voor Alpine Linux.

Daarom faalt de bewering, en gaat nuitka niet verder.

Zo kom je te weten naar welk bestand het eigenlijk verwijst:

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

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

Merk op dat ../../lib/ld-musl-armhf.so.1 een relatief pad is, dat eigenlijk verwijst naar /lib/ld-musl-armhf.so.1 op mijn systeem. En op het jouwe, waarschijnlijk ook.

Fix:

zoeken naar libutil.so. in het bestand /usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py

Er staat een regel met result.add(bestandsnaam) eronder. Gewoon boven deze regel, invoegen:

als bestandsnaam == 'ldd':
     bestandsnaam = os.path.join(os.path.dirname('/usr/bin/ldd'),os.readlink('/usr/bin/ldd')) #e.g. '/lib/ld-musl-armhf.so.1' fix voor Alpine

Derde fout

Fout, heeft 'chrpath' nodig op uw systeem, vanwege 'RPATH' instellingen in gebruikte shared

Fix:

Heeft u chrpath geïnstalleerd zoals hierboven geadviseerd?

apk toevoegen chrpath

Dat is het - nu zou nuitka zijn magie zonder haperingen moeten uitvoeren. Ik zal de auteur van nuitka de patches en dit blog artikel sturen, misschien kan hij ze in nuitka opnemen.

Fabrieksversie

Update 2.2.2019: De auteur van Nuitka heeft mijn patches (in een licht aangepaste vorm) toegevoegd. In de huidige "fabrieks"-versie werkt de -standalone compilatie voor mij. Raadpleeg deze github-pagina voor details:

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

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