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/lddpidoctor:~# 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: