GNU/Linux >> Tutoriels Linux >  >> Linux

Qu'est-ce qu'un Makefile et comment ça marche ?

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érequis
recette

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 *.txt

Avant 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 *.txt

Le make devrait appeler say_hello et generate :

$ make
Hello World
Création de fichiers texte vides...
touch file-{1..10}.txt

C'est une bonne pratique de ne pas appeler clean dans all ou le mettre comme première cible. clean doit être appelé manuellement lorsque le nettoyage est nécessaire comme premier argument de make :

$ make clean
Nettoyage...
rm *.txt

Maintenant 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 cibles all et clean .

  • Variable LINKERFLAG définit les drapeaux à utiliser avec gcc 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 variable SRCS .

  • BINS := $(SRCS:%.c=%) :C'est ce qu'on appelle la référence de substitution . Dans ce cas, si SRCS a des valeurs 'foo.c bar.c' , BINS aura 'foo bar' .

  • Ligne all: ${BINS}  :La fausse cible all 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 foo

    Comme indiqué, % est remplacé par foo . $< est remplacé par foo.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.


Linux
  1. Qu'est-ce que NGINX ? Comment ça marche?

  2. Qu'est-ce qu'un serveur Web et comment fonctionne un serveur Web ?

  3. Comment fonctionne Awk '!a[$0]++' ?

  4. Comment fonctionne le Sticky Bit ?

  5. Le but de .bashrc et comment ça marche ?

Commande de fichier Linux :que fait-elle et comment l'utiliser

Qu'est-ce que Docker ? Comment ça marche?

Qu'est-ce que la commande source sous Linux et comment ça marche ?

Qu'est-ce que la commande Grep sous Linux ? Pourquoi est-il utilisé et comment fonctionne-t-il ?

Comment fonctionne la mémoire d'échange sous Linux ?

Comment fonctionne un équilibreur de charge ? Qu'est-ce que l'équilibrage de charge ?