La fonction $(wildcard \*.cpp) répertorie uniquement les fichiers .cpp du répertoire courant, sans explorer les sous-répertoires.
patsubst permet de remplacer des motifs. Par exemple, la commande suivante convertit une liste de fichiers .c en fichiers .o :
sources := $(wildcard \*.c)
objets := $(patsubst %.c,%.o,$(sources))
executable : $(objets)
cc -o executable $(objets)
Le système de déduction automatique des dépendances de make ne fonctionne que dans le répertoire courant. Si les fichiers d'en-tête sont placés dans un répertoire distinct, la compilation peut échouer.
Pour résoudre ce problème, on peut spécifier des chemins d'inclusion et utiliser vpath pour aider make à localiser les fichiers sources :
CC = g++
LISTE_OBJ = main.o utils.o gestion.o
CPPFLAGS = -I./include
vpath %.h include
vpath %.c src
application : $(LISTE_OBJ)
$(CC) -o $@ $(LISTE_OBJ)
%.o : %.c
$(CC) $(CPPFLAGS) -c $<
Pour gérer plusieurs cibles avec un motif commun, on peut utiliser des fonctions Makefile comme subst :
sortie_grande sortie_petite : donnees.g
traitement donnees.g -$(subst sortie,,$@) > $@
Cette règle génère deux cibles distinctes en manipulant le nom de la cible avec subst.
Les variables automatiques sont essentielles :
$@représente la cible courante.$<représente la première dépendance.$^représente toutes les dépendances.
Il est recommandé d'utilisre $< pour référencer la première dépendance dans les règles.
Exemple complet : Bibliothèque partagée pour le traitement vidéo
# Configuration du compilateur et des flags
CXX = g++
CXXFLAGS = -m64 -std=c++11 -fPIC -shared
CUDA_DIR = /usr/local/cuda
NVCC = $(CUDA_DIR)/bin/nvcc
# Cible finale
BIBLIOTHEQUE = libtraitement_video.so
# Répertoires
REPERTOIRE_BUILD = ./build
REPERTOIRE_INCLUDE = -I/usr/include -I$(CUDA_DIR)/include
# Sources et objets
FICHIERS_CPP = decodage_nv.cpp export.cpp gestion_gb.cpp
FICHIERS_CU = espace_couleur.cu
OBJETS_CPP = $(FICHIERS_CPP:.cpp=.o)
OBJETS_CU = $(FICHIERS_CU:.cu=.o)
# Bibliothèques externes
LIBRAIRIES = -lopencv_core -lnvcuvid -lcuda -lpthread -lboost_filesystem
# Construction de la bibliothèque
$(REPERTOIRE_BUILD)/$(BIBLIOTHEQUE): $(OBJETS_CPP) $(OBJETS_CU) | $(REPERTOIRE_BUILD)
$(CXX) $(CXXFLAGS) -o $@ $^ $(REPERTOIRE_INCLUDE) $(LIBRAIRIES)
$(REPERTOIRE_BUILD):
mkdir -p $@
# Compilation des sources C++
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(REPERTOIRE_INCLUDE) -c $< -o $@
# Compilation des sources CUDA
%.o: %.cu
$(NVCC) -m64 --compiler-options "-fPIC" $(REPERTOIRE_INCLUDE) -c $< -o $@
# Nettoyage
clean:
rm -f *.o
rm -rf $(REPERTOIRE_BUILD)
Exemple avec FFmpeg et CUDA
# Configuration
COMPILATEUR = g++
CUDA_DIR = /usr/local/cuda
NVCC = $(CUDA_DIR)/bin/nvcc
# Options de compilation
FLAGS_COMPILATION = -m64 -std=c++11 -fPIC
FLAGS_LIAISON = -shared
# Cible
BIBLIOTHEQUE_FINALE = libdecodeur_ffmpeg.so
# Chemins
CHEMINS_INCLUDE = -I/home/projet/include -I$(CUDA_DIR)/include
CHEMINS_LIBRAIRIES = -L/usr/lib -L/usr/local/lib -L$(CUDA_DIR)/lib64
# Dépendances externes
LIBRAIRIES_EXTERNES = -lavcodec -lavformat -lavutil -lnvcuvid -lcuda -ldl
# Liste des objets
FICHIERS_SOURCES = flux_ffmpeg.cpp decodage_nv.cpp espace_couleur.cu export.cpp
OBJETS = $(FICHIERS_SOURCES:.cpp=.o)
OBJETS := $(OBJETS:.cu=.o)
# Construction
REPERTOIRE_BUILD = ./build
$(REPERTOIRE_BUILD)/$(BIBLIOTHEQUE_FINALE): $(addprefix $(REPERTOIRE_BUILD)/, $(OBJETS))
$(COMPILATEUR) $(FLAGS_COMPILATION) $(FLAGS_LIAISON) -o $@ $+ $(CHEMINS_LIBRAIRIES) $(LIBRAIRIES_EXTERNES)
# Création du répertoire de build
$(REPERTOIRE_BUILD):
mkdir -p $@
# Règles génériques pour .cpp et .cu
$(REPERTOIRE_BUILD)/%.o: %.cpp | $(REPERTOIRE_BUILD)
$(COMPILATEUR) $(FLAGS_COMPILATION) $(CHEMINS_INCLUDE) -c $< -o $@
$(REPERTOIRE_BUILD)/%.o: %.cu | $(REPERTOIRE_BUILD)
$(NVCC) $(FLAGS_COMPILATION) $(CHEMINS_INCLUDE) -c $< -o $@
# Nettoyage
distclean:
rm -rf $(REPERTOIRE_BUILD)
Différences entre les opérateurs d'affectation :
=: Affectation récursive. La valeur est évaluée à l'usage.:=: Affectation simple. La valeur est évaluée immédiatement.
Exemple illustrant la différence :
# Avec = (affectation récursive)
X = premier
Y = $(X) deuxieme
X = final
# Y vaut "final deuxieme"
# Avec := (affectation simple)
X := premier
Y := $(X) deuxieme
X := final
# Y vaut "premier deuxieme"