CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=hello
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
.cpp.o:
$(CC) $(CFLAGS) $< -o $@
Que font exactement les $@
et $<
?
$@
est le nom du fichier en cours de génération et $<
le premier prérequis (généralement le fichier source). Vous pouvez trouver une liste de toutes ces variables spéciales dans le manuel GNU Make .
Par exemple, considérons la déclaration suivante:
all: library.cpp main.cpp
Dans ce cas:
$@
est évalué à all
$<
est évalué à library.cpp
$^
est évalué à library.cpp main.cpp
Les $@
et $<
s'appellent des variables automatiques . La variable $@
représente le nom du fichier créé (c'est-à-dire la cible) et $<
représente le premier pré-requis requis pour créer le fichier de sortie.
Par exemple:
hello.o: hello.c hello.h
gcc -c $< -o $@
Ici, hello.o
est le fichier de sortie. C’est ce que $@
étend. La première dépendance est hello.c
. C'est ce que $<
se développe.
L'indicateur -c
génère le fichier .o
; voir man gcc
pour une explication plus détaillée. -o
spécifie le fichier de sortie à créer.
Pour plus de détails, vous pouvez lire cet article sur les Makefiles Linux .
Vous pouvez également vérifier les manuels GNU make
. Cela facilitera la création de Makefiles et leur débogage.
Si vous exécutez cette commande, la base de données Makefile sera générée:
make -p
Depuis Gestion des projets avec GNU Marque, 3ème édition (sous licence de documentation libre GNU ):
Les variables automatiques sont définies par
make
après la mise en correspondance d'une règle. Ils fournissent un accès aux éléments des listes de cibles et prérequises, vous évitant ainsi de spécifier explicitement les noms de fichiers. Ils sont très utiles pour éviter la duplication de code, mais ils sont essentiels pour définir des règles de modèle plus générales.Il y a sept variables automatiques "principales":
$@
: Le nom du fichier représentant la cible.
$%
: L'élément filename d'une spécification de membre d'archive.
$<
: Nom du fichier du premier préalable.
$?
: noms de tous les prérequis plus récents que la cible, séparés par des espaces.
$^
: Les noms de fichiers de tous les prérequis, séparés par des espaces. Les noms de fichiers en double ont été supprimés de cette liste, car pour la plupart des utilisations, telles que la compilation, la copie, etc., les doublons ne sont pas souhaités.
$+
: Semblable à$^
, il s'agit des noms de tous les prérequis séparés par des espaces, sauf que$+
inclut les doublons. Cette variable a été créée pour des situations spécifiques telles que des arguments pour des lieurs où les valeurs en double ont une signification.
$*
: La souche du nom de fichier cible. Un radical est typiquement un nom de fichier sans son suffixe. Son utilisation en dehors des règles de modèle est découragée.De plus, chacune des variables ci-dessus a deux variantes pour la compatibilité avec d'autres marques. Une variante ne renvoie que la partie répertoire de la valeur. Ceci est indiqué en ajoutant un “D” au symbole,
$(@D)
,$(<D)
, etc. L'autre variante ne renvoie que la portion de fichier de la valeur. Ceci est indiqué en ajoutant un “F” au symbole,$(@F)
,$(<F)
, etc. Notez que ces noms de variantes ont plusieurs caractères et doivent donc être placés entre parenthèses. GNU make fournit une alternative plus lisible avec les fonctions dir et notdir.
Les $@
et $<
sont des macros spéciales.
Où:
$@
est le nom du fichier de la cible.
$<
est le nom de la première dépendance.
Le Makefile construit l'exécutable hello
si l'un des main.cpp
, hello.cpp
, factorial.cpp
change. Le plus petit Makefile possible pour atteindre cette spécification aurait pu être:
hello: main.cpp hello.cpp factorial.cpp
g++ -o hello main.cpp hello.cpp factorial.cpp
Pour améliorer ce qui précède, nous ne compilons que les fichiers C++ qui ont été modifiés. Ensuite, nous lions simplement les fichiers objets résultants ensemble.
OBJECTS=main.o hello.o factorial.o
hello: $(OBJECTS)
g++ -o hello $(OBJECTS)
main.o: main.cpp
g++ -c main.cpp
hello.o: hello.cpp
g++ -c hello.cpp
factorial.o: factorial.cpp
g++ -c factorial.cpp
Pour améliorer cela, nous pouvons remplacer toutes les règles de fichier objet par une seule règle .cpp.o
:
OBJECTS=main.o hello.o factorial.o
hello: $(OBJECTS)
g++ -o hello $(OBJECTS)
.cpp.o:
g++ -c $< -o $@
Ici, la règle .cpp.o
définit comment construire anyfile.o
à partir de anyfile.cpp
.
$<
correspond à la première dépendance, dans ce cas, anyfile.cpp
$@
correspond à la cible, dans ce cas, anyfile.o
.Les autres modifications présentes dans le Makefile sont:
par exemple si vous voulez compiler des sources mais que les objets se trouvent dans un autre répertoire:
vous devez faire:
gcc -c -o <obj/1.o> <srcs/1.c> <obj/2.o> <srcs/2.c> ...
mais dans la plupart des cas (avec d'autres macros), le résultat sera:
gcc -c -o <all OBJ path> <all SRC path>
donc cela ne compilera rien ^^ et vous ne pourrez pas mettre les fichiers de vos objets dans un répertoire différent :(
la solution est d'utiliser ces macros spéciales
$@ $<
cela générera un fichier .o (obj/file.o) pour chaque fichier .c dans SRC (src/file.c)
$(OBJ):$(SRC)
gcc -c -o $@ $< $(HEADERS) $(FLAGS)
ça veut dire :
$@ = $(OBJ)
$< = $(SRC)
mais lignes par lignes au lieu de toutes les lignes de OBJ suivi de toutes les lignes de SRC