Si vous souhaitez exécuter ou mettre à jour une tâche lorsque certains fichiers sont mis à jour, le make
utilitaire peut être utile. Le make
l'utilitaire nécessite un fichier, Makefile
(ou makefile
), qui définit un ensemble de tâches à exécuter. Vous avez peut-être utilisé make
pour compiler un programme à partir du code source. La plupart des projets open source utilisent make
pour compiler un binaire exécutable final, qui peut ensuite être installé en utilisant make install
.
Dans cet article, nous allons explorer make
et Makefile
à l'aide d'exemples de base et avancés. Avant de commencer, assurez-vous que make
est installé sur votre système.
Exemples de base
Commençons par imprimer le classique "Hello World" sur le terminal. Créer un répertoire vide myproject
contenant un fichier Makefile
avec ce contenu :
say_hello :
echo "Hello World"
Exécutez maintenant le fichier en tapant make
dans le répertoire myproject
. La sortie sera :
$ make
écho "Hello World"
Hello World
Dans l'exemple ci-dessus, say_hello
se comporte comme un nom de fonction, comme dans n'importe quel langage de programmation. C'est ce qu'on appelle la cible . Les prérequis ou dépendances suivre la cible. Par souci de simplicité, nous n'avons défini aucun prérequis dans cet exemple. La commande echo "Hello World"
s'appelle la recette . La recette utilise des prérequis faire une cible . L'objectif, les prérequis et les recettes forment ensemble une règle .
Pour résumer, voici la syntaxe d'une règle type :
cible :prérequisrecette
Par exemple, une cible peut être un fichier binaire qui dépend de prérequis (fichiers source). D'autre part, un prérequis peut aussi être une cible qui dépend d'autres dépendances :
final_target :sub_target final_target.c
Recipe_to_create_final_target
sub_target :sub_target.c
Recipe_to_create_sub_target
Il n'est pas nécessaire que la cible soit un fichier; il peut s'agir simplement d'un nom pour la recette, comme dans notre exemple. Nous appelons cela des "cibles factices".
Revenons à l'exemple ci-dessus, lorsque make
a été exécutée, la commande entière echo "Hello World"
s'affiche, suivi de la sortie réelle de la commande. Souvent, nous ne voulons pas cela. Pour supprimer l'écho de la commande réelle, nous devons démarrer echo
avec @
:
say_hello :
@echo "Hello World"
Essayez maintenant d'exécuter make
de nouveau. La sortie doit afficher uniquement ceci :
$ make
Hello World
Ajoutons quelques cibles factices supplémentaires :generate
et clean
au Makefile
:
say_hello :
@echo "Hello World"
generate :
@echo "Creating empty text files..."
touch file-{1. .10}.txt
clean :
@echo "Nettoyage..."
rm *.txt
Si nous essayons d'exécuter make
après les modifications, seule la cible say_hello
sera exécuté. C'est parce que seule la première cible du makefile est la cible par défaut. Souvent appelé objectif par défaut , c'est la raison pour laquelle vous verrez all
comme première cible dans la plupart des projets. C'est la responsabilité de all
pour appeler d'autres cibles. Nous pouvons remplacer ce comportement en utilisant une fausse cible spéciale appelée .DEFAULT_GOAL
.
Insérons cela au début de notre makefile :
.DEFAULT_GOAL := generate
Cela exécutera la cible generate
par défaut :
$ make
Création de fichiers texte vides...
touch file-{1..10}.txt
Comme son nom l'indique, la fausse cible .DEFAULT_GOAL
ne peut exécuter qu'une seule cible à la fois. C'est pourquoi la plupart des makefiles incluent all
en tant que cible pouvant appeler autant de cibles que nécessaire.
Incluons la fausse cible all
et supprimer .DEFAULT_GOAL
:
all :say_hello generate
say_hello :
@echo "Hello World"
generate :
@echo "Création de fichiers texte vides.. ."
touch file-{1..10}.txt
clean :
@echo "Cleaning up..."
rm *.txtAvant d'exécuter
make
, incluons une autre cible bidon spéciale,.PHONY
, où nous définissons toutes les cibles qui ne sont pas des fichiers.make
exécutera sa recette indépendamment de l'existence ou non d'un fichier portant ce nom ou de l'heure de sa dernière modification. Voici le makefile complet :.PHONY :tous say_hello generate clean
tous :say_hello generate
say_hello :
@echo "Hello World"
générer :
@echo "Création de fichiers texte vides..."
touch file-{1..10}.txt
clean:
@echo "Nettoyage up..."
rm *.txtLe
make
devrait appelersay_hello
etgenerate
:$ make
Hello World
Création de fichiers texte vides...
touch file-{1..10}.txtC'est une bonne pratique de ne pas appeler
clean
dansall
ou le mettre comme première cible.clean
doit être appelé manuellement lorsque le nettoyage est nécessaire comme premier argument demake
:$ make clean
Nettoyage...
rm *.txtMaintenant que vous avez une idée du fonctionnement d'un makefile de base et de la façon d'écrire un makefile simple, regardons quelques exemples plus avancés.
Exemples avancés
Variables
Plus de ressources Linux
- Aide-mémoire des commandes Linux
- Aide-mémoire des commandes Linux avancées
- Cours en ligne gratuit :Présentation technique de RHEL
- Aide-mémoire sur le réseau Linux
- Aide-mémoire SELinux
- Aide-mémoire sur les commandes courantes de Linux
- Que sont les conteneurs Linux ?
- Nos derniers articles Linux
Dans l'exemple ci-dessus, la plupart des valeurs cibles et préalables sont codées en dur, mais dans les projets réels, elles sont remplacées par des variables et des modèles.
La façon la plus simple de définir une variable dans un makefile est d'utiliser le =
opérateur. Par exemple, pour affecter la commande gcc
à une variable CC
:
CC = gcc
Ceci est également appelé une variable étendue récursive , et il est utilisé dans une règle comme indiqué ci-dessous :
bonjour :bonjour.c
${CC} bonjour.c -o bonjour
Comme vous l'avez peut-être deviné, la recette se développe comme ci-dessous lorsqu'elle est transmise au terminal :
gcc hello.c -o hello
Les deux ${CC}
et $(CC)
sont des références valides pour appeler gcc
. Mais si l'on essaie de réaffecter une variable à elle-même, cela provoquera une boucle infinie. Vérifions ceci :
CC =gcc
CC =${CC}
tous :
@echo ${CC}
Exécution de make
entraînera :
$ make
Makefile:8 :*** La variable récursive 'CC' fait référence à elle-même (éventuellement). Arrêtez.
Pour éviter ce scénario, nous pouvons utiliser le :=
opérateur (également appelé variable simplement développée ). Nous ne devrions avoir aucun problème à exécuter le makefile ci-dessous :
CC :=gcc
CC :=${CC}
tous :
@echo ${CC}
Modèles et fonctions
Le makefile suivant peut compiler tous les programmes C en utilisant des variables, des modèles et des fonctions. Découvrons-le ligne par ligne :
# Utilisation :
# make # compiler tous les binaires
# make clean # supprimer TOUS les binaires et objets
.PHONY =tout nettoyer
CC =gcc # compilateur à utiliser
LINKERFLAG =-lm
SRCS :=$(wildcard *.c)
BINS :=$(SRCS:%. c=%)
tous :${BINS}
% :%.o
@echo "Vérification.."
${CC} ${LINKERFLAG} $<-o $@
%.o :%.c
@echo "Objet en cours de création.."
${CC} -c $<
clean :
@echo "Nettoyage..."
rm -rvf *.o ${BINS}
-
Lignes commençant par
#
sont des commentaires. -
Ligne
.PHONY = all clean
définit les fausses ciblesall
etclean
. -
Variable
LINKERFLAG
définit les drapeaux à utiliser avecgcc
dans une recette. -
SRCS := $(wildcard *.c)
:$(wildcard pattern)
est l'une des fonctions pour les noms de fichiers . Dans ce cas, tous les fichiers avec le.c
l'extension sera stockée dans une variableSRCS
. -
BINS := $(SRCS:%.c=%)
:C'est ce qu'on appelle la référence de substitution . Dans ce cas, siSRCS
a des valeurs'foo.c bar.c'
,BINS
aura'foo bar'
. -
Ligne
all: ${BINS}
:La fausse cibleall
appelle les valeurs dans${BINS}
comme cibles individuelles. -
Règle :
% :%.o
@echo "Vérification.."
${CC} ${LINKERFLAG} $< -o $@Prenons un exemple pour comprendre cette règle. Supposons
foo
est l'une des valeurs de${BINS}
. Alors%
correspondra àfoo
(%
peut correspondre à n'importe quel nom de cible). Vous trouverez ci-dessous la règle dans sa forme développée :foo :foo.o
@echo "Vérification.."
gcc -lm foo.o -o fooComme indiqué,
%
est remplacé parfoo
.$<
est remplacé parfoo.o
.$<
est modelé pour correspondre aux prérequis et$@
correspond à la cible. Cette règle sera appelée pour chaque valeur dans${BINS}
-
Règle :
%.o : %.c
@echo "Creating object.."
${CC} -c $<Chaque prérequis de la règle précédente est considéré comme une cible pour cette règle. Vous trouverez ci-dessous la règle dans sa forme développée :
foo.o :foo.c
@echo "Creating object.."
gcc -c foo.c -
Enfin, nous supprimons tous les fichiers binaires et objets dans la cible
clean
.
Ci-dessous se trouve la réécriture du makefile ci-dessus, en supposant qu'il est placé dans le répertoire ayant un seul fichier foo.c:
# Utilisation :
# make # compiler tous les binaires
# make clean # supprimer TOUS les binaires et objets
.PHONY =tout nettoyer
CC =gcc # compilateur à utiliser
LINKERFLAG =-lm
SRCS :=foo.c
BINS :=foo
all :foo
foo:foo.o
@echo "Vérification.."
gcc -lm foo.o -o foo
foo.o :foo.c
@echo "Creating object.."
gcc -c foo.c
clean:
@echo "Cleaning up..."
rm -rvf foo.o foo
Pour plus d'informations sur les makefiles, reportez-vous au manuel GNU Make, qui propose une référence complète et des exemples.
Vous pouvez également lire notre Introduction à GNU Autotools pour savoir comment automatiser la génération d'un makefile pour votre projet de codage.