====== RPi build tools ====== > Immagini Docker per cross-compilare per il Raspberry Pi. > Documentazione nel [[https://git.mittelab.org/5p4k/rpi-build-tools|sorgente]], guida all'uso nel [[https://hub.docker.com/r/5p4k/rpi-cross|Docker hub]]. Ho un altro progetto per il Raspberry Pi, [[pwmpi]], che però ho scritto in C++17, quando il massimo che c'era sul Raspberry Pi era gcc 4.7. Da lì, la necessita di cross-compilare per il Raspberry Pi con compilatori più recenti. Adesso sul Raspberry Pi ci sono compilatori decisamente più aggiornati, tuttavia la convenienza di precompilare (cross-compilare, in questo caso) resta imbattuta, soprattutto per semplicità. Per compilare servono librerie, header, un compilatore che supporti l'architettura di destinazione, per cui perché dev'essere complicato? E invece... Ci sono diverse tecniche reperibili online per crosscompilare per Raspberry Pi. * Compilare direttamente su un'[[https://hub.docker.com/r/raspbian/stretch|immagine ARM di Raspbian]] in emulazione con QEMU (estremamente lento, vincolato alla toolchain distribuita con Raspbian) * Costruire un'intera toolchain dedicata e.g. con [[https://crosstool-ng.github.io/|crosstool-ng]] o [[https://buildroot.uclibc.org/|buildroot]] * Innumerevoli progetti scaricano una **sysroot** (l'insieme delle librerie e degli header) precostruita da qualcuno, scaricano o compilano una versione di GCC per ARM e crosscompilano * Da notare che GCC stesso dev'essere **compilato** specificamente per ARM, il che è un po' controintuitivo secondo me, dato che concettualmente, come in LLVM/Clang, la parte specifica per l'architettura di destinazione è solo una parte dell'intero processo di compilazione (quella finale di ottimizzazione e output). Per cui ho cercato di ripercorrere la strada più diretta, creando una sysroot a mano e usando Clang invece di Gcc come compilatore sul sistema host. * **Data inizio:** 24 nov. 2018 * **Data fine:** 7 apr. 2020 * **Stato:** completo (aggiornamenti possibili) [[https://git.mittelab.org/5p4k/rpi-build-tools|Sorgente]] [[https://hub.docker.com/r/5p4k/rpi-cross|Docker hub]] ==== Considerazioni su ARMv6 e Raspbian ==== All'inizio ero abbastanza confuso sulla differenza tra Raspbian e Debian, entrambi con tecnologia ''armhf''. Sostanzialmente, Raspbian è compilato apposta per ARMv6, mentre lo standard ''armhf'' di Debian è in realtà ARMv7. Suppongo che funzioni fondamentalmente su tutte le periferiche eccetto il Raspberry Pi 1. Ad ogni modo scovato l'inghippo, ho reperito anche le [[https://raspberrypi.stackexchange.com/a/91653/72884|compile flags]] per Raspbian. ==== Costruire una sysroot ==== Quale sysroot migliore di quella direttamente installata sul sistema operativo? Sfruttando la capacità di ''apt'' di scaricare pacchetti di varie architetture, ho assemblato [[https://git.mittelab.org/5p4k/rpi-build-tools/-/blob/master/rpi-cross/scripts/rpi-sysroot.sh|uno script]] che scarica (in [[https://git.mittelab.org/5p4k/rpi-build-tools/-/blob/master/rpi-cross/sysroot.Dockerfile|un'immagine Debian x86_64]]) i pacchetti di Raspbian e li scompatta in una cartella. Con un po' di trial and error, su un'immagine Docker con QEMU e su un vero Raspberry Pi, ho ridotto la [[https://git.mittelab.org/5p4k/rpi-build-tools/-/blob/master/rpi-cross/_buster/packages.list|lista dei pacchetti necessari]] per una sysroot a 15 soltanto (11 senza libc++). Eseguibili e archivi compressi si possono scartare; forse anche di più. Sostanzialmente rimangono librerie ed header. ==== Una sysroot basata su LLVM? ==== Originariamente, nell'interesse di ottenere una soluzione rapida, invece di scompattare i ''.deb'' di Raspbian, avevo pensato a costruire una sysroot basata su LLVM, [[https://git.mittelab.org/5p4k/rpi-build-tools/-/blob/master/legacy/scripts/cross-build-libcxx.sh|crosscompilando tutti i vari componenti di LLVM]]. D'altronde, Clang sarebbe stato comunque il compilatore scelto, per cui perché non usare l'intero progetto? Tuttavia questo al momento non è possibile su ARMv6, perché ''compiler-rt'' (che genera files come ''crtbegin.o'', che vengono segretamente linkati dentro tutti i programmi C++, dato che contengono parti di runtime necessario) non [[https://bugs.llvm.org/show_bug.cgi?id=39906|supporta ARMv6]] correttamente. ==== Cross-compilare con una sysroot ==== Installata la sysroot in ''/usr/share/rpi-sysroot'', installato Clang su una distro (Debian, ma anche Alpine), dovrebbe essere relativamente semplice, dato che Clang su Linux di default supporta anche ARM. Non è proprio così, perché bisogna dire al compilatore dove trovare gli header. A quanto pare, la "procedura standard" per reperire la libreria standard del C e del C++, consiste nel... testare i percorsi che sono stati specificati al momento in cui il compilatore stesso è stato compilato. Sembra che questi vengano hardcodizzati nel compilatore. La maniera migliore, è dargli soltanto la sysroot, e sperare che Clang ce la faccia a trovarli. In realtà ce la fa a trovare la libreria standard del C++, ma non quella del C. Per quella è servito un bel po' di trial and error finché non ho trovato [[https://git.mittelab.org/5p4k/rpi-build-tools/-/blob/master/rpi-cross/_buster/RPiStdLib.cmake|un percorso unico]] da passare che non rompesse le dipendenze nascoste tra i vari header. Qui da notare, su Debian il problema non era emerso perché Clang utilizzava gli header installati sul sistema host stesso (!) automaticamente. Per fortuna, cercare una soluzione minimale è utile, perché Alpine si è immediatamente rifiutato di compilare. I risultati delle peripezie sono in realtà [[https://git.mittelab.org/5p4k/rpi-build-tools/-/blob/master/rpi-cross/sysroot/RPi.cmake|poche righe di CMake]]. Inestimabile la documentazione di CMake su che variabili usare. ==== Linkare con una sysroot ==== Questo merita una menzione a parte, perché anche linkare non può esser fatto con ''ld'', ma dev'essere fatto ovviamente con la controparte di LLVM, ''lld'' (naming...). Per questo basta passare ''-fuse-ld=lld''' al linker, ma attenzione! Questo si applica soltanto a librerie standard, librerie dinamiche ed eseguibili. Per le librerie statiche [[https://cmake.org/cmake/help/git-stage/variable/CMAKE_STATIC_LINKER_FLAGS.html#variable:CMAKE_STATIC_LINKER_FLAGS|a quanto pare]], l'assemblaggio del binario finale è fatto con ''ar'', per cui nel caso di ''CMAKE_STATIC_LINKER_FLAGS'', non c'è bisogno di specificare LLD.