recipetool
Nous avons vu comment ajouter des applications dont les recettes sont livrées avec Poky ou référencées sur Open Embedded Layers Index. Supposons que nous devions adapter de manière plus ou moins importante le contenu d’une de ces recettes.
Comment procéder sans toucher aux fichiers originaux ?
Nous allons le voir dans cette séquence...
Il y a plusieurs manières d’adapter un élément d’une image, qui dépendent du type de modification à effectuer. Notre première approche va consister à remplacer un fichier fourni par une recette.
Pour ce faire, on va commencer par rechercher dans l’arborescence de
Poky le répertoire où se trouve la recette, et examiner les fichiers
présents pour comprendre le mécanisme de construction. Prenons par
exemple l’application psplash
qui affiche une image lors
du boot du système.
[build-qemu]$ cd ../poky/ [poky]$ find . -name psplash ./meta/recipes-core/psplash ./meta-poky/recipes-core/psplash [poky]$
Surprise, deux répertoires sont consacrés à cette application. Le
premier concerne la recette d'origine de psplash
, et le
second traite de la personnalisation de l'application pour l'image
Yocto. Examinons leurs contenus :
[poky]$ ls meta/recipes-core/psplash/ files psplash_git.bb [poky]$ ls meta/recipes-core/psplash/files/ psplash-init psplash-poky-img.h psplash-start.service psplash-systemd.service [poky]$
Le répertoire de base contient un fichier recette au format
.bb
qui indique comment télécharger et compiler
l’application et un sous-répertoire files/
. Dans ce
dernier se trouvent un script de lancement psplash-init
utilisé quand le système démarre à la manière System V, des
fichiers de configuration servant quand le démarrage est géré par
Systemd et un fichier header d'extension
.h
qui contient l’image sous forme de tableau en C :
[poky]$ cat meta/recipes-core/psplash/files/psplash-poky-img.h /* GdkPixbuf RGB C-Source image dump 1-byte-run-length-encoded */ #define POKY_IMG_ROWSTRIDE (1920) #define POKY_IMG_WIDTH (640) #define POKY_IMG_HEIGHT (480) #define POKY_IMG_BYTES_PER_PIXEL (3) /* 3:RGB, 4:RGBA */ #define POKY_IMG_RLE_PIXEL_DATA ((uint8*) \ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" \ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" \ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" \ [...] "\377\377\377\377\377\223\377\377\377\322\376\377\374\3\372\374\371\265" \ "\276\306fx\203\306]oz\3as~\250\260\270\362\367\371\322\376\377\374\377" \ "\377\377\377\377\377\377\377\377\377\377\377\223\377\377\377\321\376" \ "\377\374\2\320\326\330p\203\216\312]oz\2hz\205\302\307\312\321\376\377" \ [...] "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" \ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" \ "\377\377\377\377\232\377\377\377")
Ce charabia apparent est bien la représentation d’une image graphique
pixel par pixel. Voyons l’autre répertoire — celui plus
spécifique Yocto Project — intitulé pspslash
:
[poky]$ ls meta-poky/recipes-core/psplash/ files psplash_git.bbappend
Il contient un fichier .bbappend
avec le
même nom que la recette .bb
du répertoire précédent. Il
s’agit d’une extension de recette, qui est prise en
compte après la recette principale et peut donc venir
modifier son contenu avant de l’interpréter.
Il est important de bien comprendre que bitbake
charge
d’abord en mémoire toutes les recettes et extensions en analysant leurs
contenus (étape «Parsing recipes
») avant
d’organiser le travail (étape «Initialising
tasks
») puis de réaliser les opérations nécessaires (étape
«Executing RunQueue Tasks
»). Il est donc
possible pour une extension de recette .bbappend
de
surcharger le contenu précédent d’une recette .bb
et de
modifier son comportement.
Voyons le contenu de cette extension :
[poky]$ cat meta-poky/recipes-core/psplash/psplash_git.bbappend FILESEXTRAPATHS_prepend_poky := "${THISDIR}/files:"
Il s’agit d’un contenu que l’on retrouve très souvent dans les fichiers
.bbappend
avec de légères variations. Nous l’avons vu dans
la séquence précédente, contrairement aux langages de programmation
habituels, il y a une véritable interprétation de la partie gauche
d’une affectation. Le caractère souligné (underscore)
«_
» sert à préfixer un opérateur qui précise
ou limite la portée de cette affectation.
La partie gauche signifie ici «Lors de la compilation d’une
image Poky» (_poky
) «ajouter au
début» (_prepend
) de la variable
FILESEXTRAPATHS
.
L’affectation «:=
» indique
que l’interprétation de la partie droite doit se faire dès la lecture
du fichier. Avec une affectation «=
», elle
serait différée au moment de l’analyse de toutes les variables lues.
Ceci nous garantit que l’on prend en compte immédiatement le contenu de
la variable THISDIR
. Comme son nom l'indique, cette
dernière représente le répertoire courant, celui de l'extension de
recette.
La partie droite de l’affectation permet de préciser le sous-dossier
files/
qui est juste à côté de la recette. On notera qu’il
est suivi d’un caractère deux-points «:
».
La variable FILESEXTRAPATHS
contient la liste des chemins
dans lesquels on recherche les fichiers nécessaires pour la réalisation
d’une recette. Les chemins de la liste sont séparés par des deux-points
«:
» et ils sont parcourus dans l’ordre de la
liste.
En ajoutant le répertoire files/
accompagnant cette
extension de recette au début de la liste, on s’assure que les fichiers
qu’il contient auront précédence sur ceux de la recette initiale.
Ici, le nom du répertoire (files/
) est le même que le
répertoire de la recette initiale, mais cela n'est pas obligatoire.
Voyons ce que contient ce nouveau répertoire files/
:
[poky]$ ls meta-poky/recipes-core/psplash/files/ psplash-poky-img.h [poky]$
Le fichier présent dans l’arborescence meta-poky/
indiqué
vient donc remplacer celui de l’arborescence meta/
. Ceci
permet d’afficher une image personnalisée au démarrage.
Nous pouvons réaliser le même type de modification pour afficher notre
propre splashscreen.
Créons un répertoire de travail dans notre layer, avec le même nom que la recette initiale :
[poky]$ cd ../meta-my-layer [meta-my-layer]$ mkdir -p recipes-core/psplash/files [meta-my-layer]$ cd recipes-core/psplash/files/ [files]$
Après avoir consulté la documentation de psplash
, je crée
avec Gimp une image de dimension 640×400 pixels que j’exporte au format
PNG. J’ai fait plusieurs essais pour obtenir la dimension qui me
convient (et qui est légèrement différente de l’originale). Vous pouvez
créer votre propre image ou
télécharger celle-ci.
[files]$ ls my-splash.png
J’utilise l’outil gdk-pixbuf-csource
(fourni sur ma
machine par le package libgdk-pixbuf2.0-dev
) pour
lui demander de convertir mon image PNG en fichier header pour
le langage C.
[files]$ gdk-pixbuf-csource --macros my-splash.png > psplash-poky-img.h
Il est nécessaire de modifier quelques éléments de l’image, que l’on
peut automatiser avec les lignes sed
suivantes :
[files]$ sed -i -e "s/MY_PIXBUF/POKY_IMG/g" psplash-poky-img.h [files]$ sed -i -e "s/guint8/uint8/g" psplash-poky-img.h
Nous avons obtenu
le fichier
header représentant l'image. Il nous faut créer
le fichier
d’extension de recette, de la même manière que celui de
meta-poky
.
[files]$ cd .. [psplash]$ nano psplash_git.bbappend
Le fichier ne contient que la ligne indiquant que le contenu du
répertoire files/
doit être plus prioritaire lors de la
recherche d’un fichier.
FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
Nous pouvons alors regénérer et tester notre image.
[psplash]$ cd ../../../build-qemu/ [build-qemu]$ bitbake my-image
Au démarrage, nous avons le plaisir de voir notre splashscreen personnalisé apparaître comme sur la figure II.3-1.
Nous voyons ainsi comment remplacer un fichier complet proposé par une recette. Cela peut être utile dans de nombreux cas, principalement pour des éléments de configuration système (nous le retrouverons par exemple pour ajuster la configuration du réseau).
Néanmoins d’autres modifications peuvent être nécessaires, celles qui consistent à modifier une petite partie d’un fichier. Par exemple quelques lignes d’un fichier source avant compilation. Pour cela on préfère la méthode du patch.
Nous allons commencer par produire et faire appliquer un
patch sur un fichier fourni directement par une
recette, sans qu'il y ait de compilation. Je vous propose par exemple
de prendre la recette «base-files
» qui se
trouve dans le répertoire meta/recipes-core/
de Poky.
Comme son nom l’indique il s’agit d’une recette qui fournit directement
des fichiers de base situés dans son sous-répertoire
base-files/
. L’un d’eux est
«profile
», qui est copié dans le répertoire
etc/
de la cible. Il configure des variables
d’environnement du shell comme PATH
, PS1
,
TERM
, EDITOR
… avec des valeurs par
défaut que l’utilisateur pourra surcharger s’il le souhaite.
La variable EDITOR
justement, qui indique l’éditeur
préféré de l’utilisateur. Son contenu est pris en compte par exemple
lorsqu'il faut enregistrer un message de commit pour
git
ou éditer un fichier de programmation horaire
(crontab
). Cette variable est initialisée à la valeur
«vi
». Mais nous avons installé sur notre image
l'éditeur nano
, il serait dommage de ne pas en profiter.
Créons donc un patch pour modifier cette ligne du fichier.
Lorsque le patch à créer concerne les fichiers fournis par une
recette, comme c'est le cas ici, le plus simple est d'appeler
manuellement diff
. Lorsqu'il s'agira de modification des
fichiers d'un projet téléchargé avec git
par exemple, on
préférera faire appel au système de gestion de version pour produire le
patch. Nous en verrons un exemple dans
la
séquence IV.3.
Pour commencer je crée une copie temporaire du répertoire
base-files/
qui contient tous les fichiers. Je l’effacerai
quand j’aurai fini de préparer le patch, je reste donc dans mon
répertoire de travail initial.
[build-qemu]$ cp -R ../poky/meta/recipes-core/base-files/base-files base-files-origin
J’en crée une deuxième copie où je ferai la modification.
[build-qemu]$ cp -R ../poky/meta/recipes-core/base-files/base-files base-files-modified
Puis j’édite le fichier profile
du répertoire
base-files-modified/
pour remplacer la ligne :
EDITOR="vi" # needed for packages like cron, git-commit
par :
EDITOR="nano" # needed for packages like cron, git-commit
Je vérifie que le patch puisse être créé correctement :
[build-qemu]$ diff -ruN base-files-origin/ base-files-modified/ diff -ruN base-files-origin/profile base-files-modified/profile --- base-files-origin/profile 2021-07-22 06:13:49.521228698 +0200 +++ base-files-modified/profile 2021-07-22 06:14:49.020848341 +0200 @@ -60,7 +60,7 @@ fi fi -EDITOR="vi" # needed for packages like cron, git-commit +EDITOR="nano" # needed for packages like cron, git-commit export PATH PS1 OPIEDIR QPEDIR QTDIR EDITOR TERM umask 022
Le patch est correct, je l’enregistre en créant un répertoire
base-files/
dans notre layer personnalisé, avant
de supprimer les deux répertoires temporaires.
[build-qemu]$ mkdir -p ../meta-my-layer/recipes-core/base-files/files/ [build-qemu]$ diff -ruN base-files-origin/ base-files-modified/ > ../meta-my-layer/recipes-core/base-files/files/001-prefer-nano-to-vi-in-profile.patch [build-qemu]$ rm -rf base-files-origin/ base-files-modified/
Nous avons obtenu ainsi
notre
fichier de patch. Il doit nécessairement avoir une
extension «.patch
». Il est recommandé de lui
donner un nom significatif (par exemple
prefer-nano-to-vi-in-profile
) et l'usage veut que le nom
du fichier commence par un nombre qui permettra d'ordonner les
patches dans le cas où plusieurs sont fournis (les
patches pouvant modifier successivement les mêmes fichiers,
l'ordre d'application est important).
Je crée ensuite une extension de recette dans le répertoire
base-files/
de notre layer, en vérifiant au
préalable le nom de la recette à surcharger :
[build-qemu]$ ls ../poky/meta/recipes-core/base-files/ base-files base-files_3.0.14.bb
Lorsque le fichier d’extension s’applique à une version spécifique
d’une recette (par exemple 3.0.14
uniquement), on le nomme
base-files_3.0.14.bbappend
.
Attention, le caractère souligné (underscore)
«_
» dans le nom de recette a une
véritable signification : il permet de distinguer le nom du
package (qui ne peut donc pas contenir de souligné, uniquement
des tirets «-
» pour séparer les mots) de son
numéro de version.
Si l'extension s'applique à plusieurs versions de la recette, on
utilise le caractère générique pourcent
«%
» dont le rôle rapelle celui de
l’astérique «*
» dans les motifs du
shell :
base-files_3.%.bbappend
»
s’appliquerait à toutes les recettes de base-files
dont le numéro majeur de version est 3.
base-files_%.bbappend
» s’applique à
toutes les versions.
En toute rigueur un patch s'applique à une version spécifique
d'un fichier, même s'il est peu probable que le fichier
profile
change beaucoup d'une version à l'autre de la
recette base-files
. Je crée donc
la
recette suivante :
[build-qemu]$ nano ../meta-my-layer/recipes-core/base-files/base-files_3.0.14.bbappend FILESEXTRAPATHS_prepend := "${THISDIR}/files:" SRC_URI += "file://001-prefer-nano-to-vi-in-profile.patch"
Comme auparavant nous ajoutons le chemin du sous-répertoire contenant
notre patch dans FILESEXTRAPATHS
. Notre répertoire étant
inséré en tête (_prepend
) notre fichier sera donc choisi
plus prioritairement que celui de la recette initiale.
Notons que l’utilisation de «+=
» ne
fonctionnerait pas, car il utilise toujours une espace comme caractère
de séparation et non un deux-points.
On ajoute notre patch à la liste des fichiers appartenant à la
recette (variable SRC_URI
). Son extension
«.patch
» suffit à ce qu’il soit pris en compte
correctement par bitbake
. On peut relancer la génération
de l’image.
[build-qemu]$ bitbake my-image
Et nous testons notre résultat :
mybox login: root Password: (linux) root@mybox:~# echo $EDITOR nano root@mybox:~#
La variable d’environnement est bien initialisée avec la modification apportée par notre patch.
Le patch que nous avons développé dans l’exemple précédent était produit et appliqué sur un fichier présent dans une recette. Nous allons à présent voir comment produire un patch s’appliquant sur un fichier source d’un package téléchargé et compilé par une recette.
Pour continuer avec les packages que nous avons déjà installés,
je vous propose de travailler sur nano
. La première chose
à faire est de regarder le fichier de recette pour voir comment
bitbake
obtient les sources du package.
Nous savons déjà que la recette pour nano
est fournie par
le layer meta-oe
de meta-openembedded
. Il
nous suffit de jeter un coup d'oeil rapide dans les sous-répertoires
de meta-oe
pour trouver cette recette. Nous verrons dans
la
séquence IV-1 comment rechercher une recette dont nous connaissons
seulement le nom en parcourant tous les layers mentionnés dans
notre fichier conf/bblayers.conf
.
[build-qemu]$ cat ../meta-openembedded/meta-oe/recipes-support/nano/nano_5.6.bb DESCRIPTION = "GNU nano (Nano's ANOther editor, or \ Not ANOther editor) is an enhanced clone of the \ Pico text editor." HOMEPAGE = "http://www.nano-editor.org/" SECTION = "console/utils" LICENSE = "GPLv3" LIC_FILES_CHKSUM = "file://COPYING;md5=f27defe1e96c2e1ecd4e0c9be8967949" DEPENDS = "ncurses file" RDEPENDS_${PN} = "ncurses-terminfo-base" PV_MAJOR = "${@d.getVar('PV').split('.')[0]}" SRC_URI = "https://nano-editor.org/dist/v${PV_MAJOR}/nano-${PV}.tar.xz" SRC_URI[sha256sum] = "fce183e4a7034d07d219c79aa2f579005d1fd49f156db6e50f53543a87637a32" UPSTREAM_CHECK_URI = "https://ftp.gnu.org/gnu/nano" inherit autotools gettext pkgconfig PACKAGECONFIG[tiny] = "--enable-tiny,"
Nous pouvons remarquer que la variable SRC_URI
qui décrit
la provenance des fichiers source indique l’URL suivante pour le
téléchargement.
SRC_URI = "https://nano-editor.org/dist/v${PV_MAJOR}/nano-${PV}.tar.xz"
Deux variables sont mises à contribution dans ce chemin :
PV
et PV_MAJOR
.
La variable PV
(Package Version)
est omniprésente dans les recettes de Yocto, elle est automatiquement
remplie par bitbake
avec le numéro de version du package
concerné. Comment ce numéro est-il obtenu ? Simplement grâce au
nom du fichier de recette.
Il s’agit ici de «nano_5.6.bb
», donc
PV
est remplie avec la chaîne trouvée à droite du symbole
underscore, soit «5.6
».
La variable PV_MAJOR
est définie juste au-dessus de
SRC_URI
grâce à un petit morceau de script Python en-ligne
qui extrait la première portion de PV
en se basant le
caractère point «.
» comme séparateur.
Ici, il s’agit donc de «5
».
Autrement dit, l’URL devient :
https://nano-editor.org/dist/v5/nano-5.6.tar.xz
.
Téléchargeons ce package dans notre répertoire de travail :
[build-qemu]$ wget https://nano-editor.org/dist/v5/nano-5.6.tar.xz [...] 2021-07-22 09:22:05 (10,4 MB/s) - «nano-5.6.tar.xz» enregistré [1431976/1431976]
Comme nous l'avions fait précédemment avec le package
base-files
, nous extrayons l'archive en deux versions pour
utiliser diff
après modification.
[build-qemu]$ tar xf nano-5.6.tar.xz [build-qemu]$ mv nano-5.6 nano-5.6-origin [build-qemu]$ tar xf nano-5.6.tar.xz [build-qemu]$ mv nano-5.6 nano-5.6-modified [build-qemu]$
Pour cet exemple, je cherche à faire une modification simple mais assez
facilement visible. Par exemple, je propose de modifier le
texte de bienvenue qui apparaît lorsqu'on lance
«nano
» sans argument juste au-dessus des deux
lignes rappelant les raccourcis clavier. On voit ce texte sur
la figure II.3-2.
Nous pouvons trouver le message de bienvenue dans le fichier
nano-5.6-modified/src/nano.c
à la ligne 2510 :
statusbar(_("Welcome to nano. For basic help, type Ctrl+G."));
Je le modifie pour le remplacer par :
statusbar(_("Welcome to my patched version of nano."));
Puis je produis un patch entre les deux versions :
[build-qemu]$ diff -ruN nano-4.4-origin/ nano-4.4-modified/ > 001-custom-welcome-message.patch
recipetool
Maintenant que
notre
fichier de patch est prêt, il nous reste à le stocker dans
un répertoire de notre layer, puis écrire une extension de
recette au format «.bbappend
» pour
nano
comme nous l'avons déjà fait pour
base-files
.
Un outil fourni par Yocto peut nous aider à réaliser cette tâche un peu
répétitive : recipetool
.
Cet utilitaire est conçu pour créer ou modifier des recettes,
permettant ainsi d'ajouter assez facilement des patches, des
fragments de configuration, etc. Personnellement, je l'utilise surtout
lors de la customisation du contenu du kernel Linux ou de
busybox
.
Nous allons demander à recipetool
de créer une extension
pour la recette de nano
dans le layer
meta-my-layer
et d'ajouter le patch que nous venons
de produire.
[build-qemu]$ recipetool appendsrcfile ../meta-my-layer/ nano 001-custom-welcome-message.patch NOTE: Starting bitbake server... [...] NOTE: Writing append file /home/cpb/Yocto-lab/meta-my-layer/recipes-support/nano/nano_5.6.bbappend NOTE: Copying 001-custom-welcome-message.patch to /home/cpb/Yocto-lab/meta-my-layer/recipes-support/nano/nano/001-custom-welcome-message.patch
Nous voyons que recipetool
a créé un sous-répertoire de
notre layer pour nano
. Il l'a placé dans
l'arborescence recipes-support/
, respectant ce qui
existait dans le layer meta-openembedded
original.
[build-qemu]$ ls ../meta-my-layer/ conf README recipes-custom recipes-support COPYING.MIT recipes-core recipes-example [build-qemu]$ ls ../meta-my-layer/recipes-support/ nano [build-qemu]$ ls ../meta-my-layer/recipes-support/nano/ nano nano_5.6.bbappend
Je n'ai pas utilisé l'option «-w
» (comme
wildcard, caractère générique) de recipetool
, aussi
celui-ci crée une extension de recette pour la version exacte de
nano
présente dans le layer original (5.6). Si
j'avais employé cette option, il aurait créé une extension du nom de
nano_%.bbappend
qui aurait concerné n'importe quelle
version de nano
. Ceci est déconseillé lorsqu'on fait un
patch sur du code source amené à bouger entre les versions
successives d'un projet.
Le sous-répertoire nano/
du répertoire d'extension joue le
même rôle que le sous-répertoire files/
que nous avions
créé avec base-files
: il abrite le patch à
appliquer.
[build-qemu]$ ls ../meta-my-layer/recipes-support/nano/nano 001-custom-welcome-message.patch
L'extension de recette est simple et rappelle celle que nous avions écrite précédemment :
[build-qemu]$ cat ../meta-my-layer/recipes-support/nano/nano_5.6.bbappend SRC_URI += "file://001-custom-welcome-message.patch;subdir=nano-5.6" FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
Nous pouvons relancer le build de notre système :
[build-qemu]$ bitbake my-image
Puis sur notre cible virtuelle, nous pouvons admirer le résultat de
notre patch en lançant la commande nano
, comme on
le voit sur la figure II.3-3.
Nous avons vu dans cette séquence comment modifier le comportement de recettes existantes sans toucher aux fichiers originaux, avec différentes approches selon le type de modification à apporter.
Le principe des extensions grâce aux fichiers .bbappend
est très puissant et permet de garantir la pérennité des modifications
que l’on apporte même si les versions des packages d’origine
évoluent.
Les deux premières parties de ce cours en ligne nous ont permis de voir comment créer une image de Linux embarqué pour une architecture cible de notre choix, et d’ajuster son contenu. Il est temps à présent de s’intéresser à l’ajout de notre propre code, ce qui sera l’objet de la troisième partie.
Ce document est placé sous licence Creative Common CC-by-nc. Vous pouvez copier son contenu et le réemployer à votre gré pour une utilisation non-commerciale. Vous devez en outre mentionner sa provenance.