Conseils pour l'écriture de fichiers Makefile

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"

Étiquettes: Makefile GCC NVCC C++ CUDA

Publié le 12 juin à 03h00