Lorsqu’on réalise un build pour un système Linux embarqué avec Yocto Project, de nombreux fichiers sont parcourus par l’outil Bitbake. Il n’est pas toujours simple de savoir dans quel ordre ces fichiers de configuration sont examinés, mais c’est pourtant une clé pour la bonne compréhension des opérations réalisées.
Nous allons nous intéresser ici aux fichiers de configuration et de données (distro, machine, image, recettes, etc.) pas aux fichiers python internes de Bitbake, ni aux fichiers sources, objets ou exécutables obtenus lors de la compilation des packages.
Bien sûr nous regarderons certains fichiers directement livrés avec Yocto, mais ce qui m’intéresse avant tout, c’est la connaissance des fichiers sur lesquels nous avons la possibilité d’agir, ceux que nous pouvons personnaliser avant de lancer le build.
Environnement de travail
Nous allons nous placer dans une situation assez simple mais réaliste pour un petit projet :
- préparation d’une image pour Raspberry Pi 4 réalisée en utilisant la branche Scarthgap de Poky,
- utilisation du layer
meta-raspberrypi
, - utilisation du layer
meta-oe
se trouvant dans le repositorymeta-openembedded
.
Pour être certain de voir tous les accès aux fichiers demandés par bitbake
, je l’ai lancé en le préfixant par « strace -f -o bitbake-trace.txt
» afin d’obtenir dans le fichier bitbake-trace.txt
la liste de tous les appels-système invoqués lors du build.
Je me suis retrouvé avec un fichier de traces de 270Go ! J’en ai extrait les lignes qui contiennent un appel open()
– ou plus précisément openat()
– qui n’échoue pas. Puis j’ai filtré le résultat pour ne conserver que les extensions de fichiers qui m’intéressent (.bb
, .conf
, .bbappend
, .bbclass
, etc.).
J’ai obtenu au final un fichier de 8.3 Mo, comprenant 95110 lignes. Comme il s’agit des opérations séquentielles lors du build, de nombreux accès aux fichiers sont redondants (par exemple les classes sont héritées par de nombreuses recettes différentes). Si on élimine les redondances, il n’y a « que » 8158 fichiers parcourus.
Partant de cette base de travail et en consultant le véritable contenu des fichiers, on peut comprendre assez bien l’enchaînement des opérations pour Bitbake.
Fichiers de configuration
Le comportement général de Bitbake est le suivant :
- lire les informations de configuration globale,
- parcourir les layers par ordre de priorité croissante et mémoriser le contenu des recettes (une recette
.bbappend
dans un layer de priorité élevée peut amender le contenu de la recette.bb
de même nom située dans un layer de priorité plus faible), - réaliser les actions nécessaires pour obtenir la cible réclamée au lancement de la commande (image, recette, etc.).
Ici nous ne nous intéressons qu’aux deux premiers points. Pour être exact c’est même le premier point qui nous concerne le plus.
Liste des layers concernés
Le premier fichier lu par bitbake
est conf/bblayers.conf
. Il renseigne principalement la variable BBLAYERS
qui contient la liste des layers à prendre en considération. Voici un extrait de ce fichier :
BBLAYERS ?= " \
/home/Builds/Lab/layers/poky/meta \
/home/Builds/Lab/layers/poky/meta-poky \
/home/Builds/Lab/layers/poky/meta-yocto-bsp \
/home/Builds/Lab/layers/meta-raspberrypi \
/home/Builds/Lab/layers/meta-openembedded/meta-oe \
"
Le fichier conf/bblayers.conf
peut être manipulé manuellement ou par l’intermédiaire de la commande bitbake-layers
.
Configurations des layers
Bitbake va ensuite lire les cinq fichiers conf/layer.conf
se trouvant à l’intérieur des cinq répertoires mentionnés ci-dessus. Cela va lui permettre d’obtenir des informations sur les layers, entre autres :
- Vérifier la compatibilité des layers avec la branche de Poky (par exemple :
LAYERSERIES_COMPAT_openembedded-layer = "scarthgap"
). - Obtenir les priorités respectives des layers (
BBFILE_PRIORITY_openembedded-layer = "5"
), c’est-à-dire l’ordre de parcours à venir. - Mémoriser les emplacements des recettes contenues dans les layers (
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend
)
Lorsque nous créons notre propre layer (par exemple avec la commande « bitbake-layers create-layer <layer-path>
» ) un fichier layer.conf
est créé avec des valeurs par défaut. Il est conseillé de le consulter et d’ajuster éventuellement la priorité et la compatibilité avec Poky.
Configuration de bitbake
Le fichier lu ensuite par Bitbake se trouve dans le layer poky/meta
, juste à côté de layer.conf
. Il s’agit de conf/bitbake.conf
et il a une très grande importance pour le reste du build.
Dans ce layer se trouvent tout d’abord l’initialisation d’un grand nombre de variables :
- les noms symboliques des chemins qu’on utilise dans les recettes (comme
${bindir}
ou${sysconfdir}
), - les informations sur la machine hôte, sur la cible, sur la machine prévue pour héberger le SDK, etc.
- les valeurs par défaut pour les variables renseignées ou utilisées dans les recettes (
SUMMARY
,LICENSE
,SRC_URI
,PN
,PV
,DEPENDS
,FILES
,WORKDIR
,D
,S
,B
…), - les valeurs par défaut pour les paramètres de build (
TCLIBC
,MAKE
,CC
,DISTRO
,OVERRIDES
,DL_DIR
,SSTATE_DIR
, …) - l’initialisation de variables utilisées dans de nombreuses recettes (
IMAGE_FEATURES
,DISTRO_FEATURES
,MACHINE_FEATURES
, etc.) - etc.
Le second aspect extrêmement important de ce fichier, c’est qu’il va organiser l’inclusion de tous les autres fichiers de configuration. Voici la portion concernée :
require conf/abi_version.conf
include conf/site.conf
include conf/auto.conf
include conf/local.conf
require conf/multiconfig/${BB_CURRENT_MC}.conf
include conf/machine/${MACHINE}.conf
include conf/machine-sdk/${SDKMACHINE}.conf
include conf/distro/${DISTRO}.conf
include conf/distro/defaultsetup.conf
include conf/documentation.conf
include conf/licenses.conf
require conf/sanity.conf
require conf/cve-check-map.conf
include conf/bblock.conf
Rappelons que la directive require
concerne une inclusion obligatoire (sous peine d’échec du build) alors que include
concerne un fichier facultatif.
Les fichiers de cette liste ne sont pas tous aussi intéressants. Nous allons nous concentrer sur les cinq que j’ai surligné, car ce sont les principaux points de configuration où nous pouvons être amenés à intervenir.
Configuration spécifique pour le développeur
Le fichier conf/site.conf
est facultatif. Aucune version n’en est fournie par Yocto. Il est toutefois tout à fait possible d’en ajouter un dans le sous répertoire conf/
de notre dossier de build (à côté de bblayers.conf
et de local.conf
que nous allons voir ensuite).
Le rôle de ce fichier est habituellement de configurer les paramètres liés à l’environnement de build local. Ceux qui ne doivent pas être partagés avec les autres développeurs, par exemple des répertoires downloads/
ou sstate-cache/
à des emplacements particuliers, ou une configuration d’utilisation du processeur BB_NUMBER_THREADS
ou PARALLEL_MAKE
.
Dans le projet Yocto Cooker, nous n’avons choisi de ne pas inclure (pour le moment du moins) site.conf
dans le format des menus de configuration. Ceux-ci doivent en effet pouvoir être partagés entre les développeurs utilisant Yocto.
Configuration du build
Avant de parcourir les layers ainsi mémorisés, bitbake
va lire le fichier conf/local.conf
qui va lui donner plusieurs renseignements importants :
- La variable
MACHINE
qui indique le nom du fichier de configuration de la cible. C’est généralement la première variable que l’on configure pour notre build. - La variable
SDK_MACHINE
sert lors de l’extraction de l’environnement de développement (pour produire du code en dehors de Yocto et l’intégrer directement sur la cible). - les variables
DL_DIR
,SSTATE_DIR
ouTMP_DIR
qui indiquent les chemins utilisés durant le build. - La variable
PACKAGE_CLASSES
qui définit le format des packages binaires utilisés en interne parbitbake
durant le build. - La variable
DISTRO
contient le nom de la distribution. - etc.
Le fichier conf/local.conf
est généralement le premier point de personnalisation pour Yocto Project. Il est important de sauvegarder son contenu et celui de conf/bblayers.conf
pour assurer la reproductibilité du build. C’est pourquoi le menu de configuration de Yocto Cooker contient une section spécifique pour mémoriser et re-générer ce fichier.
Configuration de la machine cible
La variable MACHINE
indique le nom de la machine cible. Bitbake va alors rechercher dans les différents layers un fichier conf/machine/${MACHINE}.conf
. Dans le cas du build ci-dessus, il s’agissait de conf/machine/raspberrypi4.conf
se trouvant dans le layer meta-raspberrypi
.
Ce fichier va lui-même inclure d’autre fichiers du même layer (rpi-base.inc
, rpi-default-settings.inc
, rpi-default-providers.inc
, etc.)
Ce fichier configure tout ce qui concerne l’aspect hardware du build. On y trouve par exemple :
TUNE_FEATURES
qui décrit les spécificités du processeur à destination de la toolchain de compilation (armv7a
,neon
,thumb
, etc.)MACHINE_FEATURES
qui contient une liste de fonctionnalités offertes par le matériel (wifi
,bluetooth
,keyboard
, etc.) qui pourront être testées dans les recettes pour inclure ou paramétrer certains packages.WKS_FILE
etIMAGE_FSTYPES
indiquant le partitionnement et les formats de sortie pour le root filesystem.PREFERRED_PROVIDER_virtual/kernel
etPREFERRED_VERSION_<kernel>
: la recette à utiliser pour compiler le kernel et sa version.KERNEL_DEVICETREE
le nom du device tree à utiliser pour ce hardware.PREFERRED_PROVIDER_virtual/bootloader
etPREFERRED_VERSION_<bootloader>
indiquant la recette pour le bootloader et sa version.- etc.
Configuration de la machine de développement
Un autre fichier, moins connu que le précédent est parcouru par Bitbake, afin de connaître les options de compilation à appliquer lorsque l’on veut préparer la toolchain de cross-compilation en dehors de Yocto. Il s’agit de l’environnement obtenu quand on exécute bitbake -c populate_sdk <image-name>
.
Je n’ai jamais rencontré de situation où l’on utilise un autre fichier de configuration que poky/meta/conf/machine-sdk/x86_64.conf
même si d’autres architectures sont proposées :
$ ls ../../layers/poky/meta/conf/machine-sdk/
aarch64.conf i586.conf i686.conf loongarch64.conf ppc64.conf ppc64le.conf riscv64.conf x86_64.conf
Configuration de la distribution
Enfin le dernier fichier de configuration qui nous intéresse est conf/distro/${DISTRO}.conf
.
Rappelons que la variable DISTRO
est renseignée dans conf/local.conf
avec la valeur par défaut poky
. Le fichier concerné est donc poky/meta-poky/conf/distro/poky.conf
. Outre des paramètres comme le nom de la distribution et son numéro de version, le rôle principal de ce fichier sera l’initialisation de variables comme DISTRO_FEATURES
, DISTRO_EXTRA_RDEPENDS
ou DISTRO_OVERRIDES
. Ces variables permettront de sélectionner certains packages dans les recettes ou d’activer des options spécifiques lors de la compilation des packages.
Depuis la version Scarthgap de Poky, nous sommes fortement incités à surcharger ce fichier, en créant notre propre distro et en plaçant le fichier de configuration dans le sous-répertoire conf/distro/
d’un layer personnel. Tant que nous utilisons la distribution poky
par défaut, un message d’avertissement s’affiche lors de la connexion de l’utilisateur. Ce message se trouve dans le fichier suivant :
$ cat ../../layers/poky/meta-poky/recipes-core/base-files/files/poky/motd
WARNING: Poky is a reference Yocto Project distribution that should be used for
testing and development purposes only. It is recommended that you create your
own distribution for production use.
Configuration personnalisée
Nous voici donc avec cinq emplacements que nous pouvons personnaliser pour la configuration du build. Bien sûr viendront ensuite les ajustements de la recette d’image et des recettes de packages.
J’ai tendance à préférer intégrer le maximum de configuration au niveau de la distribution (dans conf/distro/<distro-name>.conf
) et de la description de la cible (dans conf/machine/<machine-name>.conf
), car ces fichiers se trouvent dans un layer personnalisé ce qui simplifie leur versioning et leur partage avec d’autres développeurs. Les fichiers site.conf
et local.conf
se trouvant dans le répertoire de build, leur sauvegarde réclame un mécanisme spécifique – par exemple Yocto Cooker 🙂 .
Pour définir sa propre distribution, il suffit d’ajouter un fichier conf/distro/<distro-name>.conf
dans un layer nous appartenant, et de rajouter la ligne DISTRO = "<distro-name>"
dans local.conf
. Pour créer le fichier, on peut s’inspirer de celui de Poky, en élaguant les fonctionnalités inutiles pour notre projet (dans un premier temps, il suffit même d’inclure conf/distro/poky.conf
avant de surcharger les variables de description).
De même, la définition d’une machine revient à ajouter un fichier conf/machine/<machine-name>.conf
dans notre layer et une ligne MACHINE="<machine-name>"
dans local.conf
. Souvent, on inclut directement la configuration de la machine d’origine en intervenant simplement sur le device tree.
Dans le cas de la machine, il faudra également intervenir sur la recette du kernel pour surcharger la variable COMPATIBLE_MACHINE
.
Recettes et image
Les variables lues dans toute cette série de fichiers « .conf
» sont globales, et peuvent être surchargées au fur et à mesure de leurs initialisations. L’initialisation la plus faible est celle de l’opérateur « ??=
» puis vient l’initialisation « ?=
» suivie de « =
» et finalement l’override « :forcevariable
» .
Une fois cette phase d’initialisation terminée, Bitbake va lire le contenu des différents fichiers de recette, en chargeant les variables dans des contextes indépendants pour chaque recette. Par exemple la variable SRC_URI
est spécifique pour chaque package. Chaque fichier .bb
renseigne sa propre instance de cette variable qui peut éventuellement être complétée dans les .bbappend
du même package.
Toutes les recettes sont ainsi lues, incluant éventuellement des fichiers .inc
. Dans le contexte de chaque recette sont également ajoutées les méthodes et les variables définies dans les fichiers .bbclass
dont elle hérite.
Une fois les recettes chargées en mémoire, bitbake
est prêt à commencer le build proprement dit. En cherchant la plupart du temps à produire une recette d’image, en suivant récursivement ses dépendances. Parfois on ne veut produire qu’un seul package (et ses dépendances bien sûr).
Conclusion
Nous avons examinés la liste des fichiers de configuration de bitbake
et surtout l’ordre dans lequel ils sont lus. Ceci nous permet d’ajuster notre production de plusieurs manières :
- en fournissant un fichier initialement absent, plus précisément
site.conf
; - en complétant et modifiant un fichier fourni par Yocto comme
local.conf
; - en fournissant une alternative aux fichier
<distro>.conf
et<machine>.conf
se trouvant dans les layers intégrés dans notre image ; - et bien sûr en créant une recette
<image>.bb
personnalisée et en ajustant les recettes fournies grâce à des.bbappend
.
–
Dans le prochain article de cette petite série, nous étudierons les variables principales contenues dans ces fichiers de configuration.
–
Pour en savoir plus et mettre en pratique, n’hésitez à participer à une session de formation « Linux embarqué avec Yocto Project » ou « Yocto Project avancé » que j’anime chez Logilin.