Serie: Embedded Linux - Yocto

  Götz   Lesezeit: 14 Minuten  🗪 2 Kommentare

Im zweiten Teil der Serie wird der System-Builder Yocto vorgestellt.

serie: embedded linux - yocto

Was ist Yocto?

Wie in Teil 1 der Serie schon eingeführt, decken die auf dem Markt vorhanden Distributionen nicht die Vielfalt der Hardware & Software ab die existiert. Es ist notwendig praktisch für jedes Produkt eine Sonderlösung, aus eigenem Kernel & Userland zu erstellen. Auf dem Markt existieren dazu zwei etablierte Lösungen: Buildroot und Yocto. Mit Buildroot werden wir uns in dieser Serie nicht befassen, deswegen gibt es nur einen kleinen Abriss: Buildroot erstellt mit einer Reihe an Shellskripten und Makefiles ein Image, welches auf ein eingebettetes System aufgespielt wird. Es ist das Ältere von den beiden Systemen und steht unter keiner Schirmherrschaft eines grossen Herstellers.

Yocto kann am ehesten mit dem Nix Paketmanager verglichen werden. Es kümmert sich um das Laden der Quellen, Bauen des Quellcodes zu ausführbarer Software und dem entsprechenden Verpacken in eines der gängigen Paketformate. Zwischen den einzelnen Paketen lassen sich Abhängigkeitsbeziehungen definieren, die sich auch noch durch Optionen dynamisch anpassen lassen. Ein vollständiges Linux (from Scratch) ist damit sehr aufwendig zu definieren, da es keinen definierten Startpunkt gibt. Damit diese Abstraktionsleistung nicht durch jeden Einzelnen neu erbracht werden muss, gibt es das Projekt Yocto. Yocto hat einen Grundstock erstellte, der es einfach macht Software für die gängigen Hardwareplattformen zu adaptieren. Der Entwicklergemeinschaft wird damit ein Werkzeugkasten an die Hand gegeben Software auf eine spezifizierte Art und Weise in das System einzupflegen und zu nutzen. Yocto definiert spezifische Funktionen wie eine Lösung auszusehen hat. Dennoch bleibt viel Arbeit übrig, es müssen tausende Module für unzählige Bibliotheken geschrieben werden. Mit Yocto erhält man nur eine Schablone, ausmalen muss man jedoch alles selbst.

Auch hier hat die Entwicklergemeinde eine Lösung hervorgebracht: OpenEmbedded. Das Projekt selbst war eine Nebenentwicklung und hat sich im Laufe der Zeit als vollwertiges Framework für Buildsysteme, Bibliotheken und Programme für eingebettete Systeme etabliert. Im Jahr 2011 hat es sein Framework an Yocto angepasst, sodass es zukünftig dessen Werkzeuge und Interfaces verwendet. Beide Projekte sind formal getrennt, aber sie stehen gemeinsam unter dem Dach der Linux Foundation und kooperieren eng miteinander.

Aufbau Yocto

Yocto bietet eine Referenzimplementierung namens Poky an. Poky bezeichnet dabei nur ein Layer (dazu später mehr). Zu diesem Layer wird das Werkzeug Bitbake und das Standardlayer meta ausgeliefert. In meta sind verschieden Buildsystemen, Paketierungssystemen, Programmiersprachen, Bootloader und vielen mehr enthalten. Es implementiert für die Programmiersprachen und Werkzeuge Schnittstellen die Bitbake später im Bauprozess aufruft. Damit muss beim Entwickeln nicht mehr der korrekte Aufruf eines cmake Projekts konfiguriert werden. Es steht somit ein einheitliches, umfassendes, vereinfachendes Interface bereit. Dies gilt auch für weitere Sprachen, wie Rust und Go. Eine ähnliche Beschreibung sind für verschiedene Buildwerkzeuge, den Kernel und diverse Bootloader enthalten. Weitere Beschreibungen sind im OpenEmbedded Projekt oder in Layer von Drittanbietern (Bsp. NXP) enthalten. Je nachdem welche Software das System benötigt lädt man entsprechende Layer, passend zur Version, nach und bindet diese in sein Projekt ein. Möchte man z. B. ein Stück Qt-Software bauen, so muss das Layer meta-qt eingebunden werden. Darin sind alle Werkzeuge (Bsp. qmake) und Bibliotheken enthalten. Das Projekt OpenEmbedded bietet ein Standardframework an, das heruntergeladen und eingebunden werden muss. Dieses heisst meta-openembedded und enthält seinerseits weitere Layer die einzeln, je nach Wunsch oder Abhängigkeit, eingebunden werden können (meta-python, meta-perl, ...).

Was ist ein Yocto-Layer?

Ein Layer ist im Prinzip eine Sammlung von Rezepten (auch dazu später mehr) von Programmen und Bibliotheken die thematisch zueinander passen. Das Layer meta enthält die absolute Basis, wie das Rezept für die Glibc. Das Layer meta-python enthält alle notwendigen Rezepte zum Erstellen eines Python-Interpreters und Schnittstellen gegenüber diverse Drittbibliotheken, sowie eine Abstraktion zum schnellen Einbinden von Python Paketen aus dem PyPi Repository. Natürlich benötigt auch meta-python Software aus dem Layer von meta und ist entsprechend abhängig davon. Die meisten Layer sind von mehreren Layern abhängig.

Was ist ein Rezept?

Als ein Rezept wird eine Textdatei bezeichnet, die aus einer Reihe von Anweisungen ein Programm oder Bibliothek baut. Benötigt ein Programm eine Bibliothek, so wird dessen Rezept als Abhängigkeit referenziert. Die Abhängigkeit muss dann als erstes gebaut werden. Bitbake löst die Abhängigkeiten auf und startet die Buildprozesse in korrekter Reihenfolge, warnt aber auch wenn sich z. B. zirkuläre Abhängigkeiten ergeben: Rezept A ist von Rezept B abhängig, welches aber auch von Rezept A abhängig ist. Rezepte werden in einer Python/Shell ähnlichen Syntax verfasst. Dies ist dem Buildsystem Bitbake geschuldet, welches selbst in Python geschrieben wurde und nativ den Python-Code ausführt. Der Vollständigkeit halber seinen auch noch Klassen erwähnt. Eine Klasse stellt dabei eine Reihe von Funktionen, Abstraktionen oder Reimplementierung der Standardfunktionen von Yocto oder Bitbake dar. Sie ermöglichen das Verwenden von Buildwerkzeugen wie Cargo, Cmake oder Python über spezielle Interfaces. Jedes Rezept bindet automatisch binutils, gcc und make ein.

Rezepte sind enorm vielfältig und können sich in ihrer Komplexität und Aufbau stark voneinander unterscheiden. So gibt es auch Rezepte für Pakete, die nativ auf dem Host laufen, um zum Beispiel Programme zu bauen, die für das Paketirenn von RPM Paketen benötigt wird, oder das Bauen eines eigenen Rust-Compilers. Yocto verzichtet weitestgehend auf die Werkzeuge des Hosts und beschränkt sich auf diese die zum Erstellen eigener Werkzeuge benötigt werden.

Beispiel: Btop++ - wie funktioniert ein Buildprozess?

Ein einfaches Rezept wird anhand des Programms btop erläutert. Btop (besser btop++) wurde auf Linuxnews.de schon mal vorgestellt.

DESCRIPTION = "Resource monitor that shows usage and stats for processor, memory, disks, network and processes."
SECTION = "tools"
DEPENDS = "sed-native"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=96af5705d6f64a88e035781ef00e98a8"

SRCREV    = "c3eb02f27f69f2cd03d5ec9ddc8ce7dc8eda3de8"
SRC_URI = "git://github.com/aristocratos/btop.git;branch=main"

S = "${WORKDIR}/git"

do_compile(){
    oe_runmake -e
}

do_install(){
    oe_runmake install DESTDIR=${D}
    chown root:root -R ${D}/usr/local/
}

FILES:${PN} = "/usr/local/"

Jedes Rezept besteht grob aus drei Funktionen: do_fetch, do_compile und do_install. Entsprechend der Klassen die eingebunden werden verändert sich die Ausführung der Funktionen, sie werden durch die Klasse überschrieben. In diesem Rezept werden keine Klassen eingebunden, demnach verarbeiten wir hier ein C/C++-Programm mit Makefile (der Standard in einem Rezept). Die Funktionen stehen in einer Abhängigkeitsbeziehung, sie werden in einer festen Reihenfolge ausgeführt. Tritt während einer Ausführung ein Fehler auf, bricht der Bauprozess mit einer Fehlermeldung ab. Nach der Korrektur oder Änderung einer Funktion werden alle betroffenen Funktionen erneut ausgeführt. Bei der Implementierung muss demnach darauf geachtet werden, dass die Funktionen keine Seiteneffekte auf nachfolgende oder vorausgehende Funktionen haben, zusätzlich muss ihre Ausführung idempotent, also atomar, sein. Im Folgenden eine Auflistung der Variablen, die in diesem Rezept gesetzt werden:

  • DESCRIPTION: Eine einfache Beschreibung der Software, welche gebaut wird.
  • SECTION: Beschreibt die Zuordnung der Software einer Kategorie, hier entsprechend „tools".
  • DEPENDS: Abhängigkeiten die zur Laufzeit des Build-Prozesses verfügbar sein müssen. Da die Software aus dem Host-Betriebsystem nicht verwendet wird muss für jedes Rezept die Software als natives Rezept eingebunden werden, welche über den Standardsatz hinausgeht.
  • LICENSE: Die Lizenz der Software.
  • LIC_FILES_CHKSUM: Die Datei aus den Quellen und deren Checksumme in der die Lizenz benannt wird.
  • SRCREV: Der Hash des Commits des Repository von dem die Quellen von btop++ heruntergeladen werden.
  • SRC_URI: Die URI des Quellcodes. Hier wird ein Git-Repository als Quelle referenziert, der Branch auf dem der Commit gesucht werden muss wird als Parameter mitangegeben.
  • S: Ist die Abkürzung für „Source" und bezeichnet das Verzeichnis, in dem die Quellen entpackt werden sollen.
  • WORKDIR: Bezeichnet das Verzeichnis in dem alle Quellen, Buildordner und das fertig gepackte Archiv liegt.
  • D: Repräsentiert das spätere Wurzelverzeichnis und ist für dieses Programm das Installationsverzeichnis. Ist bis auf die installierte Software leer und wird anschließend zu einem Paket geschnürt.
  • FILES:${PN}: Eine Hilfsvariable, die später dem Paketmanager alle Ordner und/oder Dateien zeigt, die dieser paketieren soll. Die spezielle Syntax kann hier nachvollzogen werden.

Die Variablen werden beim Parsen des Rezeptes gesetzt und stehen in den Funktionen zur Verfügung.

do_fetch ist in diesem Rezept nicht vertreten, damit verwenden wir hier die Standardimplementierung von Yocto. Die Funktionen do_compile und do_install werden hingegen von unserem Rezept überschrieben. Für den Bauprozess erzeugt Yocto ein eigenes Sysroot (Wurzelverzeichnis). Darin werden alle Werkzeuge, Cross-Compiler und Bibliotheken abgelegt. Damit wird sichergestellt, dass keine nicht explizit oder als Abhängigkeit definierte Software den Buildprozess beeinflusst. Meiner Meinung ist das, dass Kernfeature von Yocto. Man kann sich sicher sein, dass nur die tatsächlich in Abhängigkeit stehende und als Abhängigkeit benannte Software genutzt wird.

In der Funktion do_install findet sich nur der Aufruf oe_runmake -e, dieser ist Synonym zu make -e und ruft Make mit der Anweisung auf, mit allen Variablen der Shell die Variablen des Makefiles zu überschreiben. In diesem Fall versucht das Makefile nicht selbst die Pfade zu ermitteln, sondern nutzt die korrekten Pfade für den Cross-Compiler und Bibliotheken.

Die Funktion do_install installiert sämtliche erzeugte Software in ein Installationsverzeichnis. Dieses wird, wie schon beschrieben, zu einem Paket geschnürt. Für btop++ nutzen wir wieder die Yocto eigene Abstraktion des Befehls Make. Die hier verwendeten Argumente können aus der btop++ Installationsanleitung entnommen werden.

Ihr seht, die Erstellung eines eigenen Rezepts ist gar nicht so schwierig. Im Prinzip kann mit jeder Software so verfahren werden. Natürlich wird es mit komplexerer (oder schlechterer) Software immer aufwendiger diese an das Buildsystem anzupassen, vor allem da man an die Bibliotheken und Strukturen von Yocto gebunden ist. Es gibt zwar Möglichkeiten veraltete Bibliotheken zu installieren, aber vor allem bei hoch integrierten Komponenten (X-Server) wird das schnell sehr fehleranfällig.

Online findet man mit dem Layerindex verschiedene Layer, Rezepte und Klassen aus verschiedensten Quellen. Die Datenbank hält eine Vielzahl an Versionen und ein einfaches Interface bereit nach bestimmten Layern, Rezepten oder Klassen zu suchen. Jedes Rezept wird auch hinsichtlich seiner definierten Felder und Abhängigkeiten durchsucht, korrekt aufgelöst und übersichtlich angeordnet angeboten. Der Layerindex ist die erste Anlaufstelle, wenn man nach Rezepten sucht.

Kommt da noch mehr?

Ja! Ich habe hier nur einen kleinen Ausschnitt von dem Yocto Projekt vorgestellt. Neben den Rezepten gibt es noch eine eigene Konfigurationssprache für das Beschreiben der Layer, Distributionen und Konfigurationen. Es gibt unzählige Variationen der Rezepte, die verschiedenste Teilprobleme lösen (erzeugen eines Images, Konfigurationen, ...). Auch eine eigene Syntax zum Partitionieren für Images existieren. Das Yocto Projekt ist zum Glück sehr gut dokumentiert und dessen Mega-Manual ist ein guter erster Anlauf.

Das Projekt: SpotyPee

Wer jetzt heiss auf Yocto geworden ist und unbedingt ein eigenes Projekt damit umsetzten möchte, der sei gewarnt: Es gibt leider nicht viele gut dokumentierte Beispielprojekte für Yocto. Viele sind inzwischen veraltet, Erklärungen sind nicht ganz korrekt oder völlig falsch. Da ich selbst mit diversen Buildsystemen für eingebettete Systeme zu tun habe, konnte ich stark durch den beruflichen Kontext profitieren.

Ich versuche das nun mit meinem Bastelprojekt ein wenig zu ändern. Das Projekt läuft seit einiger Zeit bei mir zu Hause und ist auch im täglichen Einsatz.

Ich höre leidenschaftlich gerne Musik und gehöre zu der Sorte Mensch die es regelmässig nach neuen Stücken und Künstlern dürstet. Als Quelle neuer Musik dient mir dabei Spotify, welches mir mit seinen automatisiert (oder von Nutzern) erstellten Playlisten den Arbeitstag, die Freizeit oder Hausarbeit versüsst. Leider ist meine Anlage, eine Sony CMT-MX750Ni, nicht in der Lage Musik per Streaming durch Spotify abzuspielen. Die vorhandene begrenzte Streaming-Funktionalität ist derart wackelig zu bedienen, dass ich eine dauerhafte Verwendung schon längst aufgegeben habe.

Abgesehen von dem Mangel an Schnittstellen ist die Anlage voll funktionsfähig und viel zu schade zum Entsorgen.

Die Anlage hat zum Glück einen 3,5 mm Klinkenbuchse für Audiosignale. Mit einem USB-Audio-Wandler und einem Raspberry-Pi lässt sich diese wunderbar über Spotify einbinden. Wie genau das Projekt aufgebaut ist, erfahrt ihr in Teil 3 der Serie. Ich erkläre dort wie man sich ein eigenes Image mit Yocto generiert und die grobe Struktur des Projekts.

Tags

Embedded, Yocto, OpenEmbedded, Bitbake, System-Builder, Wettbewerb

Lars
Geschrieben von Lars am 24. Juni 2022 um 07:53

Moin, das Prinzip klingt nach Gentoo. Also der Distribution von www.gentoo.org, die komplett aus dem Source erstellt wird.

Die Variablen in den Rezepten ähneln auch den in den Gentoo ebuild Dateien.

JM2C

s_s
Geschrieben von s_s am 26. Oktober 2022 um 15:52

Einfach Super. Es sind sehr wenige Artikel für Yocto, die man auf deutsch findet. Ich hoffe, du schreibst weiter :)