Emuliertes Compilen > Cross-Compilen

Intro⌗
Himmel, Arsch und Zwirn! Wer kennt nicht diesen Hans-Entertainment-Moment, wenn man eine gute Woche damit verbringt, seinem Rechner beim Compilen zuzugucken, nur um immer wieder zu sehen, wie er abkackt. – Nein. Ich will nicht mehr. Ich kann nicht mehr. Ich halte das alles nicht mehr aus. Irgendwie muss das doch gehen. Eine andere Lösung muss her. Und zwar eine (objektiv gesehen) stümpfere! Denn Stumpf ist Trumpf!
Der Linker ist verwirrt? Ich auch!⌗
Konkret ging es bei mir darum, ein rust-Projekt von einem amd64
-Host nach aarch64
zu kompilieren. Was kann da schon schief gehen? So einiges, wenn das besagte Projekt von externen C-Bibliotheken abhängt, die auf dem CISC-Host vorliegen, in der RISC-Toolchain aber nicht. Oder andersherum. Oder wenn CISC-Host Libraries zum Herunterladen von Quellen zum Bauen von RISC-Libraries benötigt werden. Genauer: ein build.rs
-Skript, welches mittels dem curl
-crate auf openssl
(C-API, amd64
-Host) aufbaut, um Quellen für das aarch64
-Projekt vorzubereiten… 😠
Oder wie auch immer. Solche Probleme gäbe es jedenfalls nicht, wenn besagte C-Bibiliotheken einfach in Rust vorlägen. Deshalb -> Have you considered rewriting it in rust? \s
Zurück in die Realität⌗
Ein informierter rust-Hacker wird wahscheinlich wissen, dass man sich bei solchen cross-compile Komplikationen mittels dem crate cross
kurieren kann.1 Und tatsächlich habe ich jenes auch immer regelmäßig verwendet, um meinen Discord-Bot2 zu übersetzen. Nach dem besagten, vom Scheitern gezeichneten Zeitraum habe ich jedoch eine deutlich stümpfere Klinge gewählt:
Ich emuliere einfach ein komplettes
aarch64
-System, von vorne bis hinten.
Andocken 👉👌⌗
In meinem Fall habe ich mich dafür entschieden, Dockerfiles zu verwenden; Bis dato für mich ziemliches “Neuland”. Hier das Produkt: (Props für die Inspiration hierhin!)
Hier können nach belieben noch weitere Abhängigkeiten hinzugefügt werden; Eben genau diejenigen, welche man beim Compilen auf einem 64-Bit Raspberry verwenden würde.
Aus diesem Dockerfile muss nun ein Image gebaut werden. Zusätzlich wird jedoch QEMU
als Emulator benötigt.
-> Quick-Start für Arch:
$ pacman -Syu qemu-system-aarch64 qemu-user-static qemu-user-static-binfmt
Und natürlich Docker selbst, falls noch nicht vorhanden: (Änderungen mittels usermod
werden erst nach einem re-login wirksam)
$ pacman -Syu docker
$ usermod -aG docker $USER
$ systemctl enable --now docker.socket
-> Image bauen:
$ docker build -t rpios64 -f Dockerfile .
Wobei rpios64
das “tag”, mehr oder weniger der “Name” des erstellten Images ist, Dockerfile
der Pfad zum Dockerfile und der Punkt .
am Ende das Build-Directory des Docker-Images festlegt. Wenn man sonst keine externen Dateien in sein Image spielt, ist dieser Verzeichnispfad aber nicht unbedingt weiter interessant.
-> Emulierte cargo
-binary aufrufen
$ cd in/das/rust/projekt
$ docker run --volume .:/build rpios64:latest \
cargo build --manifest-path=/build/Cargo.toml \
--target-dir=/build/target/rpios64
Enter drücken und beten!
Anmerkungen zu den Argumenten:
--volume .:/build
: Da der Quellcode im Docker-Image nicht vorhanden ist, wird hiermit das Projektverzeichnis.
im Image unter dem absoluten Pfad/build
eigehängt.--manifest-path=/build/Cargo.toml
: Damitcargo
im Image weiß, wo unser eingehängtes Projekt liegt.--target-dir=/build/target/rpios64
: Hiermit wird das Ausgabeverzeichnis festgelegt. Besonders nützlich, da bei der Emulationcargo
nicht automatisch Ordner für die angegebenen Targets anglegt, wie es sonst der Fall mitcross-rs
wäre; Ohne--target-dir
würden die Buildartefakte in./target/debug
liegen – also die gleiche Stelle, wo Artefakte voncargo build
ohne jegliche Emulation zu finden wären. Save your build-cache people!
Eine Nebenwirkung dieser Methode ist, dass ./target/rpios64
nun root
gehört. Warum? Noch nicht rausgefunden. Lässt sich aber einfach lösen:
$ chown -R $USER:$USER target
??? Profit (?)⌗
Eine weitere Nebenwirkung dieser Methode ist, dass das Bauen wirklich ewig dauert. Vorallem wird das im Vergleich zu einer vernünftig konfigurierten cross-toolchain deutlich, die halt dann doch Compiler im nativen Befehlssatz des Hosts zur Verfügung stellt.
Aber hey: ich finde einen erfolgreichen Build, auf den man etwas länger gewartet hat besser, als ein paar schnellere, die fehlschlagen. In der Zwischenzeit kann man ja schließlich einfach mal die Beine hochlegen. 😉
Oder Kaffee kochen. ☕
Oder an die frische Luft gehen?? 🤔