Makefile

Compiler le projet en un coup

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 ?
L'ajout d'une toute petite instruction dans `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

gcc -c -o myotherCfile.o myotherCfile.c
gcc -c -o main.o main.c
gcc -o main main.o myotherCfile.o

où le flag -c signifie que l'on ne fait compiler mais pas lier.

Comme le montre l'image ci-dessous :

  • la compilation consiste à transformer un fichier source .c en fichier objet (du code machine) .o mais en laissant des "trous" pour les fonctions qui sont définis dans d'autres modules.
  • la liaison vient remplir les trous avec le code machine manquant.

Pourquoi a-t-on besoin d'un outil pour compiler automatiquement ?

Parce que les commandes ci-dessus sont rébarbatives à taper et à retenir. Il vaut mieux un outil pour gérer la compilation.

Compiler avec Makefile ( version naïve)

  • Créer un fichier Makefile.
  • Y écrire :
    all:
        gcc myotherCfile.c main.c -o main -Wall

Dans le terminal, on tape make pour construire le projet. Le nom all s'appelle une cible.

Attention, la ligne d'après contient un tab (et pas 4 espaces !) suivi de la commande à exécuter pour construire le projet.

Compiler intelligente avec Makefile

Avec un Makefile, on peut avoir plusieurs cible.

all: main

main: main.o myotherCfile.o
    gcc -o main main.o myotherCfile.o

main.o: main.c
    gcc -c -o main.o main.c

myotherCfile.o: myotherCfile.c
    gcc -c -o myotherCfile.o myotherCfile.c

clean:
    rm *.o main

La cible main a besoin d'avoir déjà effectué le travail pour les cibles main.o et myotherCfile.o, et consiste à effectuer gcc -o main main.o myotherCfile.o.

  • Où est-ce qu'a lieu la liaison dans le Makefile ci-dessus ? Dans la cible main.

  • Qu'est ce que fait gcc -c -o main.o main.c ?

Elle compile le fichier main.c en main.o en laissant des trous pour les fonctions déclarées mais non définies.

Variables dans un MakeFile

On peut définir des constantes dans un MakeFile. Par exemple, on définit la constante CC qui donne le nom du compilateur. Pour avoir le contenu de la constante on écrit $(CC). Ecrire CC ça écrit juste CC ; nous on veut le contenu.

CC=gcc

all: main

main: main.o myotherCfile.o
    $(CC) -o main main.o myotherCfile.o

main.o: main.c
    $(CC) -c -o main.o main.c

myotherCfile.o: myotherCfile.c
    $(CC) -c -o myotherCfile.o myotherCfile.c

clean:
    rm *.o main

Pattern

Voici trois règles qui ont le même pattern :

myotherCfile.o: myotherCfile.c
    $(CC) -c -o myotherCfile.o myotherCfile.c

bloup.o: bloup.c
    $(CC) -c -o bloup.o bloup.c

miaou.o: miaou.c
    $(CC) -c -o miaou.o miaou.c

Au lieu de cela, on peut écrire :

%.o: %.c
    $(CC) -c -o $@ $^
  • Le % signifie nimportequelnomdefichier.
  • $@ = le nom de la règle nimportequelnomdefichier.o
  • $^ = la prémisse, ici nimportequelnomdefichier.c
nom de la règle:prémisse
$@$^

Lister les fichiers

La commande principale pourrait être :

main: main.o myotherCfile.o bloup.o miaou.o
    $(CC) -o main main.o myotherCfile.o bloup.o miaou.o

Pour réaliser cela, on a besoin de lister les .o. Or, on ne les connait pas encore. Mais on sait qu'il y a en un par fichier source .c. On peut lister les fichiers sources avec la commande wildcard :

SOURCES=$(wildcard *.c)

La fonction wildcard prend un argument qui est une expression régulière de fichiers et elle produit la liste des fichiers qui correspondent à l'expression régulière. Dans l'exemple, la constante SOURCES vaut main.c myotherCfile.c bloup.c miaou.c.

Pour obtenir la liste des .o correspondantes, on fait une substitution :

main.c myotherCfile.c bloup.c miaou.c

🡇 main.o myotherCfile.o bloup.o miaou.o

Pour cela on écrit :

OBJECTS=$(SOURCES:.c=.o)

Et maintenant, la règle principale qui était :

main: main.o myotherCfile.o bloup.o miaou.o
    $(CC) -o main main.o myotherCfile.o bloup.o miaou.o

devient

main: $(OBJECTS)
    $(CC) -o main $^

Aller plus loin

On peut faire des boucles et autres en Makefile... bref...