.. _outils:
Outils
======
Pour le cours de programmation système, nous travaillerons sous Linux avec l'éditeur de code `Visual Studio Code / VSCode `__.
Si, sur votre ordinateur personnel, vous êtes sous Windows, je vous recommande d'utiliser `WSL `__ pour travailler avec un environnement Linux, `accessible directement depuis VSCode `__.
Pour compiler nous utiliserons `GCC `__ (*GNU Compiler Collection*) (possible aussi avec `Clang `__ (*C language family frontend for LLVM*)).
Installation des différents outils utiles au développement (hormis VSCode) sous Debian/Ubuntu :
.. code-block:: bash
sudo apt update
sudo apt install build-essential clang cmake cppcheck valgrind gdb lldb clang-tidy
Dans VSCode, installez les extensions suivantes :
- `C/C++ Extension Pack `__ : pour IntelliSense, la navigation dans le code, le debug, la compilation, ...
.. code-block:: bash
# installation depuis le terminal :
code --install-extension ms-vscode.cpptools-extension-pack
# installation depuis le panel VSCode (Ctrl+P) :
ext install ms-vscode.cpptools-extension-pack
- `Clang-Format `__ : pour le formatage automatique du code avec `clang-format `__.
.. code-block:: bash
# installation depuis le terminal :
code --install-extension xaver.clang-format
# installation depuis le panel VSCode (Ctrl+P) :
ext install xaver.clang-format
.. _outils_compilation:
Compilation
-----------
La compilation d'un programme C se déroule en 3 étapes :
1. Prétraitement – le préprocesseur traite les directives comme ``#include``, ``#define`` et produit un fichier temporaire ``.i`` contenant du C "pur", sans macros ni includes.
.. code-block:: bash
gcc -E fichier.c -o fichier.i
2. Compilation – le compilateur transforme ce fichier en assembleur ``.s``
.. code-block:: bash
gcc -S fichier.i -o fichier.s
3. Assemblage - puis transforme l'assembleur en code binaire dans un fichier objet ``.o``
.. code-block:: bash
gcc -c fichier.s -o fichier.o
4. Édition de liens (linking) – l'éditeur de liens assemble les objets et les bibliothèques en un programme final.
.. code-block:: bash
gcc fichier.o -o programme
En pratique, ces étapes sont enchaînées automatiquement pour produire un exécutable :
.. code-block:: bash
# avec gcc, éventuellement gcc-VERSION
gcc -Wall -Wextra -pedantic -std=c2x monprog.c -o monprog
# ou avec clang, éventuellement clang-VERSION
clang -Wall -Wextra -pedantic -std=c2x monprog.c -o monprog
# puis execution avec
./monprog
Options fréquentes :
+-----------------+------------------------------------------------------------------+
| Option | Effet |
+-----------------+------------------------------------------------------------------+
| ``-Wall`` | Active les avertissements de base |
+-----------------+------------------------------------------------------------------+
| ``-Wextra`` | Avertissements supplémentaires |
+-----------------+------------------------------------------------------------------+
| ``-pedantic`` | Respect strict de la norme |
+-----------------+------------------------------------------------------------------+
| ``-std=c2x`` | Utilisation de la norme C23 |
+-----------------+------------------------------------------------------------------+
| ``-g`` | Ajoute les infos de debug pour ``gdb`` ou ``lldb`` |
+-----------------+------------------------------------------------------------------+
| ``-O2``/``-O3`` | Active les optimisations |
+-----------------+------------------------------------------------------------------+
| ``-c`` | Compile sans édition de liens → produit uniquement un fichier .o |
+-----------------+------------------------------------------------------------------+
| ``-o fichier`` | Définit le nom du fichier de sortie |
+-----------------+------------------------------------------------------------------+
.. _outils_fuites:
Analyse à l'exécution
---------------------
Pour détecter les problèmes de mémoire, en particulier lors de l'utilisation de pointeurs (fuites, accès invalides,...), deux outils peuvent être utilisés :
- `valgrind `__ (compiler avec `-g` pour inclure les informations de debug) :
.. code-block:: bash
# compilation
gcc -std=c2x -Wall -Wextra -pedantic -g mon_prog.c -o mon_prog
# exécution d'un programme avec détection des fuites mémoire
valgrind ./mon_prog
# pour un rapport plus détaillé avec les emplacements dans le code source
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./monprog
S'il y a une partie `LEAK SUMMARY` vers la fin, il y a un problème au niveau de l'allocation :
.. code-block:: console
==340142== HEAP SUMMARY:
==340142== in use at exit: 400 bytes in 1 blocks
==340142== total heap usage: 3 allocs, 2 frees, 2,448 bytes allocated
==340142==
==340142== LEAK SUMMARY:
==340142== definitely lost: 400 bytes in 1 blocks
==340142== indirectly lost: 0 bytes in 0 blocks
==340142== possibly lost: 0 bytes in 0 blocks
==340142== still reachable: 0 bytes in 0 blocks
==340142== suppressed: 0 bytes in 0 blocks
Si tout va bien (sur l'allocation), le `HEAP SUMMARY` affiche qu'il y a autant d'allocs que de free :
.. code-block:: console
==336877== HEAP SUMMARY:
==336877== in use at exit: 0 bytes in 0 blocks
==336877== total heap usage: 3 allocs, 3 frees, 2,448 bytes allocated
D'autres erreurs peuvent être affichés lors de l'exécution.
- `AddressSanitizer `__ : activé dans le ``CMakeLists.txt`` (ligne 65) lors d'une compilation en mode debug grâce aux options : ``-fsanitize=address,undefined -fno-sanitize-recover=all``.
Lors d'une compilation à la main (en cas d'affichage de `AddressSanitizer:DEADLYSIGNAL` à répétition, faire `Ctrl+C` puis relancer, peut arriver plusieurs fois) :
.. code-block:: bash
# compilation
gcc -std=c2x -Wall -Wextra -pedantic -g -fsanitize=address,undefined -fno-sanitize-recover=all mon_prog.c -o mon_prog
# exécution
./mon_prog
# si beaucoup de 'AddressSanitizer:DEADLYSIGNAL' s'affichent faire Ctrl+C puis relancer
# (peut arriver plusieurs fois de suite)
Le message en cas d'erreur est clair avec `AddressSanitizer` :
.. code-block:: console
=================================================================
==331863==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7609d7adefdf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x64dcf0ad14a5 in main (path/p1_e4+0x24a5) (BuildId: 4b6a256d8842ed960cc21b54141f97e11cd9ef36)
#2 0x7609d6e29d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s).
`AddressSanitizer` détecte mieux les accès en dehors des limites d'un tableau, par exemple :
.. code-block:: c
:linenos:
#include
int main() {
int tab[5];
tab[10] = 4;
printf("tab[10] = %d\n", tab[10]);
return 0;
}
`AddressSanitizer` affiche (à l'exécution) :
.. code-block:: console
mon_fichier.c:5:8: runtime error: index 10 out of bounds for type 'int [5]'
Là où valgrind ne le détecte pas systématiquement.
Formatage automatique
---------------------
Pour maintenir un code lisible, cohérent et conforme à de bonnes pratiques, les outils de formatage automatique comme `clang-format `__ sont très utiles.
Il permet d'uniformiser le style de code entre plusieurs projets/groupes/classe, de gagner du temps en évitant les discussions inutiles liées à la mise en forme du code et d'éviter les commits sur git avec uniquement des changement de format.
Il utilise le fichier ``.clang-format`` pour lire les règles de formatage à utiliser.
Pour le générer, vous pouvez utiliser la ligne suivante :
.. code-block:: bash
clang-format -style=llvm -dump-config > .clang-format
Personnellement, j'ai modifié les options suivantes (qui se rapprochent plus d'un formatage classique en Python ou Rust) :
.. code-block:: text
AllowShortEnumsOnASingleLine: true -> false
AllowShortFunctionsOnASingleLine: All -> None
AllowShortLambdasOnASingleLine: All -> None
BinPackArguments: true -> false
BinPackParameters: true -> false
ConstructorInitializerAllOnOneLineOrOnePerLine: false -> true
IndentWidth: 2 -> 4
ce qui donne le fichier ``.clang-format`` :
.. admonition:: Voir ``.clang-format``
:class: dropdown
.. literalinclude:: ../code/.clang-format
:language: text
:linenos:
Dans VSCode, l'extension ``Clang-Format`` permet de formater le code automatiquement à la sauvegarde.
Pour automatiser le formatage lorsque le fichier est enregistré : ``File>Preferences>Settings`` (ou ``Ctrl+,`` sous Linux), chercher ``format on save`` puis activer ``Editor: Format On Save``.
Puis lorsqu'un fichier ``.c`` est ouvert, appuyez sur ``Ctrl+Shift+P`` puis tapez ``format document with`` puis sélectionnez ``Configure Default Formatter`` puis ``Clang-Format``. Vous pouvez aussi faire `Ctrl+Shift+I` et définir `Clang-Format` comme formateur par défaut.
Pour un réglage par projet : dans le fichier ``.vscode/settings.json`` (à créer si non existant), ajouter :
.. code-block:: json
{
"editor.formatOnSave": true,
"[c]": {
"editor.defaultFormatter": "xaver.clang-format"
},
"[cpp]": {
"editor.defaultFormatter": "xaver.clang-format"
},
"clang-format.style": "file",
}
Si ça fonctionne, le code :
.. code-block:: c
#include
int main( )
{
int a= 0;
int b = 4 ;
for ( int i = 0;
i< 10; ++ i)
{
printf(
"%d", i
);
}
return 0 ;
}
sera automatiquement formaté en :
.. code-block:: c
#include
int main() {
int a = 0;
int b = 4;
for (int i = 0; i < 10; ++i) {
printf("%d", i);
}
return 0;
}
Pour l'utiliser dans la ligne de commande :
.. code-block:: bash
clang-format -i src/*.c src/*.h
L'option `-i` applique les modifications directement dans les fichiers.
Debug
-----
Il existe plusieurs outils pour le debug dans la ligne de commande comme `gdb `__ ou `lldb `__.
VSCode propose un debugger pratique et rapide à utiliser pour un fichier :
.. youtube:: 69ZHsUsMajM
Pour des projets avec plus d'un fichier, utiliser les outils intégrés avec l'extension CMake est plus adapté.
Je vous invite à voir ce tutoriel `🇫🇷 `__/ `🇬🇧 `__ pour plus de détails.
Automatisation : Make et CMake
------------------------------
`Make `__ est un outil d'automatisation de la compilation.
Il utilise des fichiers ``Makefile`` pour décrire les règles de compilation et les dépendances.
Seuls les fichiers modifiés depuis la dernière compilation sont recompilés.
- `Makefile Tools `__ : pour le support de Make.
.. code-block:: bash
# installation depuis le terminal :
code --install-extension ms-vscode.makefile-tools
# installation depuis le panel VSCode (Ctrl+P) :
ext install ms-vscode.makefile-tools
`CMake `__ est un générateur de build multiplate-forme.
Il permet de générer des ``Makefile`` et la configuration des outils utiles au développement.
Il s'intègre aussi très bien dans les IDE (VSCode, CLion, ...) et permet de gérer des projets complexes.
- `CMake `__ : pour le support de CMake.
- `CMake Tools `__ : pour plus d'outils autour de CMake.
.. code-block:: bash
# installation depuis le terminal :
code --install-extension twxs.cmake
code --install-extension ms-vscode.cmake-tools
# installation depuis le panel VSCode (Ctrl+P) :
ext install twxs.cmake
ext install ms-vscode.cmake-tools
Ci-dessous, un exemple de fichier ``CMakeLists.txt``, lorsque des nouveaux fichiers s'ajoutent au projet, il faut les ajouter sur les lignes 5-8, de la même manière que les fichiers ``main.c`` et ``utils.c`` de l'exemple :
.. admonition:: Voir ``CMakeLists.txt``
:class: dropdown
.. literalinclude:: ../code/CMakeLists.txt
:language: text
:linenos:
Pour gérer ``CMake`` à la main :
.. code-block:: console
# créer et se déplacer dans un dossier build
mkdir -p build
cd build
# génération des fichiers Makefile à partir du CMakeLists.txt
cmake ..
# compilation du projet
cmake --build .
# compilation du projet en mode debug ou release
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake -DCMAKE_BUILD_TYPE=Release ..
Analyse statique
----------------
Pour aider à détecter des bugs que le compilateur ne voit pas, nous pouvons utiliser `clang-tidy `__ (déjà présent dans le fichier ``CMakeLists.txt`` aux lignes 85-89 et avec le fichier ``.clang-tidy``) et `cppcheck `__.
.. code-block:: console
# analyser récursivement un dossier (ex: src/) avec des messages détaillés
# et supprime les messages inutiles sur les includes système
cppcheck --enable=all --inconclusive --std=c23 --language=c --quiet --suppress=missingIncludeSystem src/
Pour un projet complexe ou hors CMake, vous pouvez générer le fichier ``compile_commands.json`` avec :
.. code-block:: bash
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
Ce fichier permet à ``clang-tidy`` de connaître les options de compilation de chaque fichier source.
Autres outils
-------------
Docstring
~~~~~~~~~
Pour générer les templates de docstring, `Doxygen Documentation Generator `__ :
Depuis la ligne de commande :
.. code-block:: bash
code --install-extension cschlosser.doxdocgen
Depuis le panel VSCode (``Ctrl+P``) :
.. code-block:: console
ext install cschlosser.doxdocgen
Ensuite, pour ajouter une docstring à une fonction, taper ``/**`` puis enter :
.. code-block:: c
// 1 taper /** (le */ s'ajoute automatiquement)
/** */
int my_function(int a, float b) {
// ...
}
// 2 presser "Entrer"
/**
* @brief
*
* @param a
* @param b
* @return int
*/
int my_function(int a, float b) {
// ...
}
Ensuite, quand vous passerez le curseur sur la fonction, vous verrez les informations sur ce que fait la fonction et les différents paramètres.
Affichage erreurs
~~~~~~~~~~~~~~~~~
`Error Lens `__ permet d'afficher les erreurs directement sur la ligne, peut être envahissant si vous codez en français avec un spell checker qui mettra en avant toutes les variables où il y a une faute car pas accent.