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