Vor einiger Zeit hatte ich schon einmal in Artikel das cross compiling unter Mac OS X für den Raspberry Pi mit Eclipse beschrieben. Wozu nun also noch einmal über das gleiche Thema schreiben? Nun ganz einfach, es geht noch viel mehr als nur Standard C/C++ und es gibt auch noch andere Targets. Des weiteren ist es mir gelungen, die Linaro ARM Toolchains, sowohl für den Raspberry Pi als auch für den BeagleBone Black, auf dem Mac zu kompilieren.
Der Artikel bezieht sich zwar hautsächlich auf das Cross Kompilieren unter Mac OS X, alle was im folgenden beschrieben wird kann aber auch eins zu eins auf linux angewendet werden.
ARM Toolchain für Mac OS X
Beginnen wir also zunächst mit Toolchain. Damit nicht jeder die Toolchain erneut kompilieren muss, habe ich sie als Binary bereitgestellt:
- Für den BeagleBone – arm-linux-gnueabihf 4.9-2014.05 for Mac OS X (37)
- Für den Raspberry Pi – arm-linux-gnueabihf-raspbian 4.9 2014.05 for Mac OS X (74)
Die beiden Versionen sollten auch für die jeweiligen Derivaten eingesetzt werden können.
Alle Versionen liegen im Package Manager Format, also als Installationsroutine, vor. Nach der Installation befinden sich dir Toolchains in den folgenden Verzeichnissen:
/usr/local/linaro/arm-linux-gnueabihf
– BeagleBone Black/usr/local/linaro/arm-linux-gnueabihf-raspbian
– Raspberry Pi
Nun kann man eigentlich schon mit dem cross kompilieren beginnen:
$ nano hello.c
Den populärsten Cod eingeben:
#include <stdio.h> int main(void) { puts("Hello World"); return 0; }
Die Datei speichern, nano Verlassen [ctrl+x] und danach kompilieren:
$ /usr/local/linaro/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -g -o hello hello.c
Nun noch testen ob alles funktioniert hat:
$ scp hello root@192.168.7.2:/root $ ssh root@192.168.7.2 # ./hello Hello World
Hat das alles so funktioniert könnte man gleich zu Eclipse übergehen. Aber zur Zeit verfügen wir lediglich über eine “prebuild sysroot”. Das bedeutet, wir können nur Code mit Standard C und Standard C++ erzeugen. Soll auf bereits vorhandene Linux Bibliotheken, wir Glib etc., zurückgegriffen werden, müssen diese erst kompiliert werden.
Erweiterte Sysroot
Es existieren ein Unzahl an Linux Bibliotheken, die ich natürlich hier nicht alle verwendet werden. Ich beschränke mich auf die gängigen bzw. auf diese die ich als gängig bezeichne. Ich erläutere hier nicht die Konfiguration sprich:
$ ./configure $ make $ make install
Sonder das Ganze ist in ein Bash Script verpackt, welches die erweitert Sysroot automatisch erstellt. Ursprünglich wollte ich nur die Bibliotheken Glib, Qt und OpenCV haben, jedoch besitzen diese wiederum weitere Abhängigkeit, so dass die Sysroot nun über die Folgenden Bibliotheken verfügt:
- Image Libraries:
- libjpeg – Independent JPEG Group’s JPEG runtime library
- limping – PNG library
- libtiff – Tag Image File Format library
- libraw – raw image decoder library
- liblcms2 – Little CMS 2 color management library development headers
- imagemagick – image manipulation library
- Audio and Video Libraries:
- gstreamer – pipeline-based multimedia framework
- gst-plugins-base – GStreamer libraries from the “base” set
- gst-plugins-good – GStreamer development files for libraries from the “good” set
- alsa – Advanced Linux Sound Architecture
- liboog – Ogg bitstream library
- libvorbis – Vorbis General Audio Compression Codec
- libtheora – Theora Video Compression Codec
- libvisual – Audio visualization framework
- wavpack – Audio codec (lossy and lossless) encoder and decoder
- taglib – Library for reading and editing the meta-data of audio formats 1.
- Compression Libraries:
- zlib – Compressing File-I/O Library
- bzip2 – High-quality block-sorting file compressor
- liblzma – XZ-format compression library
- Markup Language Libraries:
- expat – XML parsing C library
- libxml2 – GNOME XML library
- Hardware Libraries:
- tslib – Abstraction layer for touchscreen
- i2c-tools – Heterogeneous set of I2C tools for Linux
- bluez – Bluetooth protocol stack library
- Communication:
- libmodbus – Library for the Modbus protocol
- curl – Command line tool for transferring data with URL syntax
- Mathematic and Scientific Libraries:
- gsl – GNU Scientific Library
- gmp – Multi precision arithmetic library developers tools
- mpfr – Multiple precision floating-point computation developers tools
- opencv – Open Source Computer Vision Library
- Database:
- sqlite – Database management system libraries
- Assistance Libraries:
- Qt – Qt 4 development files and development programs for host 1.
- glib – GLib development library
- dbus – Simple interprocess messaging system
- libffi – Foreign function interface library
- ncurses – text-based user interfaces library
- readline – GNU readline and history libraries
- cryptodev – Access to Linux kernel cryptographic drivers
- openssl – Secure Sockets Layer toolkit
- liborc – Library of Optimized Inner Loops Runtime Compiler
- freetype – FreeType 2 font engine
- fontconfig – Generic font configuration library
- cairo – The Cairo 2D vector graphics library
- liblqr – Converts plain array images into multi-size representation
- libssh2 – SSH2 client-side library
- X11 Libraries:
- libX11 – X11 client-side library
- libXext – X11 miscellaneous extensions library
- pixman – pixel-manipulation library for X
- xtrans – X transport library
- xproto
- xextproto
- inputproto
- xcb-proto – X C Binding
- libpthread-stubs – pthread stubs not provided by native libc
- libXau – X11 authorisation library
- libgpg-error – Library for common error values and messages in GnuPG components
- libgcrypt – LGPL Crypto library
- libxslt – XSLT 1.0 processing library
- libxcb – X C Binding
- videoproto – X11 Video extension wire protocol
- kbproto – X11 XKB extension wire protocol
- libXv – X11 Video extension library
Zunächst muss das Script von Github geladen werden. Ich verwende hierfür einen Ordner Namens “Development” der in meiner Benutzerverzeichnis liegt, in dem wiederum Unterordner der einzelnen Targets vorhanden sind. Das sich dies innerhalb es Artikels fortführt hier die Kurze Erläuterung:
- Das Benutzerverzeichnis
/Users/USER_NAME
kürze ich im folgenden mit$HOME
ab. - Das Arbeitsverzeichnis für den BeagelBone Black lautet:
$HOME/Development/BeagleBone
- Das Arbeitsverzeichnis für den Raspberry Pi lautet:
$HOME/Development/Raspi
Die Installation der erweiterten Sysroot für den BeagleBone ist dann wie folgt:
$ cd $HOME/Development/BeagleBone $ git clone https://github.com/Illuminux/arm-cross-sysroot.git
Nach dem das Script und dessen zusätzliche Dateien geklont wurde, muss noch die Konfigurationsdatei angepasst werden:
$ cd arm-cross-sysroot $ cp config.cfg.sample config.cfg $ nano config.cfg
Die folgende Konfigurationsdatei zeigt die Standard Einstellungen für den BeagleBone Black:
# The variable BASE_DIR is the directory in which the main script is located. # Directory in which the sysroot should be installed SYSROOT_DIR="${BASE_DIR}/../sysroot" # Directory in which the downloads are to be loaded DOWNLOAD_DIR="${BASE_DIR}/../../cross-sysroot-downloads" # Directory where the toolchain is located (exclude bin directory) TOOLCHAIN_DIR="/usr/local/linaro/arm-linux-gnueabihf" # Target architecture TARGET="arm-linux-gnueabihf" # Target Board # softfloat: Default soft float boarde # hardfloat: Default hard float boarde # raspi: Raspberry PI # beaglebone: BeagleBone / Beglebone black # BORAD="beaglebone"
Die Bedeutung der einzelnen Parameter sind:
SYSROOT_DIR
: Ist das Verzeichnis, in das die erweitere Sysroot installiert werden soll. Der Ort kann nach der Installation nicht mehr verändert werden. Das Standart Verzeichnis währe “$HOME/Development/BeagleBone/sysroot” bzw. “$HOME/Development/Raspi/sysroot”.DOWNLOAD_DIR
: Ist das Verzeichnis, in das die Source Paket geladen werden. Es liegt auf der gleichen Ebene wie die Targets, so muss der Download bei verschiedenen Targets nur einmal durchgeführt werden ($HOME/Development/cross-sysroot-downloads).TOOLCHAIN_DIR
: Ist das Verzeichnis in dem die jeweiligen Toolchain des Targets liegen.TARGET
: Ist der Prefix des Cross Compilers z.B. arm-linux-gnueabihf bei arm-linux-gnueabihf-gcc.BORAD
: Ist das Target Board
Sind alle Parameter korrekt, kann mit dem Kompilieren der Sysroot begonnen werden:
$ ./sysroot-build.sh
Das Erstellen nimmt nun einige Zeit in Anspruch, man hat also zwei oder auch drei Stunden Pause. Ggf. ist es sinnvoll Time Machine für diese Zeit auszuschalten, da sonst alle Source Dateien ein Backup erhalten.
Cross Compiling mit Eclipse CDT
Sofern noch nicht gesehen muss erst einmal Eclipse installiert werden. Ich empfehle Eclipse in der Version Kepler zu installieren und zwar in der 32Bit Variante. Nach der Installation Eclipse öffnen. Ggf. muss noch Java installiert werden, hierzu wird man aber aufgefordert.
Als erstes muss nun des Target eingestellt werden. Es können hier mehrere Targets angelegt werden. Im Menü “Window” → den Eintrag → “Open Prespective” → “Other” wählen.
In dem Dialog “Remote System Explorer” (RSE) aktivieren und mit OK bestätigen. Wenn der Welcome Screen geschlossen wird, ist der RSE links dargestellt. Innerhalb des RSE das Kontextmenü aufrufen (Rechtsklick).
- Im Dialog “Linux” wählen und “Next” drücken.
- Hostname oder IP-Adresse eingeben und “Next” drücken.
- Files: Unter “Configuration” den Eintrag “ssh.files” wählen und “Next” drücken.
- Processes: Unter “Configuration” den Eintrag “processes.shell.linux” wählen und “Next” drücken.
- Shells: Unter “Configuration” den Eintrag “ssh.shells” wählen und “Next” drücken.
- SSH Terminals: Unter “Configuration” den Eintrag “ssh.terminals” wählen und “Finish” drücken.
In die Perspective C/C++ wechseln, Icon oben rechts und ein neues C++ Project anlegen (z.B. hello).
Als Project Typ “Executable” → “Empty Project” und als Toolchain “Cross GCC” wählen und “Next” drücken. Der nachfolgende Dialog kann auch gleich mit “Next” übersprungen werden.
In Cross GCC Command nun den Cross Compiler Prefix “arm-linux-gnueabihf-” eingeben und unter Cross Compiler Path den Pfad zu den Toolchain Binaries festlegen, z.B.:
- BeagleBone: /usr/local/linaro/arm-linux-gnueabihf/bin
- Raspberry: /usr/local/linaro/arm-linux-gnueabihf-raspbian/bin
Mit Finish den Dialog bestätigen und das Projekt ist angelegt. Mit “File” → “New” → “Source File” eine neue cpp-Datei (z.B. hello.cpp) anlegen und den folgenden Inhalt einfugen:
#include <cstdlib> #include <iostream> int main(void) { for(unsigned int i=0; i<10; i++) std::cout << "Hello World " << i+1 << "times" << std::endl; return EXIT_SUCCESS; }
Mit “Project” → “Build Project” oder [cmd+b] kann alles kompiliert werden. Um das Programm auf dem Target auszuführen wählt man “Run” → “Run Configurations”. Mit Rechtsklick auf “C/C++ Remote Application” klicken und im Kontextmenü den Eintrag “New” wählen. Unter “Main” den “Remote Absolute File Path for C/C++ Application”, z.B. “/root/hello”. Auf “Apply” klicken um alles zu übernehmen und mit “Run” das Programm auf dem Target Starten.
Soll der Debugger zum Einsatz kommen, wählt man “Run” → “Debug Configurations”. In “Main” geht man, sofern es nicht übernommen wurde, genau so vor wir bei “Run”. Zusätzlich wählt man noch die Registerkarte “Debugger” aus und stellt unter “GDB debugger”, den Debugger für das Target “/usr/local/linaro/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gdb” ein. Mit “Apply” alles übernehmen und mit “Debug” das Programm im Debugmodus auf dem Target ausführen. Hiebei lässt sich nun das Programm Zeile für Zeile ausführen.
Dies basierte nun alles auf Standard C++, der Aufwand mit der Sysroot kommt also noch nicht zum Tragen. Um den gleiche Source wie oben mit Glib zu erstellen muss Eclipse erst einmal der Ort der erweiterten Sysroot mitgeteilt werden. Rechtsklick auf das Projekt und den Eintrag “Properties” auswählen. Im Dialog den Eintrag “C/C++ General” → “Path and Symbols” auswählen und unter Includes die Schaltfläche “Add” klicken. Den Pfad zu den Sysroot Includes eingeben.
- “%HOME/Development/BeagleBone/sysroot/include”
- “%HOME/Development/BeagleBone/sysroot/include/glib-2.0″
- “%HOME/Development/BeagleBone/sysroot/lib/glib-2.0/include”
Das Kontrollkästchen “Add to all Languages” aktivieren und OK klicken. Danach “Library Path” wählen und den Pfad zu den Sysroot Libs eingeben:
- “%HOME/Development/BeagleBone/sysroot/lib”
Nun noch die benötigten Libraries eingeben:
- glib-2.0
- pthread
#include <stdlib.h> #include <glib.h> int main(void) { for(guint i=0; i<10; i++) g_print("Hello World %d times\n", i+1); return EXIT_SUCCESS; }
Zugegeben für das Beispiel mach der Aufwand keinen Sinn, der kommt erst bei größeren Projekten zum Tragen.
Cross Compiling mit Qt Creator
Zunächst muss erst einmal der Qt Creator installiert werden. Auch wenn in der Sysroot die Version 4.8.6 installiert wurde kann dennoch der aktuelle Qt Creator 3.2.1 for Mac, der eigentlich für Qt in der Version 5 oder höher bestimmt ist, installiert werden (minimum Qt Creator 2.6.2). Nach erfolgreicher Installation, Qt Creator öffnen und das Menü “Qt Creator” → “Einstellungen” aufrufen.
Nun definiert man erstmal ein Gerät, sprich das Target. Dazu links unten “Gerät” wählen und rechts oben “Hinzufügen” klicken.
Im Dialog “Generisches Linux-Gerät” wählen und auf “Assistenten starten” klicken.
Die Verbindungsdaten für das Target eingeben und Weiter drücken.
Den nachfolgenden Dialog mit “Fertig” beenden, es erscheint ein Testdialog. Nachdem der Test erfolgreich abgeschlossen ist, auf “Schließen” drücken.
Im Hauptdialog, links unten, auf “Anwenden” drücken um ales zu speichern.
Danach den Eintrag “Erstellung und Ausführen” wählen und dort auf “Compiler”, “Hinzufügen” und “GCC” drücken. Einen beliebigen Namen eingeben und unter Compiler Pfad “/usr/local/linaro/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++” eintragen (BeagleBone). Danach links auf Anwenden drücken. Kleiner Tip [cmd+shift+h] zeigt versteckte Dateien an.
Nun auf “Qt Version” hinzufügen drücken und die entsprechende Qt Version auswählen:
/usr/local/Trolltech/Qt-4.8.6-beaglebone/bin/qmake
für BeagleBone/usr/local/Trolltech/Qt-4.8.6-raspi/bin/qmake
für Raspberry Pi
“Debugger” wählen und den Pfad für den Debugger eingeben:
/usr/local/linaro/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gdb
für BeagleBone/usr/local/linaro/arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gdb
für Raspberry Pi
Zum Schluss auf “Kits” drücken und die zuvor getroffenen Parameter einstellen, wie unten abgebildet am Beispiel eines BeagleBon Blacks.
Bevor wir nun ein Programm erstellen und auf dem Target laufenlassen können, müssen noch einige Modifikationen des Target vorgenommen werden.
Vorbereitung des Targets für Qt
Zum Verständnis, die Qt Version die das Script kompiliert hat, ist die embedded Qt Version mit eigenem Framebuffer. Die Qt Version in den Debian Paketquellen setzt ein Fenstermanager voraus. Dies bedeute man kann Qt nicht aus den Paketquellen installieren, sondern es müssen die kompilierten Bibliotheken auf das Target kopiert werden. Nachfolgend am Beispiel des BeagleBone Black:
$ ssh root@192.168.7.2 # mkdir -p /usr/local/Trolltech/Qt-4.8.6-beaglebone # exit $ scp -r /usr/local/Trolltech/Qt-4.8.6-beaglebone/lib/ root@192.168.7.2:/usr/local/Trolltech/Qt-4.8.6-beaglebone $ scp -r /usr/local/Trolltech/Qt-4.8.6-beaglebone/plugins/ root@ 192.168.7.2:/usr/local/Trolltech/Qt-4.8.6-beaglebone
Damit die Bibliotheken auch gefunden werden, muss der Pfad in die Umgebungsvariable PATH eingetragen werden.
$ ssh root@192.168.7.2 # nano /etc/profile
In der Datei die Zeile 5 und 7 durch den Pfad “/usr/local/Trolltech/Qt-4.8.6-beaglebone/lib” erweitern.
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1)) # and Bourne compatible shells (bash(1), ksh(1), ash(1), ...). if [ "`id -u`" -eq 0 ]; then PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/Trolltech/Qt-4.8.6-beaglebone/lib" else PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/usr/local/Trolltech/Qt-4.8.6-beaglebone/lib" fi export PATH
Da die libffi in der Version 6 installiert wurde, die Version 5 ließ sich unter OS X nicht konfigurieren, muss auch diese auf das Target kopiert werden.
$ scp Development/BeagleBone/sysroot/lib/libffi.* root@192.168.7.2:/usr/lib/
Da bei einem frisch installierten BeagleBone Black und auch beim Raspberry Pi bereits ein Window Manager läuft, muss dieser entfernt werden.
$ ssh root@192.168.7.2 # apt-get remove lightdm # reboot
Das Target ist nun einsatzbereit.
Qt auf dem Target testen
Nun kann ein Neues Projekt erstellt werden. Dazu “Datei” → “Neu” wählen. Im Dialog “Anwendungen” → “Qt-Widgets-Anwendung” markieren und in dem Dropdown Menü, oben rechts, Vorlage für Embedded Linux wählen, danach auf den Button “Auswählen” klicken.
Dem Projekt einen Namen geben (z.B. Demo), im gewünschten Ordner speichern und “Weiter” drücken. Kleine Empfehlung, legt einen Unterordner, mit dem gleichen Namen wie euer Projekt, an und speichert es darin. Nun kommt das Schöne an Qt Creator, man kann ein und das selbe Projekt für verschiedene Targets und auch den Host PC anlegen und innerhalb des Projektes ganz einfach wechseln. (Weiter zum nächsten Dialog)
Nun der Klasse noch ein Name gegeben, ich belasse es für die Demonstration bei der Vorgabe “MainWindow”, was man bei einem regulären Projekt niemals machen sollte. Drücke weiter und bestätige den nachfolgenden Dialog mit Fertig.
Es erscheint nun unser Hauptfenster, in dem sich bereits eine Fenster-Anwendung befindet. Durch drücken des Hammers kann und sollte diese kompiliert werden können.
Hat das funktioniert kann durch drücken auf den grünen Pfeil das Programm auf das Target übertragen und dort ausgeführt werden. Zuvor muss allerdings noch die Projektdatei angepasst werden, so dass sie für das Demo wie folgt aussieht:
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = Demo1 target.files = Demo1 target.path = /root INSTALLS = target TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp HEADERS += mainwindow.h FORMS += mainwindow.ui
Zusätzlich muss noch ein Kommandozeilenargument übergeben werden. Dazu auf “Projekte” → “Erstellung und Ausführung” → “Ausführung” klicken und im Feld “Argumente” den Parameter “-qws” eintragen.
Drückt man nun auf ausführen, sollte die Anwendung auf dem Bildschirm des Targets erscheinen.