Makefile
personnaliséMakefile
Pour le moment, nous avons compilé notre code applicatif métier à l’extérieur de Yocto, en utilisant la toolchain extraite (et installée éventuellement sur une autre machine de développement). Ceci est parfaitement acceptable — et même recommandé — pendant les phases initiales de codage, mise au point, et débogage pour simplifier le travail du développeur et réduire les temps de compilation.
Une fois le projet suffisament stable pour les premiers déploiements, il est intéressant d'incorporer la compilation du code métier directement dans la production de l'image embarquée. Généralement cela concerne une branche stable du code source, tandis que des branches de développement, d'expérimentation et de test coexistent dans le système de gestion de versions.
Dans cette séquence nous allons voir comment obtenir une
recette simple afin d’intégrer directement notre code
applicatif dans l’image produite par bitbake
.
Les recettes les plus simples à écrire
sont celles qui font appel aux méthodes des outils de construction standards comme les
Autotools. En effet, Yocto contient déjà des classes (des
fichiers «.bbclass
» situés dans
poky/meta/classes-recipes/
) capables de compiler ce type de
projet. Il suffit que notre recette demande à hériter des méthodes de
la classe.
Nous pouvons assez facilement écrire ce type de recette. Mais il y a encore mieux :
la commande «devtool
»
que nous avons déjà utilisée précédemment est capable de
créer la recette adéquate en étudiant l'organisation des fichiers sources. Il nous suffit de lui
donner le lien vers le répertoire de sources :
[build-qemu]$ devtool add hello-autotools https://github.com/logilin/hello-autotools NOTE: Starting bitbake server... INFO: Fetching git://github.com/logilin/hello-autotools;protocol=https;branch=master... [...] INFO: Using default source tree path /home/Builds/Lab/builds/build-qemu/workspace/sources/hello-autotools INFO: Recipe /home/Builds/Lab/builds/build-qemu/workspace/recipes/hello-autotools/hello-autotools_git.bb has been automatically created; further editing may be required to make it fully functional
«devtool
» nous indique avoir téléchargé le package de sources
et l'avoir extrait dans le sous-répertoire «workspace/sources/
»
comme nous l'avions déjà vu avec notre patch pour nano
Puis elle nous dit avoir créé une recette de base qu'ellle
nous conseille de vérifier.
Nous pouvons examiner et éditer cette recette
[build-qemu]$ devtool edit-recipe hello-autotools # Recipe created by recipetool # This is the basis of a recipe and may need further editing in order to be fully functional. # (Feel free to remove these comments when editing.) # WARNING: the following LICENSE and LIC_FILES_CHKSUM values are best guesses - it is # your responsibility to verify that the values are complete and correct. LICENSE = "GPL-2.0-only" LIC_FILES_CHKSUM = "file://LICENSE;md5=8c16666ae6c159876a0ba63099614381" SRC_URI = "git://github.com/logilin/hello-autotools;protocol=https;branch=master" # Modify these as desired PV = "1.0+git" SRCREV = "c5e0f97948c37f38b016dc6ec616a1a4389a592f" S = "${WORKDIR}/git" # NOTE: if this software is not capable of being built in a separate build directory # from the source, you should replace autotools with autotools-brokensep in the # inherit line inherit autotools # Specify any options you want to pass to the configure script using EXTRA_OECONF: EXTRA_OECONF = ""
Ce fichier mérite quelques explications :
SUMMARY
, DESCRIPTION
,
SECTION
, LICENSE
et
LIC_FILES_CHKSUM
dans
la séquence III.1.
Il ne me semble pas inutile d'ajouter une petite ligne de résumé dans
la variable «SUMMARY
».
SRC_URI
contient le
lien pour télécharger les sources du projet.
Yocto connaît les types d’URL courant (http://
,
https://
, ftp://
,git://
,
svn://
, etc.) ainsi que les principales extensions
de compression (.tar.gz
, .zip
,
.tar.bz2
, etc.).
Il s’occupe donc de télécharger et d’extraire les fichiers
sources.
S
»
(comme Sources) indique l’emplacement où
bitbake
doit se placer pour réaliser les étapes de
la compilation. Ici, c'est le sous-répertoire git/
du répertoire de travail ${WORKDIR}
.
inherit
autotools
» enfin indique que la recette
doit hériter des méthodes de la classe autotools
pour les étapes do_compile()
,
do_install()
, etc.
La variable «EXTRA_OECONF
»
permet d'ajouter des options spécifiques pour la compilation. Ce
n'est pas nécessaire ici.
La recette légèrement modifiée est donc la suivante :
[build-qemu]$ cat workspace/recipes/hello-autotools/hello-autotools_git.bb SUMMARY = "Simple Hello World based on autotools." LICENSE = "GPL-2.0-only" LIC_FILES_CHKSUM = "file://LICENSE;md5=8c16666ae6c159876a0ba63099614381" SRC_URI = "git://github.com/logilin/hello-autotools;protocol=https;branch=master" PV = "1.0+git" SRCREV = "c5e0f97948c37f38b016dc6ec616a1a4389a592f" S = "${WORKDIR}/git" inherit autotools EXTRA_OECONF = ""
Je la valide, un peu comme nous l'avons fait avec le patch de nano
,
en précisant dans quel layer et quel sous-répertoire «recipes-...
»
l'ajouter :
[build-qemu]$ devtool finish hello-autotools ../../layers/meta-my-layer/recipes-custom/ NOTE: Starting bitbake server... [...] INFO: Moving recipe file to /home/Builds/Lab/layers/meta-my-layer/recipes-custom/hello-autotools [...] [build-qemu]$ ls ../../layers/meta-my-layer/recipes-custom/hello-autotools/ hello-autotools_git.bb
Il nous suffit à présent d’ajouter la ligne suivante dans notre fichier image.
IMAGE_INSTALL:append = " hello-autotools"
Après re-compilation, notre projet est bien installé :
My experimental distro 1.0 mybox ttyAMA0 mybox login: root Password: (linux) root@mybox:~# hello-autotools Hello from mybox (built with autools) root@mybox:~#
Les projets que l’on compile avec l’environnement
CMake peuvent également être facilement incorporés à Yocto
en utilisant devtool
:
[build-qemu]$ devtool add hello-cmake https://github.com/logilin/hello-cmake [...] INFO: Recipe /home/Builds/Lab/builds/build-qemu/workspace/recipes/hello-cmake/hello-cmake_git.bb has been automatically created; further editing may be required to make it fully functional [build-qemu]$ devtool edit-recipe hello-cmake
Après suppression des lignes de commentaires inutiles, la recette devient :
LICENSE = "GPL-2.0-only" LIC_FILES_CHKSUM = "file://LICENSE;md5=8c16666ae6c159876a0ba63099614381" SRC_URI = "git://github.com/logilin/hello-cmake;protocol=https;branch=master" PV = "1.0+git" SRCREV = "dd3affd45589b47c60e26369c0a747b135eb1fee" S = "${WORKDIR}/git" inherit cmake EXTRA_OECMAKE = ""
Je valide la recette puis je l'ajoute dans mon image :
[build-qemu]$ devtool finish hello-cmake ../../layers/meta-my-layer/recipes-custom/ [...] [build-qemu]$ nano ../../layers/meta-my-layer/recipes-custom/images/my-image.bb [...] IMAGE_INSTALL:append = " hello-cmake"
Et après compilation et boot, on observe :
My experimental distro 1.0 mybox ttyAMA0 mybox login: root Password: (linux) root@mybox:~# hello-cmake Hello from mybox (built with CMake) root@mybox:~#
Makefile
personnalisé
Il arrive fréquemment qu’un projet dispose d’un
Makefile
spécifique, directement livré avec les
sources, sans passer par la phase de configuration des
Autotools ou de CMake.
Pour ce type de projet, devtool
est capable de créer une recette
par défaut, mais il peut être nécessaire de l'éditer un peu plus que
les précédentes.
[build-qemu]$ devtool add hello-makefile https://github.com/logilin/hello-makefile [...] INFO: Recipe /home/Builds/Lab/builds/build-qemu/workspace/recipes/hello-makefile/hello-makefile_git.bb has been automatically created; further editing may be required to make it fully functional [build-qemu]$ devtool edit-recipe hello-makefile
Après quelques effacements de commentaires, la recette devient :
LICENSE = "GPL-2.0-only" LIC_FILES_CHKSUM = "file://LICENSE;md5=8c16666ae6c159876a0ba63099614381" SRC_URI = "git://github.com/logilin/hello-makefile;protocol=https;branch=master" PV = "1.0+git" SRCREV = "f15090770356f854c3403ebe0bcaad51d4439ffc" S = "${WORKDIR}/git" do_compile () { oe_runmake } do_install () { oe_runmake install 'DESTDIR=${D}' }
Nous voyons l'utilisation de la commande
«oe_runmake()
»
(Open Embedded Run Make) de Yocto Project qui permet
d'appeler make
en ayant renseigné les variables
d'environnement nécessaires.
Vérifions le Makefile
du projet :
[build-qemu]$ cat workspace/sources/hello-makefile/Makefile [...] CC ?= gcc CFLAGS += -Wall EXE = hello-makefile OBJS = hello-makefile.o DESTDIR ?= /usr/local/bin [...] .PHONY: install install: $(EXE) install -m 0755 -d ${DESTDIR}/ install -m 0755 ${EXE} ${DESTDIR}/
Nous voyons que le Makefile
installe le fichier exécutable
directement dans le répertoire ${DESTDIR}
. Mais la recette
lui passe la valeur ${D}
c'est-à-dire la racine du système
de fichiers de la cible. Nous devons modifier cette ligne. Je remplace
donc la fin de la recette ainsi :
[...] do_install () { oe_runmake install 'DESTDIR=${D}${bindir}' }
Je valide ma recette :
[build-qemu]$ devtool finish hello-makefile ../../layers/meta-my-layer/recipes-custom/
Après ajout de
«IMAGE_INSTALL:append = " hello-makefile"
»
dans mon fichier d'image, je relance le build et au démarrage de
l'émulateur je peux exécuter :
mybox login: root Password: (linux) root@mybox:~# hello-makefile Hello from mybox (build with standard Makefile) root@mybox:~#
Makefile
Finalement, les recettes les plus complexes sont celles qui doivent
compiler des packages ne comportant aucun
Makefile
(ou un Makefile
mal fichu,
qui ne permet pas la cross-compilation). Pour nous placer dans
la situation la plus défavorable, imaginons un projet ne comportant
qu’un fichier source
à télécharger depuis un site web, avec le contenu suivant :
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/param.h> #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif int main(void) { char hostname[MAXHOSTNAMELEN]; gethostname(hostname, MAXHOSTNAMELEN); hostname[MAXHOSTNAMELEN - 1] = '\0'; fprintf(stdout, "Simple hello from %s (built without Makefile)\n", hostname); #ifndef NDEBUG fprintf(stdout," [DEBUG MODE]\n"); #endif #ifdef MY_OPTION fprintf(stdout, " [MY OPTION]\n"); #endif return EXIT_SUCCESS; }
Le fichier ci-dessus prend en considération deux constantes symboliques qui peuvent être précisées lors de la compilation :
NDEBUG
(No Debug) pour indiquer que le code
n’est plus en mode «débogage» et que certains
messages de traces et certaines vérifications peuvent être
ignorés.
MY_OPTION
qui nous permettra de vérifier le comportement en affichant un message
spécifique si elle est présente lors de la compilation.
Je crée un répertoire hello-simple
dans mon layer,
et j’ajoute la recette hello-simple.bb
suivante, dans
laquelle la compilation est explicitement réalisée par la méthode
do_compile()
et la copie de l'exécutable par
do_install()
. Le fichier source initial n'ayant pas de
numéro de version, on voit que le fichier de recette est nommé
simplement avec le nom du package.
[build-qemu]$ mkdir ../../layers/meta-my-layer/recipes-custom/hello-simple/
Voici le contenu du fichier de recette :
DESCRIPTION = "A simple hello world application built without Makefile." SECTION = "Custom" LICENSE = "GPL-2.0-only" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6" SRC_URI="https://www.logilin.fr/files/yocto-lab/files/${BPN}.c" SRC_URI[md5sum] = "ac02346e365c2030fd9acee21023028a" S = "${WORKDIR}" TARGET_CFLAGS += "-DNDEBUG -DMY_OPTION" do_compile() { ${CC} ${CFLAGS} ${LDFLAGS} ${S}/${BPN}.c -o ${WORKDIR}/${BPN} } do_install() { install -m 0755 -d ${D}${bindir} install -m 0755 ${WORKDIR}/${BPN} ${D}${bindir} }
Comme on le voit, la fonction do_compile()
fait
directement appel au compilateur C, avec les options nécessaires pour
la compilation et l’édition des liens. La variable CFLAGS
est automatiquement renseignée avec le contenu de la variable
TARGET_CFLAGS
pendant la cross-compilation pour
la cible. Dans cette variable, nous avons défini les deux constantes
NDEBUG
et MY_OPTION
.
Le code source du programme appelle fprintf()
en fonction
de la présence de ces constantes. Bien entendu, pour des projets réels,
on est amené à définir des options plus complètes dans
TARGET_CFLAGS
, et éventuellement TARGET_LDFLAGS
.
La recette ci-dessus ne fait pas référence au nom du fichier source,
elle utilise seulement le nom du package à travers la variable
«${BPN}
», elle pourrait donc être adaptée
facilement (à la somme de contrôle MD5 près) pour n’importe quel
fichier à compiler de cette manière. Après compilation, nous pouvons
vérifier que les deux options ont bien été passées au compilateur. Le
premier message ne s’affiche que si l’option n’est pas définie, il est
donc normal qu’il ne soit pas présent ici.
My experimental distro 1.0 mybox ttyAMA0 mybox login: root Password: (linux) root@mybox:~# hello-simple Simple hello from mybox (built without Makefile) [MY OPTION] root@mybox:~#
Nous avons vu dans cette séquence différentes manières d’intégrer dans
le build de Yocto des applications externes, qu’il s’agisse de
projets développés spécifiquement ou des utilitaires libres glanés sur
Internet (dans ce cas, il est toutefois préférable de s’assurer que la
recette ne soit pas déjà disponible quelque part). Cette intégration
de code personnalisé dans l’image produite par Yocto Project est
généralement réalisée en fin de projet, car il est plus simple pendant
la phase de mise au point d'assurer la compilation indépendamment de
bitbake
, comme nous l’avons vu dans la séquence
précédente.
Ainsi s’achève la troisième partie de ce cours en ligne, après le développement du code métier, nous allons nous intéresser à l’ajustement de l'image pour une cible spécifique.
Si vous préférez une session de cours interactif, en mode présentiel ou distanciel, n'hésitez pas à vous inscrire à mes formations "Développeur Linux embarqué avec Yocto Project" ou "Yocto Project avancé" .
Ce document est placé sous licence Creative Commons 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.
Le nom Yocto Project est une marque déposée par la Linux Foundation. Le présent document n'est en aucune façon approuvé par Yocto Project ou la Linux Foundation.