«« sommaire »»

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

Christophe BLAESS - janvier 2020

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       apmd        efivar      gnu-efi  keymaps  lrzsz    pciutils  setserial  usbinit   v86d
alsa-state  efibootmgr  formfactor  grub     libacpi  opensbi  pm-utils  u-boot     usbutils

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

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

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

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

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

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

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

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

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

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

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

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 près 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"
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. Je n’ai pas intégré l’utilitaire valgrind car il n’est pas supporté par la génération de processeur émulé par qemuarm, mais si vous faites un build pour Raspberry Pi par exemple, il vous est tout à fait possible de l’ajouter.

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.0.1 mybox /dev/ttyAMA0

mybox login: root
Password: (linux)

root@mybox:~# strace -V
strace -- version 5.3
Copyright (c) 1991-2019 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) 8.3.1
Copyright (C) 2019 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.10
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 zeus 
Clonage dans 'meta-openembedded'…
remote: Counting objects: 121436, done.
remote: Compressing objects: 100% (41174/41174), done.
[...]
Extraction des fichiers: 100% (5553/5553), 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 plus d’un millier de 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.

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 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

L’emplacement du fichier est logique. 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 tester la présence des outils de compilation sur la cible après avoir regénéré l'image. 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) 9.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 tout 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 nous utilisons pour nos builds. Nous devons l’y ajouter :

[build-qemu]$ bitbake-layers  show-layers
NOTE: Starting bitbake server...
layer                 path                                      priority
========================================================================== 
meta                  /media/cpb/Yocto-lab/poky/meta  5
meta-poky             /media/cpb/Yocto-lab/poky/meta-poky  5
meta-yocto-bsp        /media/cpb/Yocto-lab/poky/meta-yocto-bsp  5
meta-oe               /media/cpb/Yocto-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                  /media/cpb/Yocto-lab/poky/meta  5
meta-poky             /media/cpb/Yocto-lab/poky/meta-poky  5
meta-yocto-bsp        /media/cpb/Yocto-lab/poky/meta-yocto-bsp  5
meta-oe               /media/cpb/Yocto-lab/meta-openembedded/meta-oe  6
meta-my-own-layer     /media/cpb/Yocto-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 »»