«« sommaire »»

II.2 – Ajout d'applications dans l'image

Christophe BLAESS - juillet 2021

Dans la séquence précédente, nous avons modifié le fichier «local.conf» que nous trouvons dans le sous-répertoire «conf/» de notre répertoire de build. Les modifications étaient assez simples : ajustement de l’emplacement des dossiers de stockage temporaire, ajout d’utilisateur et configuration de mots de passe, puis modification du nom de machine.

Dans cette séquence nous allons ajouter quelques applications sur notre système. Pour cela, nous allons commencer en modifiant à nouveau ce fichier, puis nous créerons notre propre recette d'image personnalisée.

Utilitaires présents dans Poky

Notre première possibilité est d’ajouter des packages dont les recettes sont livrées avec Poky. Regardons, par exemple les fichiers se trouvant dans les sous-répertoires «recipes*/» de l'arborescence de Poky :

[build-qemu]$ ls  ../poky/meta/recipes*
../poky/meta/recipes.txt

../poky/meta/recipes-bsp:
acpid       efibootmgr  gnu-efi  libacpi  pciutils   u-boot    v86d
alsa-state  efivar      grub     lrzsz    pm-utils   usbinit
apmd        formfactor  keymaps  opensbi  setserial  usbutils

../poky/meta/recipes-connectivity:
avahi      iproute2     mobile-broadband-provider-info  ppp
bind       iw           neard                           ppp-dialin
bluez5     kea          nfs-utils                       resolvconf
connman    libnss-mdns  ofono                           socat
dhcpcd     libpcap      openssh                         ssh-pregen-hostkeys
inetutils  libuv        openssl                         wpa-supplicant

../poky/meta/recipes-core:
base-files   fts              initscripts  newlib         udev
base-passwd  gettext          kbd          os-release     update-rc.d
busybox      glib-2.0         libcgroup    ovmf           util-linux
coreutils    glibc            libxcrypt    packagegroups  volatile-binds
dbus         glib-networking  libxml       psplash        zlib
dbus-wait    ifupdown         meta         readline
dropbear     images           musl         sysfsutils
ell          init-ifupdown    ncurses      systemd
expat        initrdscripts    netbase      sysvinit

../poky/meta/recipes-devtools:
apt                 dwarfsrcfiles     libmodulemd  prelink
autoconf            e2fsprogs         librepo      pseudo
autoconf-archive    elfutils          libtool      python
automake            expect            llvm         python-numpy
binutils            fdisk             log4cplus    qemu
bison               file              m4           quilt
bootchart2          flex              make         rpm
btrfs-tools         gcc               makedevs     rsync
ccache              gdb               meson        ruby
cdrtools            git               mklibs       run-postinsts
chrpath             glide             mmc          squashfs-tools
cmake               gnu-config        mtd          strace
createrepo-c        go                mtools       subversion
dejagnu             help2man          nasm         swig
desktop-file-utils  i2c-tools         ninja        syslinux
devel-config        icecc-create-env  opkg         systemd-bootchart
diffstat            icecc-toolchain   opkg-utils   tcf-agent
distcc              intltool          orc          tcltk
dmidecode           jquery            patch        unfs3
dnf                 json-c            patchelf     unifdef
docbook-xml         libcomps          perl         vala
dosfstools          libdnf            pkgconf      valgrind
dpkg                libedit           pkgconfig    xmlto

../poky/meta/recipes-extended:
acpica        go-examples  libtirpc       parted        tcp-wrappers
asciidoc      gperf        lighttpd       pbzip2        texinfo
at            grep         logrotate      perl          texinfo-dummy-native
bash          groff        lsb            pigz          time
bc            gzip         lsof           procps        timezone
blktool       hdparm       ltp            psmisc        unzip
bzip2         images       lzip           quota         watchdog
cpio          iptables     man-db         rpcbind       wget
cracklib      iputils      man-pages      rpcsvc-proto  which
cronie        less         mc             screen        xdg-utils
cups          libaio       mdadm          sed           xinetd
cwautomacros  libarchive   mingetty       shadow        xz
diffutils     libidn       minicom        slang         zip
ed            libmnl       msmtp          stress-ng     zstd
ethtool       libnsl       net-tools      sudo
findutils     libnss-nis   newt           sysklogd
gawk          libpipeline  packagegroups  sysstat
ghostscript   libsolv      pam            tar

../poky/meta/recipes-gnome:
epiphany               gsettings-desktop-schemas  libdazzle  librsvg
gcr                    gtk+                       libgudev   libsecret
gdk-pixbuf             gtk-doc                    libhandy
gnome                  hicolor-icon-theme         libnotify
gobject-introspection  json-glib                  libportal

../poky/meta/recipes-graphics:
builder          libmatchbox           virglrenderer
cairo            libsdl2               vulkan
cantarell-fonts  libva                 waffle
clutter          matchbox-session      wayland
cogl             matchbox-wm           x11-common
drm              menu-cache            xcursor-transparent-theme
fontconfig       mesa                  xinput-calibrator
freetype         mini-x-session        xorg-app
glew             mx                    xorg-driver
glslang          packagegroups         xorg-font
harfbuzz         pango                 xorg-lib
igt-gpu-tools    piglit                xorg-proto
images           pong-clock            xorg-util
jpeg             shaderc               xorg-xserver
kmscube          spir                  xrestop
libepoxy         startup-notification
libfakekey       ttf-fonts

../poky/meta/recipes-kernel:
blktrace    kexec           linux-libc-headers    perf
cryptodev   kmod            lttng                 powertop
dtc         linux           make-mod-scripts      systemtap
kern-tools  linux-firmware  modutils-initscripts  wireless-regdb

../poky/meta/recipes-multimedia:
alsa       lame       libomxil       libtheora  mpg123      webp
ffmpeg     liba52     libpng         libtiff    pulseaudio  x264
flac       libid3tag  libsamplerate  libvorbis  sbc
gstreamer  libogg     libsndfile     mpeg2dec   speex

../poky/meta/recipes-rt:
images  README  rt-tests

../poky/meta/recipes-sato:
images               matchbox-panel-2     pcmanfm          settings-daemon
l3afpad              matchbox-sato        pulseaudio-sato  shutdown-desktop
matchbox-config-gtk  matchbox-terminal    puzzles          webkit
matchbox-desktop     matchbox-theme-sato  rxvt-unicode
matchbox-keyboard    packagegroups        sato-screenshot

../poky/meta/recipes-support:
apr                    gnutls         libical           nettle
argp-standalone        gpgme          libjitterentropy  npth
aspell                 icu            libksba           nss-myhostname
atk                    iso-codes      libmd             numactl
attr                   itstool        libmpc            p11-kit
bash-completion        libassuan      libnl             pinentry
bmap-tools             libatomic-ops  libpcre           popt
boost                  libbsd         libproxy          ptest-runner
ca-certificates        libcap         libpsl            re2c
consolekit             libcap-ng      libsoup           rng-tools
curl                   libcheck       libssh2           serf
db                     libcroco       libunistring      shared-mime-info
debianutils            libdaemon      libunwind         sqlite
diffoscope             libevdev       liburcu           taglib
dos2unix               libevent       libusb            user-creation
enchant                libexif        libxslt           vim
fribidi                libffi         libyaml           vte
gdbm                   libfm          lz4               xxhash
gmp                    libgcrypt      lzo
gnome-desktop-testing  libgit2        lzop
gnupg                  libgpg-error   mpfr

La liste n’est pas évidente à lire, mais un comptage rapide avec «find ../poky/meta/recipes* -name '*bb'| wc -l» nous indique la présence de plus de 800 recettes livrées dans Poky. La plupart d’entre-elles sont des services ou des utilitaires bas-niveaux assez peu visibles au premier abord. Nous pouvons toutefois ajouter sur notre image un petit outil que j’aime bien : mc le Midnight Commander. Pour cela nous ajoutons simplement la ligne suivante dans local.conf :

IMAGE_INSTALL_append = " mc"

La variable «IMAGE_INSTALL» contient la liste des noms de packages à installer sur le système cible.

Le suffixe «_append» indique que la chaîne fournie en partie droite de l'affectation doit être ajoutée à la fin de la variable concernée. Nous ajoutons donc le nom du package mc précédé d'une espace qui sert de séparateur entre les éléments de la liste de packages.

Nous pourrions préférer utiliser :

IMAGE_INSTALL_prepend = "mc "

pour ajouter la chaîne en début de variable (et en la faisant suivre de l'espace de séparation).

Plusieurs lignes de ce type peuvent se suivre pour allonger d’autant le contenu de l’image.

Après avoir regénéré «core-image-base» et nous être connectés, nous pouvons lancer la commande «mc» et retrouver l'atmosphère un peu surannée des années 1990 sur Ms-Dos (figure II.2-1). Malgré son aspect désuet aujourd'hui, cet outil peut encore s'avérer très pratique lors de la mise au point d’un système embarqué pour le confort du développement en ligne de commande.

Il existe d’autre outils dont les recettes se trouvent dans Poky que j’aime bien intégrer dans mes images embarquées afin de permettre la mise au point du code métier : strace, valgrind, powertop, gdbserver… Nous en reparlerons dans la troisième partie, pour l’instant contentons-nous d’ajouter les lignes suivantes :

IMAGE_INSTALL_append = " mc"
IMAGE_INSTALL_append = " strace valgrind"
IMAGE_INSTALL_append = " gdbserver powertop"

Comme on le voit, on peut aussi bien enchaîner des lignes d’ajout que regrouper les différents packages désirés dans une seule ligne.

Après recompilation de l’image, je vérifie la présence des outils ajoutés. Je ne m’intéresse pas ici à leur fonctionnement ou leur résultat, juste à leur présence sur ma cible :

Poky (Yocto Project Reference Distro) 3.3.1 mybox /dev/ttyAMA0

mybox login: root
Password: (linux)

root@mybox:~# strace -V
strace -- version 5.11
Copyright (c) 1991-2021 The strace developers <https://strace.io>.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Optional features enabled: (none)
root@mybox:~# gdbserver --version
GNU gdbserver (GDB) 10.1
Copyright (C) 2021 Free Software Foundation, Inc.
gdbserver is free software, covered by the GNU General Public License.
This gdbserver was configured as "arm-poky-linux-gnueabi"
root@mybox:~# powertop --version
PowerTOP version v2.13
root@mybox:~# valgrind --version
valgrind-3.16.1
root@mybox:~# 

Ajout de packages hors Poky

Pendant la mise au point d’un système embarqué, on a souvent besoin d’éditer des fichiers directement sur la cible. Sur une installation «core-image-base», nous disposons bien de l’éditeur «vi», intégré dans le package busybox. Néanmoins tout le monde n’est pas familier des commandes de vi, et l’utilisation d’un outil un peu plus intuitif s’avère généralement plus reposante. Supposons que nous souhaitions installer l'éditeur «nano» par exemple, qui est plus simple d’emploi que vi.

Si nous essayons simplement d’ajouter la ligne :

IMAGE_INSTALL_append = " nano"

dans notre fichier «local.conf», bitbake va se plaindre, nous afficher une série de lignes en rouge et terminer en indiquant une erreur :

ERROR: Nothing RPROVIDES 'nano' (but /home/testing/Build/Lab/Yocto-lab/poky/meta/recipes-core/images/core-image-base.bb RDEPENDS on or otherwise requires it)
NOTE: Runtime target 'nano' is unbuildable, removing...
Missing or unbuildable dependency chain was: ['nano']
ERROR: Required build target 'core-image-base' has no buildable providers.
Missing or unbuildable dependency chain was: ['core-image-base', 'nano']

Clairement, bitbake n’a pas trouvé dans les recettes fournies par Poky celle permettant de construire nano.

Il va probablement nous falloir trouver un layer (un ensemble de recettes) à installer sur notre système. Pour cela, l'habitude est de se rendre sur le site https://layers.openembedded.org, de cliquer sur l’onglet «recipes» et de saisir le nom du package désiré dans le moteur de recherche. Nous trouvons alors une page de résultat comme celle de la figure II.2-2.

Clairement la recette qui m’intéresse est la première de la liste. Elle se trouve dans le layer indiqué dans la colonne de droite : «meta-oe». En cliquant sur ce lien je vois qu’il s’agit d’un layer appartenant à un ensemble plus grand : «meta-openembedded», et je trouve l’adresse du téléchargement à effectuer.

[build-qemu]$ cd ..
[Yocto-lab]$ git clone  git://git.openembedded.org/meta-openembedded -b hardknott 
Clonage dans 'meta-openembedded'…
remote: Counting objects: 157846, done.
remote: Compressing objects: 100% (52832/52832), done.
[...]
Résolution des deltas: 100% (98799/98799), fait.
[Yocto-lab]$ ls  meta-openembedded/
contrib           meta-gnome       meta-networking  meta-python     README
COPYING.MIT       meta-initramfs   meta-oe          meta-webserver
meta-filesystems  meta-multimedia  meta-perl        meta-xfce
[Yocto-lab]$ 

L’ensemble «meta-openembedded» est assez incontournable dès lors que l’on prépare un système avec Yocto. Il contient près de deux mille recettes pour de nombreux packages très utiles pour Linux embarqué.

Nous devons ajouter le layer «meta-oe» à la liste de ceux pris en compte dans notre build.

[Yocto-lab]$ cd build-qemu/
[build-qemu]$ bitbake-layers add-layer ../meta-openembedded/meta-oe/ 
NOTE: Starting bitbake server...
[build-qemu]$ 

Nous pouvons à présent relancer notre compilation, elle se déroule normalement et nano est intégré dans notre image.

[build-qemu]$ bitbake  core-image-base
  [...]

Par exemple, sur la figure II.2-3, nous l'employons pour consulter le fichier /etc/passwd (la capture d'écran date de la version précédente de cet article mais le résultat est identique aujourd'hui, hormis le numéro de version de nano).

Utilisation des fonctionnalités d’image

Nous avons utilisé la compilation avec «core-image-base» un peu aveuglément, sans comprendre véritablement ce que cela signifie. Nous allons examiner son contenu plus en détail. Pour cela nous recherchons le fichier qui décrit «core-image-base» dans les sous-répertoires de Poky. Il s’agit d’une recette avec l’extension «.bb».

[build-qemu]$ find  ../poky/  -name  core-image-base.bb 
../poky/meta/recipes-core/images/core-image-base.bb

Lorsque nous demandons à bitbake de produire une image, il recherche un fichier d'extension «.bb» avec le nom de l'image. Ce fichier doit se trouver dans un sous-répertoire «images/» se trouvant dans un répertoire dont le nom commence par «recipes-»

Le fichier trouvé correspond bien à ces critères. Voyons son contenu.

[build-qemu]$ cat  ../poky/meta/recipes-core/images/core-image-base.bb 
SUMMARY = "A console-only image that fully supports the target device \
 hardware."
IMAGE_FEATURES += "splash"
LICENSE = "MIT"
inherit core-image

Hormis la description et la licence, deux lignes nous intéressent :

Nous voyons que Yocto nous propose ainsi ce mécanisme de features, des fonctionnalités de haut-niveau que l’on peut sélectionner sans se soucier du détail du package correspondant.

Voici les fonctionnalités proposées par la classe «core-image», qui est implémentée dans le fichier poky/meta/classes/core-image.bbclass.

x11Serveur X-window
x11-baseServeur X-window et environnement minimal (Matchbox)
x11-satoServeur X-window et environnement Sato pour mobile.
tools-debugOutils de débogage (gdb, gdbserver, strace, gcore…)
eclipse-debugOutils de débogage distant avec Eclipse.
tools-profileOutils de profiling (perf, lttng, gprof...)
tools-testappsOutils de tests du matériel.
tools-sdkInstallation des outils de développement natifs (gcc, make...) sur la cible.
nsf-serverServeur NFS
nfs-clientClient NFS
ssh-server-dropbearServeur SSH basé sur Dropbear.
ssh-server-opensshServeur SSH basé sur Openssh
hwcodecsSupport des codecs pour l’accélération matérielle
package-managementSupport des packages sur la cible (installation des outils et base de données).
dev-pkgsInstallation des fichiers headers nécessaire pour le développement des packages présents.
dbg-pkgsTous les packages sont compilés avec les informations de débogage
doc-pkgsInstallation de la documentation associée à tous les packages
read-only-rootfsSystème de fichiers en lecture-seule.
splashÉcran d’accueil pendant le boot

Nous pouvons, par exemple, ajouter dans notre fichier local.conf la ligne :

IMAGE_FEATURES += "tools-sdk"

pour intégrer dans notre image les outils nécessaires à la compilation de code directement sur la cible. La compilation ci-dessous a bien lieu sur ma cible ARM émulée par Qemu :

mybox login: root
Password: (linux)
root@mybox:~# vi my-hello.c
   #include <stdio.h>
   #include <unistd.h>

   int main(void)
   {
     char host[512];  
     gethostname(host, 512);
     printf("Hello from %s\n", host);
     return 0;
   }
root@mybox:~# gcc my-hello.c -o my-hello -Wall 
root@mybox:~# ./my-hello 
Hello from mybox
root@mybox:~# g++ --version
g++ (GCC) 10.2.0
[...]
root@mybox:~# 

Il n’est pas fréquent d’installer une chaîne de compilation native directement sur la plateforme cible, mais cela peut s’avérer intéressant pendant la phase de prototypage et de mise au point. Il s’agit d’un point sur lequel Yocto est plus puissant que son confrère Buildroot qui a renoncé à cette possibilité il y a quelques années.

Création d’une image spécifique

Jusqu’à présent nous avons utilisé l’image core-image-base en ajoutant dans local.conf des lignes «IMAGES_INSTALL_append». Cette approche est parfaitement adaptée pour les premières phases de configuration du système, mais trouve rapidement ses limites. Pour les systèmes industriels en effet, il est souvent nécessaire de gérer toute une gamme de produits différents. On est donc amenés à réaliser régulièrement des séries de builds avec peu de différences entre-eux. On préfère généralement factoriser toute la configuration commune aux différents produits dans une image particulière et ne laisser dans les fichiers local.conf que les spécificités propres à chaque build.

Autrement dit nous allons créer notre propre fichier de description d’image, et nous n’invoquerons plus «bitbake core-image-base» mais «bitbake my-image» par exemple (en situation réelle, je nomme plutôt l’image en fonction du projet de mon client).

Pour cela, nous devons commencer par créer notre propre layer. Rien de compliqué, l’outil «bitbake-layers» est là pour nous aider.

[build-qemu]$ bitbake-layers  create-layer  ../meta-my-layer
NOTE: Starting bitbake server...
Add your new layer with 'bitbake-layers add-layer ../meta-my-layer'
[build-qemu]$ ls  ..
build-bbb  build-qemu  build-rpi  downloads  meta-my-layer  meta-openembedded  meta-raspberrypi  poky  sstate-cache
[build-qemu]$

Le layer est bien créé mais, comme «bitbake-layers» nous l’affiche de manière un peu ambiguë, il n’est pas encore intégré dans la liste des layers que bitbake parcourera lors des builds. Nous devons l’y ajouter :

[build-qemu]$ bitbake-layers  show-layers
NOTE: Starting bitbake server...
layer                 path                                      priority
========================================================================== 
meta                  /home/testing/Build/Lab/poky/meta   5
meta-poky             /home/testing/Build/Lab/poky/meta-poky  5
meta-yocto-bsp        /home/testing/Build/Lab/poky/meta-yocto-bsp  5
meta-oe               /home/testing/Build/Lab/meta-openembedded/meta-oe  6

[build-qemu]$ bitbake-layers  add-layer  ../meta-my-layer/
NOTE: Starting bitbake server...
[build-qemu]$ bitbake-layers  show-layers
NOTE: Starting bitbake server...
layer                 path                                      priority
==========================================================================
meta                  /home/testing/Build/Lab/poky/meta   5
meta-poky             /home/testing/Build/Lab/poky/meta-poky  5
meta-yocto-bsp        /home/testing/Build/Lab/poky/meta-yocto-bsp  5
meta-oe               /home/testing/Build/Lab/meta-openembedded/meta-oe  6
meta-my-layer         /home/testing/Build/Lab/meta-my-layer  6
[build-qemu]$ 

Nous créons dans notre layer un début d'arborescence pour héberger nos recettes spécifiques. Son nom doit commencer par «recipes-». Je choisis arbitrairement «recipes-custom».

[build-qemu]$ mkdir  ../meta-my-layer/recipes-custom/ 

Il faut ensuite y créer un sous-répertoire nommé «images» pour stocker nos recettes décrivant des images. Puis nous y copions le fichier core-image-base.bb pour avoir un point de départ.

[build-qemu]$ mkdir  ../meta-my-layer/recipes-custom/images/
[build-qemu]$ cp  ../poky/meta/recipes-core/images/core-image-base.bb ../meta-my-layer/recipes-custom/images/my-image.bb 

En le copiant, j'ai renommé le fichier «core-image-base.bb» en «my-image.bb». Éditons-le, pour obtenir par exemple le contenu suivant :

SUMMARY = "A customized image for development purposes."
LICENSE = "MIT"
inherit core-image
IMAGE_FEATURES += "splash"
IMAGE_FEATURES += "tools-debug"
IMAGE_FEATURES += "tools-profile"
IMAGE_FEATURES += "tools-sdk"
IMAGE_FEATURES += "ssh-server-dropbear"
IMAGE_INSTALL_append = " mc"
IMAGE_INSTALL_append = " nano"

Nous éditons local.conf pour supprimer toutes les lignes «IMAGE_INSTALL_append» et «IMAGE_FEATURES» que nous avions ajoutées auparavant et lançons le nouveau build avec :

[build-qemu]$ bitbake  my-image

Après compilation, nos fichiers seront disponibles dans tmp/deploy/images/qemuarm/ avec le préfixe «my-image-qemuarm-». Au lancement, runqemu utilise la configuration la plus récente. Néanmoins, il est possible de préciser le nom de l'image pour éviter toute confusion ainsi :

[build-qemu]$ runqemu  my-image  qemuarm

On peut noter, dans la recette d'image ci-dessus la présence de deux syntaxes différentes pour ajouter une chaîne de caractères à la fin du contenu d'une variable :

Mais la différence entre ces deux syntaxes ne se limite pas uniquement à cette histoire d'espace. Sinon nous utiliserions toujours «+=» qui semble plus simple.

Savoir quand utiliser «+=» et quand préférer «_append» n'est pas simple quand il s'agit de compléter des variables initialisées dans des recettes fournies par Poky (ou par des layers supplémentaires, notamment pour le support du matériel). Il est souvent nécessaire d'aller jeter un œil sur l'implémentation des recettes, et il est bon de se familiariser avec l'arborescence de poky/ et celle de meta-openembedded/ par exemple.

Conclusion

Nous avons vu dans cette séquence – assez longue – comment personnaliser notre image en ajoutant des applications dont les recettes sont livrées avec Poky ou référencées sur le site Open Embedded Layers Index. Nous avons également réussi à créer notre propre image, plutôt que de se limiter à enrichir core-image-base en écrivant dans local.conf.

Dans la prochaine séquence, nous verrons comment modifier le comportement d’une application existante, sans toucher aux recettes téléchargées…

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.

«« sommaire »»