Skip to content

Instantly share code, notes, and snippets.

@maelvls
Last active January 23, 2023 12:02
Show Gist options
  • Save maelvls/dd321db21fb33ed7b57a to your computer and use it in GitHub Desktop.
Save maelvls/dd321db21fb33ed7b57a to your computer and use it in GitHub Desktop.
Exemple de Makefile où j'ai noté tout ce que j'avais appris sur Makefile/make
#
# ANCIEN MAKEFILE Inutile maintenant ; on utilisera ./configure; make
#
# makefile
# Mael Valais, 2014-04-07 15:23
#
# Makefile permettant la compilation du projet climso-auto
# NOTE: les # */ sont là à cause de Xcode
#
# A faire :
# - comprendre VPATH (les espaces ? pas d'espaces?) (OK)
# - essayer de comprendre les .d et -MF (PRESQUE OK)
# - comprendre ce qu'est un CFLAGS, CXXFLAGS (OK -> -g)
#
# Déroulement d'une compilation :
# - pré-compilation puis compilation en .o grâce à CC (-I pour les includes)
# - linkage des .o grâce à LD (-L pour les répertoires des librairies, -l pour les archives .a)
# -lm cherche donc libm.a ou libm.dylib
# -L/usr/local/lib permet à LD de trouver des trucs qui ne sont pas dans le PATH_LIB_JECONNAISPAS
# par défaut
# Il faut tester si BINDIR et OBJDIR existent
# Si pas en std=c99, alors on ne peut pas déclarer une variable dans un for(int...)
# NOTES GÉNÉRALES SUR LE MAKEFILE ET MAKE
# Sur ça :
# all:
# cd dir
# pwd
# `pwd` retournera exactement la même chose qu'avant le `cd dir`, pour la bonne raison
# que `make` utilise, pour chaque nouvelle ligne, un nouveau "sous-shell".
# `.PHONY all`: il s'agit de s'assurer que le target "all" ne soit pas cherché comme
# étant un fichier, car "all" n'est qu'un target artificiel, une sorte d'alias
# ATTENTION: l'assignation des variables CFLAGS, CXXFLAGS, CC, CXX & cie doit se faire
# sans écraser celles qui ont (peut-être) été données par l'utilisateur en faisant par exemple :
# make CPPFLAGS=-I. CXXFLAGS=-g LDFLAGS=-L. CXX=gcc-5
# Pour faire cela, il y a plusieurs types d'assignations :
# CPPFLAGS = value -> remplace variable existante; la valeur est "expanded" à l'appel
# CPPFLAGS := value -> remplace variable existante; la valeur est "expanded" à la déclaration
# CPPFLAGS := value ->
# CPPFLAGS += value -> ajoute à la suite du CPPFLAGS existant
# CPPFLAGS ?= value -> n'assigne QUE si CPPFLAGS n'était pas déjà assignée
# NOTE: Je parle de "expension" ou de variables "expended" pour parler du moment où elles sont
# remplacées par leurs valeurs réelles.
# Variables shell locales dans une commande dans un makefile:
# Les variables $var sont d'abord évaluées par `make`. Donc si j'écris une target avec une commande
# shell qui contient une variable locale, elle sera remplacée par make avant que le shell ne puisse
# l'évaluer. Il faut 'échapper' les variables avec deux dollars: $$var
# Exemple:
# src/version.ml:
# @V=`git describe --tags`; echo $$V
#
# Variables diverses
#
# Dossier des Sources (.c, .cpp,.h)
SRCDIR=Sources
# Dossier des librairies (.c, .cpp, .h)
SRCLIBDIR=Libraries
FILTER=main camera_sbig_lib
# Dossier des objets .o
OBJDIR=Builds
# Dossier des exécutables .out
BINDIR=.
# Nom de l'exécutable en sortie
BIN=a.out
# Librairies EXTERNES (exple -L/usr/local/lib) utilisées par le linker
# Par défaut, c'est dans LD_LIBRARY_PATH (mais pas sur MacOSX je crois)
EXTERN_INCLUDES= -I/usr/local/include
EXTERN_LIBS_DIR= -L/usr/local/lib
EXTERN_LIBS=-lm -ltiff
#
# Les variables des règles implicites et explicites
#
# Utiliser les préprocesseurs/compilateur/linker indépendament:
# - `gcc -E` permet de lancer le préprocesseur seulement
# - `gcc -c` permet de lancer préproc+parser+type check
# +generation/optimisation code => fichier assembleur "objet" .o
#
# Variable de préproceseur
CPPFLAGS= # Exemple: -DDEBUG=1, -I/usr/local/include
# Variables de compilation (.c, .cpp)
CFLAGS= # Les .c (avec gcc) Exemple: -g, -O3
CXXFLAGS= # Les .cpp Exemple: -g, -O0
CPATH=/usr/local/include/:/usr/include/ # Équivalent PATH pour chemins vers headers/includes
# NOTE: C_INCLUDE_PATH, CPLUS_INCLUDE_PATH
# sont équivalents mais spécifiques pour
# chaque langage
# Variables de choix du compilateur
CC=clang # Compilateur .c
CXX=clang++ # Compilateur .cpp
# Variable de linkage (ld/llvm)
LDFLAGS=$(EXTERN_LIBS) # Exemple: -liconv, -lm, -L/usr/local/lib
LD_LIBRARY_PATH=/usr/local/lib/:/usr/lib/ # Équivalent PATH pour chemins vers librairies
RM=rm -rf $(OBJDIR)/*.o $(BINDIR)/$(BIN) # */
# EXPLICATION :
# CC et CFLAGS sont des variables qui conditionnent les règles implicites ;
# Par exemple, si une dépendance foo.o ne trouve aucune règle exlicite
# "foo.o: foo.c", alors make fait appel à la règle implicite
# foo.o: foo.c
# $(CC) -c (la compilation)
#
# Définition des différents main.c liés à chaque règle (arduino, all...)
# NOTE: "all", "arduino"... ont besoin d'un main.c
#
MAIN_TEST_ALL=main_global.c
MAIN_TEST_ARDUINO=main_arduino.c
MAIN_TEST_CAMERA=main_camera.c
MAIN_TEST_POSITION=main_position.c
#
# Fonctions
#
# Supprimer les blancs
# $(call nospaces,a b c d ) donne abcd
space:=$(subst , ,)
nospaces=$(subst $(space),,$(1))
# NOTE : Wildcard permet de "développer" le contenu avec * par exemple (comme en shell)
# Wildcard récursif. Appel : $(call rwildcard, , *.c) pour le rep. courant
rwildcard=$(foreach d,$(wildcard $(1)*),$(call rwildcard,$d/,$(2)) $(filter $(subst *,%,$(2)),$d))
# Supprime tous les termes de CHAINE contenant TERME quelque part
# $(call filter_out_multiple,TERME,CHAINE)
recursive_rep=$(sort $(dir $(call rwildcard,$(1),*)))
filter_out = $(foreach v,$(2),$(if $(findstring $(1),$(v)),,$(v)))
# Supprime tous les termes de CHAINE où existent le TERME1 ou TERME2...
# $(call filter_out_multiple,TERME1 TERME2 (...),CHAINE)
filter_out_multiple = $(foreach v,$(2), $(if $(call nospaces,$(foreach p,$(1),$(if $(findstring $(p),$(v)),n,))),,$(v)))
#
# Préparation du VPATH qui permettra à make de chercher les sources aux bons endroits
# lors de la phase de build
#
SRCLIBDIR := $(call recursive_rep,$(SRCLIBDIR))
SRCDIR := $(call recursive_rep,$(SRCDIR))
VPATH := $(SRCDIR) $(SRCLIBDIR) # */ # Où trouver les SOURCES (libs, .c...)
#
# Construction de la liste des objets à build des les sources
#
LIST_OBJ := $(subst .c,.o,$(call rwildcard,$(SRCDIR),*.c))
LIST_OBJ += $(subst .cpp,.o,$(call rwildcard,$(SRCDIR),*.cpp))
LIST_OBJ := $(call filter_out_multiple,$(FILTER),$(LIST_OBJ)) # On filtre (main.c..)
LIST_OBJ := $(addprefix $(OBJDIR)/,$(notdir $(LIST_OBJ))) # On enlève les repertoires
#
# Construction de la liste des objets à build des les librairies
#
LIST_OBJ_LIB := $(subst .c,.o,$(call rwildcard,$(SRCLIBDIR),*.c))
LIST_OBJ_LIB += $(subst .cpp,.o,$(call rwildcard,$(SRCLIBDIR),*.cpp))
LIST_OBJ_LIB := $(call filter_out_multiple,$(FILTER),$(LIST_OBJ_LIB)) # On filtre (main.c..)
LIST_OBJ_LIB := $(addprefix $(OBJDIR)/,$(notdir $(LIST_OBJ_LIB))) # On enlève les repertoires
#
# Construction des includes (les headers)
#
#USER_INCLUDES := $(SRCDIR) $(SRCLIBDIR)
#USER_INCLUDES := $(addprefix -I,$(USER_INCLUDES))
USER_INCLUDES := $(addprefix -I,$(sort $(dir $(call rwildcard, ,*.h))))
#
# Build (sources, librairies)
#
$(OBJDIR)/%.o: %.c $(OBJDIR)
$(CC) $(USER_INCLUDES) $(EXTERN_INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
$(OBJDIR)/%.o: %.cpp $(OBJDIR)
$(CXX) $(USER_INCLUDES) $(EXTERN_INCLUDES) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
arduino: $(OBJDIR)/$(MAIN_TEST_ARDUINO:.c=.o) $(addprefix $(OBJDIR),cmd_arduino.o)
$(CXX) $(EXTERN_LIBS_DIR) $(LDFLAGS) $^ -o $(BINDIR)/$(BIN)
camera: $(OBJDIR)/$(MAIN_TEST_CAMERA:.c=.o) $(LIST_OBJ) $(LIST_OBJ_LIB)
$(CXX) $(EXTERN_LIBS_DIR) $(LDFLAGS) $^ -o $(BINDIR)/$(BIN)
position: $(OBJDIR)/$(MAIN_TEST_POSITION:.c=.o) $(LIST_OBJ) $(LIST_OBJ_LIB)
$(CXX) $(EXTERN_LIBS_DIR) $(LDFLAGS) $^ -o $(BINDIR)/$(BIN)
all: $(OBJDIR)/$(MAIN_TEST_ALL:.c=.o) $(LIST_OBJ) $(LIST_OBJ_LIB) $(BINDIR)
$(CXX) $(EXTERN_LIBS_DIR) $(LDFLAGS) $^ -o $(BINDIR)/$(BIN)
clean:
$(RM)
$(BINDIR):
$(OBJDIR):
@echo "--------- Creation du dossier $@ ---------"
@mkdir $@
essai:
@echo "---------------ESSAI----------------"
@echo "Liste des objets : $(LIST_OBJ)"
@echo "Liste des objets librairie : $(LIST_OBJ_LIB)"
@echo "USER_INCLUDES : $(USER_INCLUDES)"
@echo "EXTERN_INCLUDES : $(EXTERN_INCLUDES)"
@echo "EXTERN_LIBS_DIR : $(EXTERN_LIBS_DIR)"
@echo "EXTERN_LIBS : $(EXTERN_LIBS)"
@echo "SRCLIBDIR récursif : $(SRCLIBDIR)"
# On peut utiliser VPATH pour indiquer les chemins des dépendances :
# VPATH=$(SRCDIR):$(LIBDIR)...
# Ou utiliser vpath
# ATTENTION : apparement MAKE fait un cc -c -o cmd_arduino.o Sources/cmd_arduino.c
# tout seul de son côté à cause du VPATH ?
# Les fichiers de dépendance .d (cc -E appelle le pré-processeur)
# $(CC) -E -MM $^ -MF leFichierDesDependances.d
# Exemple d'appel :
# foo.o: foo.c
# cc -E -MM $^ -MF leFichierDesDependances.d ($^ == foo.c)
# donnera dans leFichierDesDependances.d :
# foo.o: foo.c foo.h bar.c bar.h
#
### Memo des variables automatiques ###
# $@ Le nom de la cible
# $< Le nom de la première dépendance
# $^ La liste des dépendances
# $? La liste des dépendances plus récentes que la cible
# $* Le nom du fichier sans suffixe
### Memo de la suite d'utilitaires liés à gcc (binutils)
# ld Pour linker
# ar Pour ouvrir les archives des .so
# nm Pour lister les symboles d'un .o ou dyll
### Prépend des commandes, par exemple `@echo`:
# @ suppresses the normal 'echo' of the command that is executed.
# - means ignore the exit status of the command that is executed (normally, a non-zero exit status would stop that part of the build).
# + means 'execute this command under make -n' (when commands are not normally executed).
#
# vim:ft=make
#
@maelvls
Copy link
Author

maelvls commented Oct 12, 2014

Pour lancer une commande shell :

$(shell ls)

⚠️ ATTENTION, chaque ligne lance un fork du processus actuel ; donc on ne peut pas passer des variables d'une ligne à une autre !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment