Verwendung des nuitka-Compilers für python3 auf Alpine Linux ARMHF (musl)

Motivation

pidoctor ist in Python geschrieben (da es keine einfache Möglichkeit gab, Crystal unter ARMHF / musl zum Laufen zu bringen).

Dies bedeutet eine Abhängigkeit von Python, was zusätzlichen Aufwand bedeutet.

Ich vermute, dass dieser Overhead der Grund dafür ist, dass pidoctor nicht auf 256 MB Raspberry Pi's läuft - weil der Arbeitsspeicher durch alle benötigten Pakete erschöpft ist.

Außerdem ist es wünschenswert, dass die Distribution so klein wie möglich ist! Das bedeutet, dass Python irgendwie aus der Gleichung entfernt werden muss, das Paket sollte eine Binärdatei erzeugen (wie es bei Crystal der Fall wäre).

Zum Glück gibt es nuitka.

Und - du hast Glück - ich habe einige Probleme bei der Verwendung von nuitka unter Alpine Linux, ARMHF, gelöst (vielleicht funktioniert es auch für x86).

Nuitka

nuitka ist eine phantastische Software, die ein Python-Skript zu einer ausführbaren Datei kompiliert und damit die Ausführung erheblich beschleunigt.

nuitka erledigt die ganze Arbeit, Sie müssen nicht einmal etwas in Ihrem Quellcode anpassen (denke ich). Sie können es wie folgt ausführen:

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

Beachten Sie das Flag -standalone, mit dem die erforderlichen Bibliotheken in den Ordner .dist kompiliert werden - dadurch kann die Installation auch ohne jede Abhängigkeit auf Python!

Bitte beachten Sie: WordPress versaut leider die doppelten Bindestriche und macht sie zu einfachen Bindestrichen. Das -m hat einen einfachen Bindestrich, das -follow-imports und das -standalone haben doppelte Bindestriche.

Installation von Nuitka auf Alpine Linux (speziell arhmf, kann auch auf anderen Targets funktionieren)

apk hinzufügen python3-dev

apk chrpfad hinzufügen

pip3 install -U nuitka

optional, wenn Sie im Diskless-Modus arbeiten (um nuitka zu den Dateien hinzuzufügen, die persistiert werden sollen):

lbu add /usr/bin/nuitka3-run

lbu add /usr/bin/nuitka3

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

lbu commit -d

Hinweis: Der Quelltext von nuitka ist in Python.

Prüfen Sie, ob nuitka läuft:

python3 -m nuitka

ausführen sollte ohne und zeigt Syntaxfehler an.

nuitka mit Alpine Linux armhf zum Laufen bringen

Wichtig: WordPress verfälscht leider die Syntax. Bitte beziehen Sie sich auf das GitHUB-Problem, das ich eröffnet habe, wo ich auch die Patchdateien als Referenz angehängt habe.

nuitka ruft verschiedene binäre Hilfsprogramme auf Ihrem System auf und erwartet, dass diese die Ergebnisse auf eine bestimmte Weise zurückgeben. Dies ist der Punkt, an dem nuitka zunächst strauchelt.

Erster Fehler

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

Traceback (letzter Aufruf):

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

main()

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

MainControl.main()

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

Plugins.considerExtraDlls(dist_dir, module)

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

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

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

uuid_dll_path = locateDLL("uuid")

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

return dll_map[dll_name]

KeyError: 'libuuid.so.1.3.0'

Grund für den Fehler: nuitka ruft ldconfig -p auf und erwartet eine Liste der zwischengespeicherten gemeinsamen Bibliotheken in einem Format wie diesem:

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

auf Alpine Linux schlägt ldconfig -p fehl, da es diese Option nicht gibt. ldconfig auf Alpine Linux ist nicht in der Lage, eine Liste in der von Nuitka gewünschten Form zu erstellen.

Reparieren:

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

Fügen Sie den folgenden Code unmittelbar nach den Import-Anweisungen am Anfang ein:

def find_alpine(name,pfade):
     für Pfad in Pfade:
         for root, dirs, files in os.walk(path):
             wenn Name in Dateien:
                 return os.path.join(root,name)

und fügen Sie in locateDLL vor dem Import von subprocess den folgenden Code ein:

if os.path.isfile('/etc/alpine-release'):
     return find_alpine(dll_name,["/lib","/usr/lib","/usr/local/lib"])
sonst:
     importieren subprocess

    (...)

Das (...) oben zeigt an, dass der Rest des Codes von locateDLL unter dem else eingerückt werden sollte.

Was dieser Code macht: Wenn die Datei /etc/alpine-release existiert - was anzeigt, dass wir auf Alpine Linux laufen - suchen wir in drei vordefinierten Verzeichnissen nach der gemeinsamen Bibliothek:

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

und gibt die erste passende Datei zurück. Bitte beachten Sie, dass wir die Situation nicht behandeln, wenn die Datei nicht gefunden wird - in diesem Fall wird nichts zurückgegeben.

Zweiter Fehler

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

Grund für den Fehler: nuitka ruft ldd mit jeder gemeinsam genutzten Bibliothek ("dll_filename") auf, die dem Ordner .dist hinzugefügt werden soll. Dies dient dazu, die Abhängigkeiten dieser gemeinsam genutzten Bibliotheken selbst zu finden. (Eine rekursive Suche, wenn Sie so wollen).

Zum Beispiel:

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)

Wie Sie sehen, hat libexpat.so.1 zwei Abhängigkeiten:

  • libgcc_s.so.1 -> zu finden unter /usr/lib/libgcc_s.so.1
  • libc.musl-armhf.so.1 -> zu finden unter ldd

und das ist genau der Punkt, an dem nuitka Probleme hat. Es möchte eine Datei mit einem absoluten Pfad kopieren und erhält stattdessen den Namen "ldd". Auch dies ist etwas, das spezifisch für Alpine Linux ist.

Daher scheitert die Behauptung, und nuitka wird nicht fortgesetzt.

So finden Sie heraus, auf welche Datei sie tatsächlich verweist:

pidoctor:~# der 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

Beachten Sie, dass ../../lib/ld-musl-armhf.so.1 ein relativer Pfad ist, der sich eigentlich auf /lib/ld-musl-armhf.so.1 auf meinem System. Und auf Ihrem wahrscheinlich auch.

Reparieren:

Suche nach libutil.so. in der Datei /usr/lib/python3.6/site-packages/nuitka/freezer/Standalone.py

Es gibt eine Zeile mit result.add(filename) darunter. Einfach über diese Zeile, einfügen:

wenn Dateiname == 'ldd':
     Dateiname = os.path.join(os.path.dirname('/usr/bin/ldd'),os.readlink('/usr/bin/ldd')) 1TP3Z.B. '/lib/ld-musl-armhf.so.1' Fix für Alpine

Dritter Fehler

Fehler, benötigt 'chrpath' auf Ihrem System, aufgrund der 'RPATH'-Einstellungen in den verwendeten gemeinsamen

Reparieren:

Haben Sie chrpath wie oben beschrieben installiert?

apk chrpfad hinzufügen

Das war's - jetzt sollte nuitka seine Magie ohne Schluckauf vollbringen. Ich werde dem Autor von nuitka die Patches und diesen Blogartikel schicken, vielleicht kann er sie in nuitka einbauen.

Werksversion

Update 2.2.2019: Der Autor von Nuitka hat meine Patches (in leicht veränderter Form) hinzugefügt. In der aktuellen "Fabrik"-Version funktioniert die -Standalone-Kompilierung bei mir. Bitte beachten Sie diese Github-Seite für Details:

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

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