Compilation
En gros, la compilation est le processus qui prend un ou plusieurs fichiers sources et produit un exécutable. En fait, comme on le verra, c’est une synecdoque particularisante (figure de style où on parle du tout en utilisant un terme pour une partie seulement). Pour éviter cette figure de style malheureuse, on peut parler de génération d’un exécutable (build en anglais).
Voici les étapes de la génération.
Préprocessing
Le préprocessing consiste à appliquer un prétraitement simple à un code source.
En C, les mots-clés commençant par un # correspondent à un traitement de préprocessing.
Exemple : traitement des `#define’
Par exemple #define X 32 remplace les occurrences de X par 32.
#define X 32
int main() {
int a = X + 2;
int b = X;
}
int main() {
int a = 32 + 2;
int b = 32;
}int x
Exemple : traitement des `#include’
La ligne #include "bloup.h" insère le contenu du fichier bloup.h :
#include "bloup.h"
int main() {
bloup_create();
bloup_inform(3);
}
void bloup_create();
void bloup_inform(int x);
void bloup_extract(int x, int y);
void bloup_free();
int main() {
bloup_create();
bloup_inform(3);
}
Exécuter le préprocesseur
Pour n’exécuter QUE le préprocesseur, il faut utiliser l’option -E (Expand macros) :
gcc -E main.c -o mainresultat.c
gcc -E sur un fichier source de votre choix.
Compiler le projet en un coup
Voici une commande pour construire un exécutable :
gcc myotherCfile.c main.c -o main -Wall
où le flag -o veut dire output.
- Quel défaut y-a-t-il de compiler tout ?
main.c demande de compiler tout depuis le début, le compilateur doit relire myotherCfile.c en entier aussi, alors que ce fichier n’a pas été modifié.
Compiler un projet fichier par fichier
La solution pour avoir une compilation plus rapide est de compiler uniquement les fichiers sources qui ont été modifiés.
Pour obtenir un fichier objet à partir d’un fichier source, on exécute gcc -c où le flag -c signifie que l’on ne fait compiler :
gcc -c -o main.o main.c
dynarray, dynarray_create, etc.
Avec
gcc -c -o dynarray.o dynarray.c
.c en fichier objet (du code machine) .o mais en laissant des “trous” pour les fonctions qui sont définies ailleurs.
Mais on est rendu, où est donc l’exécutable que l’on peut lancer ?
Liaison
gcc -o main main.o dynarray.o
dynarray, dynarray_create, etc.
Puis :
.o en un seul fichier exécutable.
Ainsi, les fichiers .o contiennent des trous et la liaison les remplit. Plus précisément :
- la liaison lie les différents fichiers objet entre eux.
- la liaison lie les fonctions de la
libcutilisées dans les fichiers objet avec le code exécutable correspondant - la liaison s’occupe de lier avec du code exécutable dans des bibliothèques (voir plus bas)
Résumé
En fait, on peut compiler chaque fichier source séparemment puis tout lier à la fin :
gcc -c -o dynarray.o dynarray.c
gcc -c -o main.o main.c
gcc -o main main.o dynarray.o
Chaîne de compilation
C Source Code
|
v
+----------------+
| Preprocessor |
+----------------+
|
v
+----------------+
| Compiler |
+----------------+
|
v
+----------------+
| Linker |
+----------------+
|
v
Executable
Bibliothèques
Si votre projet utilise des bibliothèques, autres que la libc, par exemple raylib, pthread, etc. alors il faut l’indiquer au compilateur. Par exemple :
gcc main.c -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 -o mon_programme
La liaison va alors rappatrier le code exécutable des bibliothèques pour construire votre exécutable. On parle de liaison statique (statique = fait à la compilation).
Le code exécutable des bibliothèques se trouve dans des fichiers .a (pour archive de code objet statique).
DrawRectangle…
Liaison dynamique
On peut aussi utiliser des librairies, sans mettre le code exécutable dans notre programme exécutable. On parle de liaison dynamique (on fera ça plus tard, ou… jamais). Le programme, à l’exécution, va lire un fichier .so (shared object) pour y trouver le code exécutable.