Aller au contenu

Make

La commande make est un utilitaire permettant de décrire les dépendances d’une application, de compiler les fichiers sources et de linker les fichiers objets nécessaires à sa génération

make

La description de ces dépendances se fait par l’intermédiaire d’un fichier texte

exec: main.o libmymathlib.a
    gcc main.o -lmymathlib -L. -o exec

main.o: main.c mymathlib.h
    gcc -Wall -Wextra -g -c -O0 -std=gnu11 -o main.o main.c

Le nom de défaut est Makefile, mais il possible de le nommer différemment

make -f Makefile

Principe et syntaxe

Un makefile est constitué d’une suite de règles

cible: dépendances
    commande
  • cible :
    • nom du fichier (exécutable, objet … généré par la commande
    • ou action à effectuer, telle que all, clean ou install
  • dépendances : fichiers utilisés comme fichiers sources pour la génération de la cible
  • commande :
    • recette / action utilisée pour générer la cible
    • exécutée si au moins une des dépendances plus récentes que la cible
    • attention : l’indentation de la commande doit être faite avec un caractère Tab et non avec des espaces.

Variables

L’utilisation de variables permet de simplifier grandement l’écriture des Makefile

EXEC=exec
CC=gcc
CFLAGS=-Wall -Wextra -g -c -O0 -std=gnu11
LDFLAGS=-lmymathlib -L.

Exemple :

$(EXEC): main.o libmymathlib.a
    $(CC) main.o $(LDFLAGS) -o $(EXEC)

main.o: main.c mymathlib.h
    $(CC) $(CFLAGS) -o main.o main.c

Plusieurs cibles

Il est possible de spécifier plusieurs cibles dans un même Makefile

EXEC=exec
CC=gcc
CFLAGS=-Wall -Wextra -g -c -O0 -std=gnu11
LDFLAGS=-lmymathlib -L.

all: $(EXEC)

$(EXEC): main.o libmymathlib.a
    $(CC) main.o $(LDFLAGS) -o $(EXEC)

main.o: main.c mymathlib.h
    $(CC) $(CFLAGS) -o main.o main.c

clean:
    rm -Rf $(EXEC) *.o

.PHONY: all clean
  • all : Pour générer l’application (fichier de sortie), il suffit de taper make ou make all. Si aucune cible n’est spécifiée, c’est la première cible qui se trouve dans le Makefile qui sera généré.
  • clean : Pour effacer le fichier de sortie et tous les fichiers de dépendances, il suffit de taper make clean
  • .PHONY :
    • Pour indiquer que les cibles all et clean sont factices
    • Pour forcer leur reconstruction

Règles suffixes

Les règles suffixes permettent d’éviter de spécifier pour chaque fichier (cible) les commandes à effectuer :

SRCS=main.c
OBJS=$(SRCS:.c=.o)
EXEC=exec
CC=...

all: $(EXEC)

$(EXEC): $(OBJS) libmymathlib.a
    $(CC) $(LDFLAGS) $^ -o $@

%.o: %.c
    $(CC) $(CFLAGS) $< -o $@

clean:
    ...
  • SRCS=main.c : Liste des fichiers sources
  • OBJS=$(SRCS:.c=.o) : Expression pour remplacer l’extension .c des noms de fichiers sources contenus dans la variable SRCS par l’extension .o des fichiers cibles
  • %.o: %.c : Règle suffixe pour la compilation des fichiers sources .c en fichiers cibles .o
  • Explication des variables :
    • $@ : nom de la cible
    • $< : nom du fichier source
    • $* : nom du fichier sans le suffixe
    • $^ : nom de toutes les dépendances
    • $? : noms de toutes les dépendances plus récentes que la cible

Génération des dépendances

Lors de la génération d’applications développées en C il est essentiel de garantir que celles-ci soient correctement générées et que toutes les modifications soient bien prises en compte

Le compilateur GNU permet de générer pour chaque fichier source un fichier de ses dépendances, lequel pourra ensuite être utilisé dans le Makefile

...
CFLAGS=-Wall -Wextra -g -c -O0 -std=gnu11 -MD
...
all: $(EXEC)
...
-include $(OBJS:.o=.d)

clean:
    rm -Rf $(EXEC) $(OBJS) $(OBJS:.o=.d)
  • -MD: Option du compilateur pour forcer la génération d’un fichier de dépendance
  • include $(OBJS:.o=.d): Instruction pour inclure tous les fichiers de dépendances
  • Ne pas oublier d’effacer les fichiers de dépendances…

Conditions

Des directives permettant d’exécuter conditionnellement une partie du Makefile, ceci en fonction d’une variable et de sa valeur

ifeq ($(VARIABLE), value)
    ## if true do that
else
    ## if false do that
endif

La variable peut être contenue dans le Makefile, mais il est également possible de la spécifier lors de l’appel du Makefile

make VARIABLE=value target

Cette technique permet de générer un logiciel pour différentes plateformes (machine hôte, cible …) ou en différentes versions (release, debug, …)

Sous-Makefile

Pour simplifier la génération de grands projets, il est possible de créer des sous-Makefile

La variable $(MAKE) fournit l’outil nécessaire à l’appel de sous-Makefile

target:
    $(MAKE) -C directory_name target

Le mot clef export permet de passer les variables à des sous-Makefile

export CFLAGS

Alternatives à make

Make est un outil qui date de 1977. Il est très puissant, mais il est aussi très difficile à maîtriser. Aujourd’hui, de nombreuses alternatives plus modernes existent :

  • CMake : Outil très populaire pour le C et le C++.
  • SCons : Outil écrit en Python.
  • Ninja : Ninja est plutôt un back end pour CMake ou SCons.
  • Meson : Autre outil écrit en Python.
  • Bazel : Outil développé par Google, plutôt adapté pour les projets de grande envergure.