Chaîne d'outils GNU (et autres outils)
make→ utilitaire pour l’automation de la génération de logicielgcc→ compilateur C/C++ar→ utilitaire pour la création de bibliothèquesas→ assembleur-
ld→ éditeur de liens -
gdb→ debugger / debugger ddd→ data display debugger-
addr2line→ utilitaire pour convertir une adresse vers un fichier et une ligne -
nm→ utilitaire pour lister les symboles d’un fichier de code objet objdump→ utilitaire pour lister des informations d’un fichier de code objetsize→ utilitaire pour lister la taille du code objetstrings→ utilitaire pour lister les chaines de caractères imprimables-
strip→ utilitaire pour éliminer les symboles d’un fichier de code objet -
gcov→ code/test coverage gprof→ profiling tool
Compilation
Avec la chaîne d’outils GNU sous Linux
compilation de l’application pour le debugging
gcc -g -c -Wall -Wextra -O2 -std=gnu11 -o fibonacci.o fibonacci.c
-g→ enclenche les options pour le debugging-c→ indique au compilateur de ne pas linker l’application-Wall→ enclenche tous les warning-Wextra→ enclenche des warning supplémentaires-O2→ force l’optimisation du code généré-std=gnu11→ autorise l’utilisation des extensions de 2011-o→ spécifie le nom du fichier de sortie
Si tous les fichiers d’entête (header files) ne sont pas dans le même répertoire que le fichier à compiler:
-I dir→ spécifie le répertoire où le compilateur trouvera les header files, par exemple:-I. -I.. -I /workspace/include
Linkage
après la compilation de l’application, vous devez linker tous les fichiers afin d’obtenir le fichier exécutable:
gcc fibonacci.o -o fibonacci
il est également possible de compiler et de linker l’application en une seule commande:
gcc -g -Wall -Wextra -O2 -std=gnu11 -o fibonacci fibonacci.c
-o→ spécifie le nom de l’exécutable-c→ attention ce flag ne doit pas être utilisé!!!
Lancement & désassemblage
démarrage l’application
./fibonacci 15
taille du code objet
size -t fibonacci.o
désassemblage du code objet
objdump -d -S -C fibonacci.o
désassmblage de l’application
objdump -d -S -C fibonacci
élimination des symboles de debugging (pour rendre le code plus léger)
strip -g -o fibonacci_s fibonacci
Utilitaire - make
make est un utilitaire permettant de décrire les dépendances d’une application et
de compiler les fichiers objets nécessaires pour sa génération.
make ou make -f Makefile ou make -f mymakefile
La description de ces dépendances se fait par l’intermédiaire d’un fichier texte. Le nom de défaut est Makefile, mais il est possible de le nommer différemment.
Exemples:
exec: main.o mymathlib.o
gcc -o exec main.o mymathlib.o
main.o: main.c mymathlib.h
gcc -c -Wall -Wextra -O2 -std=gnu11 -g -o main.o main.c
mymathlib.o: mymathlib.c mymathlib.h
gcc -c -Wall -Wextra -O2 -std=gnu11 -g -o mymathlib.o mymathlib.c
make - variables
L’utilisation de variables simplifie grandement l’écriture des Makefile,
par exemple :
EXEC=exec
CC=gcc
CFLAGS=-Wall -Wextra -O2 -std=gnu11 -g -c
$(EXEC): main.o mymathlib.o
$(CC) $(LDFLAGS) -o $(EXEC) main.o mymathlib.o
main.o: main.c mymathlib.h
$(CC) $(CFLAGS) -o main.o main.c
mymathlib.o: mymathlib.c mymathlib.h
$(CC) $(CFLAGS) -o mymathlib.o mymathlib.c
make - plusieurs cibles (targets)
Il est possible de spécifier dans un même «Makefile» plusieurs cibles différentes, par exemple :
make - règles suffixes
Les règles suffixes permettent d’éviter de spécifier pour chaque fichier (cible ou dépendance) les commandes à effectuer. Ce travail fastidieux peut être simplifié de la manière suivante:
make - dépendances
Lors de la génération d’applications développées en C il est essentiel de
garantir que celles-ci soient correctement générées et que toutes les
modifications soient bien prises en compte. Pour ce faire, le compilateur GNU
permet de générer un fichier des dépendances, qui pourra ensuite être utilisé
dans le Makefile.
make - conditions
make offre des directives permettant d’exécuter conditionnellement une
partie du Makefile, ceci en fonction d’une variable et de sa valeur, par exemple :
ifeq ($(VARIABLE), value)
## if true do that
else
## if false do this
endif
La variable peut être contenue dans le Makefile, mais il est également
possible de la spécifier lors de l’appel du Makefile, par exemple :
make VARIABLE=value <cible>
Cette technique permet de générer un logiciel pour différentes plateformes (machine hôte, cible, …) ou en différentes versions (release, debug, …)
make - sous-Makefile
make permet de créer plusieurs Makefile où chacun correspond à une
partie distincte d’un grand projet. Pour simplifier sa génération, on préfère
généralement appeler un Makefile unique gérant l’ensemble de la
génération de l’application.
La variable $(MAKE) fournit l’outil nécessaire pour l’appel de sous-Makefile.
$(MAKE) -C <directory> <target>
Le mot clef export permet de passer les variables du Makefile maître aux
sous-Makefile
make - commandes silencieuses
Pour éviter d’afficher une longue liste de commandes et d’arguments lors de la
génération d’une application, make propose une instruction permettant de
supprimer l’écho des lignes de commandes. Pour ceci, il suffit de rajouter le
caractère «@» devant la ligne de commande, par exemple :
.c.o:
@echo "(CC) $<"
@$(CC) $(CFLAGS) -MD -o $*.o $<
$(EXEC): $(OBJS)
@echo "(LD) $@"
@$(CC) $(LDFLAGS) -o $@ $^
CMake
Les Makefile sont très pratiques pour la génération d’applications,
mais ils peuvent devenir très rapidement difficiles à maintenir, surtout
pour les projets de grande envergure. C’est pour cette raison que des
outils de génération de Makefile ont été développés, parmi lesquels on
trouve CMake.
Pour générer un Makefile à l’aide de CMake, il suffit de créer un
fichier CMakeLists.txt à la racine du projet, et d’y spécifier les différentes cibles et dépendances.
Le fichier permettant de compiler fibonacci peut être le suivant:
cmake_minimum_required(VERSION 3.28)
project(fibonacci)
add_executable(fibonacci fibonacci.c)
Ce CMakeLists.txt peut être suffisant pour de la compilation native, mais pour de la compilation croisée, il est nécessaire de spécifier le compilateur à utiliser. On peut pour cela créer un fichier nanopi.cmake contenant les informations nécessaires :
set(CMAKE_SYSTEM_NAME Linux)
find_program(CMAKE_C_COMPILER aarch64-linux-gcc)
find_program(CMAKE_CXX_COMPILER aarch64-linux-g++)
set(CMAKE_FIND_ROOT_PATH /buildroot/output/host)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_C_FLAGS "-Wall -Wextra -g -O0 -MD -std=gnu17")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=cortex-a53 -funwind-tables")
set(CMAKE_CXX_FLAGS "-Wall -Wextra -g -O0 -MD -std=gnu++17")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=cortex-a53 -funwind-tables")
Ce fichier peut ensuite être utilisé par le CMakeLists.txt de la manière suivante:
cmake_minimum_required(VERSION 3.28)
project(fibonacci)
include(../../nanopi.cmake)
add_executable(fibonacci fibonacci.c)
Pour générer le Makefile à partir du CMakeLists.txt, il suffit de lancer la commande suivante:
cmake -S . -B build
Notez que le résultat se trouve dans le répertoire build, ce qui permet de garder la racine du projet propre.
Pour compiler l’application, vous pouvez soit lancer la commande make dans le répertoire build,
soit utiliser cmake de la manière suivante:
cmake --build build
Votre application est disponible dans le répertoire build et peut être déployée sur la cible
Note
CMake permet aussi de générer des fichiers pour le système Ninja, qui est plus rapide que Make. Vous trouverez plus d’informations dans la documentation officielle de CMake
Just
Just est un outil semblable à make, mais plus moderne et plus facile
à utiliser. Il utilise des fichiers justfile pour décrire les tâches à
exécuter, et offre une syntaxe plus simple et plus lisible que les
Makefile.
Just est particulièrement apprécié pour sa simplicité et sa
flexibilité, et est de plus en plus utilisé dans les projets de
développement logiciel. Contrairement à make, just ne repose pas sur
des règles de suffixes et ne permet pas de limiter les tâches en
fonction de la date de modification des fichiers. Il n’est donc pas un
remplacement direct de make et les deux outils peuvent être utilisés
de manière complémentaire en fonction des besoins du projet.