10 octobre 2018
Résumé. Quand un projet contient de nombreux fichiers sources, il est intéressant de produire les fichiers intermédiaires et les exécutables dans des répertoires séparés des sources.
Dernière mise à jour : 15 novembre 2021.
On construit un projet avec deux exécutables à produire :
main_f
, qui appelle une fonction foo()
main_fb
, qui appelle foo()
et bar()
.Source de main_fb.c
:
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main() {
foo();
bar();return 0;
}
Chaque fonction est compilée séparément. Nous avons donc les fichiers sources :
main_f.c
et main_fb.c
contenant les fonctions main()
des deux exécutables,foo.h
et bar.h
contenant les prototypes,foo.c
et bar.c
avec le code des fonctions.En utilisant les règles par défaut, les dépendances implicites, et la génération automatique des dépendances, on peut écrire un Makefile
simple :
CFLAGS = -MMD -MP
EXECS = main_f main_fb
all: $(EXECS)
main_f: main_f.o foo.o
main_fb: main_fb.o foo.o bar.o
-include $(wildcard *.d)
clean:
$(RM) *~ *.o *.d
mrproper: clean
$(RM) $(EXECS)
dont l’exécution conduit au résultat voulu
make -f Makefile.simple
$ cc -MMD -MP -c -o main_f.o main_f.c
cc -MMD -MP -c -o foo.o foo.c
cc main_f.o foo.o -o main_f
cc -MMD -MP -c -o main_fb.o main_fb.c
cc -MMD -MP -c -o bar.o bar.c
cc main_fb.o foo.o bar.o -o main_fb
Cette solution a l’inconvénient d’envahir le répertoire avec des fichiers de travail :
ls
$ bar.c bar.o foo.h main_fb main_fb.o main_f.o
bar.d foo.c foo.o main_fb.c main_f.c Makefile
bar.h foo.d main_f main_fb.d main_f.d
C’est le problème auquel on essaie de remédier.
L’objectif est de ne pas polluer le répertoire des sources. Pour cela on utilisera deux sous-répertoires
build
pour les fichiers intermédiaires (objets et dépendances) produits pendant le développement du projet,dist
pour les exécutables “à livrer”.Makefile
Le Makefile
commence par quelques définitions
BUILD_DIR = build
DIST_DIR = dist
BUILD_DIR
EXECS = main_f main_fb
OBJS_MAIN_F = main_f.o foo.o
OBJS_MAIN_FB = main_fb.o foo.o bar.o
CFLAGS = -MMD -MP
dist/
all: $(addprefix $(DIST_DIR)/,$(EXECS))
build/
):$(DIST_DIR)/main_f: $(addprefix $(BUILD_DIR)/,$(OBJS_MAIN_F))
$(DIST_DIR)/main_fb: $(addprefix $(BUILD_DIR)/,$(OBJS_MAIN_FB))
dist/
est décrite par une règle “générique”qui est dérivée de la macro classique d’édition des liens, en créant au besoin le répertoire dist/
$(DIST_DIR)/%:
@mkdir -p $(DIST_DIR)
$(LINK.c) -o $@ $^
$(BUILD_DIR)/%.o: %.c
@mkdir -p $(BUILD_DIR)
$(COMPILE.c) -o $@ $^
-include $(BUILD_DIR)/*.d
clean:
$(RM) *~
$(RM) -r (BUILD_DIR)
mrproper: clean
$(RM) -r $(DIST_DIR)
make
produit bien les fichiers voulusmake
$ cc -MMD -MP -c -o build/main_f.o main_f.c
cc -MMD -MP -c -o build/foo.o foo.c
mkdir -p dist
cc -MMD -MP -o dist/main_f build/main_f.o build/foo.o
cc -MMD -MP -c -o build/main_fb.o main_fb.c
cc -MMD -MP -c -o build/bar.o bar.c
mkdir -p dist
cc -MMD -MP -o dist/main_fb build/main_fb.o build/foo.o build/bar.o
LANG= tree -c
$ .
|-- foo.c
|-- foo.h
|-- bar.h
|-- main_f.c
|-- main_fb.c
|-- bar.c
|-- Makefile
|-- build
| |-- bar.d
| |-- bar.o
| |-- foo.d
| |-- foo.o
| |-- main_f.d
| |-- main_f.o
| |-- main_fb.d
| `-- main_fb.o
`-- dist
|-- main_f
`-- main_fb
-MP
dans les CFLAGS
:Fichier build/main_fb.d
:
build/main_fb.o: main_fb.c foo.h bar.h
foo.h:
bar.h:
Fichier build/foo.d
build/foo.o: foo.c foo.h
foo.h:
Le Makefile
ainsi construit ne contient que quelques lignes spécifiques à ce projet :
EXECS = main_f main_fb
...main_f : main_f.o foo.o
main_fb : main_fb.o foo.o bar.o
...$(DIST_DIR)/main_f : $(addprefix $(BUILD_DIR)/,$(OBJS_MAIN_F))
$(DIST_DIR)/main_fb : $(addprefix $(BUILD_DIR)/,$(OBJS_MAIN_FB))
Pour des projets qui ont une structure similaire, c’est-à-dire
il est facile de l’adapter.