Faire une feuille de chaleur
Aperçu général
GNU Make est un outil d'automatisation de construction qui construit automatiquement des programmes exécutables et des bibliothèques à partir du code source en lisant des fichiers appelés Makefiles qui précisent comment dériver le programme cible.
Concepts de base
Structure du fichier de marque
target: dependencies
command
command
Termes clés
Target # File to be created or action to be performed
Dependency # Files that target depends on
Recipe # Commands to create target
Rule # Target + dependencies + recipe
Variable # Named value that can be reused
```_
## Fichier Make de base
### Exemple simple
```makefile
# Simple C program build
program: main.o utils.o
gcc -o program main.o utils.o
main.o: main.c
gcc -c main.c
utils.o: utils.c
gcc -c utils.c
clean:
rm -f *.o program
```_
### Avec variables
```makefile
CC = gcc
CFLAGS = -Wall -g
OBJECTS = main.o utils.o
TARGET = program
$(TARGET): $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)
main.o: main.c
$(CC) $(CFLAGS) -c main.c
utils.o: utils.c
$(CC) $(CFLAGS) -c utils.c
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: clean
Variables
Définition variable
# Simple assignment
CC = gcc
CFLAGS = -Wall -g
# Recursive assignment (evaluated when used)
SOURCES = $(wildcard *.c)
# Simple assignment (evaluated immediately)
OBJECTS := $(SOURCES:.c=.o)
# Conditional assignment (only if not already defined)
CC ?= gcc
# Append to variable
CFLAGS += -O2
Variables intégrées
# Automatic variables
$@ # Target name
# Make Cheatsheet
## Overview
GNU Make is a build automation tool that automatically builds executable programs and libraries from source code by reading files called Makefiles which specify how to derive the target program.
## Basic Concepts
### Makefile Structure
```_fichier de make
cible: dépendances
commande
commande
Key Terms
Cible # Fichier à créer ou action à exécuter
Dépendance # Fichiers dont la cible dépend
Recette # Commandes pour créer une cible
Règle # Cible + dépendances + recette
Variable # Valeur nommée pouvant être réutilisée
Basic Makefile
Simple Example
# Construction de programme C simple
programme: main.o utils.o
gcc -o programme main.o utils. o
main.o: main.c
Gcc -c principal. c
utils.o: utils.c
- C'est des articles. c
nettoyer:
rm -f *.o programme
With Variables
```fichier de make CC = gcc CFLAGS = -Taille -g OBJECTIFS = main.o utils.o TARGET = programme
$(TARGET): $(OBJECTIFS) $(CC) -o $(TARGET) $(OBJETS)
main.o: main.c $(CC) $(CFLAGS) -c principal. c
utils.o: utils.c $(CC) $(CFLAGS) -c utils. c
nettoyer: rm -f $(OBJECTS) $(TARGET)
. PHYSIQUE: propre
## Variables
### Variable Definition
```fichier de make
# Attribution simple
CC = gcc
CFLAGS = -Taille -g
# Affectation récursive (évaluée en cas d'utilisation)
SOURCES = $(carte blanche *.c)
# Affectation simple (évaluée immédiatement)
OBJECTIFS := $(SOURCES:.c=.o)
# Affectation conditionnelle (seulement si elle n'est pas déjà définie)
CC ?= gcc
# Ajouter à la variable
CLAGS += -O2
Built-in Variables
`< # First dependency $^ # All dependencies $? # Dependencies newer than target $* # Stem of pattern rule match
Example usage
%.o: %.c $(CC) $(CFLAGS) -c $< -o $@
### Variables d'environnement
```makefile
# Use environment variables
HOME_DIR = $(HOME)
PATH_VAR = $(PATH)
# Override environment variables
export CC = gcc
export CFLAGS = -Wall
Règles de configuration
Modèles de base
# Pattern rule for C files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pattern rule for C++ files
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# Multiple patterns
%.o: %.c %.h
$(CC) $(CFLAGS) -c $< -o $@
Modèles avancés
# Static pattern rule
$(OBJECTS): %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Pattern with subdirectories
build/%.o: src/%.c
@mkdir -p build
$(CC) $(CFLAGS) -c $< -o $@
Fonctions
Fonctions de chaîne
# Substitution
SOURCES = main.c utils.c
OBJECTS = $(SOURCES:.c=.o)
# or
OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
# Word functions
FIRST_WORD = $(firstword $(SOURCES))
WORD_COUNT = $(words $(SOURCES))
NTH_WORD = $(word 2,$(SOURCES))
# Filter functions
C_FILES = $(filter %.c,$(SOURCES))
NOT_MAIN = $(filter-out main.c,$(SOURCES))
Fonctions de fichiers
# Wildcard
SOURCES = $(wildcard src/*.c)
HEADERS = $(wildcard include/*.h)
# Directory functions
SRC_DIRS = $(dir $(SOURCES))
BASE_NAMES = $(notdir $(SOURCES))
BASENAMES = $(basename $(SOURCES))
SUFFIXES = $(suffix $(SOURCES))
# Path manipulation
ABS_PATH = $(abspath relative/path)
REAL_PATH = $(realpath symlink/path)
Fonctions conditionnelles
# If function
RESULT = $(if $(DEBUG),debug,release)
# Or function
COMPILER = $(or $(CC),gcc)
# And function
VALID = $(and $(CC),$(SOURCES))
Déclarations conditionnelles
ideq/ifneq
ifeq ($(DEBUG),1)
CFLAGS += -g -DDEBUG
else
CFLAGS += -O2 -DNDEBUG
endif
ifneq ($(OS),Windows_NT)
CFLAGS += -fPIC
endif
ifdef/ifndef
ifdef DEBUG
CFLAGS += -g
endif
ifndef CC
CC = gcc
endif
Affectation conditionnelle
# Set if not already defined
CC ?= gcc
# Set based on condition
CFLAGS = $(if $(DEBUG),-g -O0,-O2)
Caractéristiques avancées
Inclure les fichiers
# Include other makefiles
include config.mk
include $(wildcard *.d)
# Silent include (no error if file doesn't exist)
-include optional.mk
Génération de dépendance
# Automatic dependency generation
DEPDIR := .deps
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
%.o: %.c $(DEPDIR)/%.d|$(DEPDIR)
$(CC) $(DEPFLAGS) $(CFLAGS) -c $< -o $@
$(DEPDIR):
@mkdir -p $@
DEPFILES := $(SOURCES:%.c=$(DEPDIR)/%.d)
$(DEPFILES):
include $(wildcard $(DEPFILES))
Commande seulement les conditions préalables
# Create directory before building objects
$(OBJECTS):|$(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
Objectifs fictifs
Cibles de contrefaçon communes
.PHONY: all clean install test help
all: $(TARGET)
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/bin/
test: $(TARGET)
./$(TARGET) --test
help:
@echo "Available targets:"
@echo " all - Build the program"
@echo " clean - Remove build files"
@echo " install - Install the program"
@echo " test - Run tests"
Constructions multi-cibles
Programmes multiples
PROGRAMS = client server
COMMON_OBJS = utils.o network.o
all: $(PROGRAMS)
client: client.o $(COMMON_OBJS)
$(CC) -o $@ $^
server: server.o $(COMMON_OBJS)
$(CC) -o $@ $^
clean:
rm -f *.o $(PROGRAMS)
.PHONY: all clean
Bâtiment de la bibliothèque
LIBNAME = libutils
STATIC_LIB = $(LIBNAME).a
SHARED_LIB = $(LIBNAME).so
LIB_OBJS = utils.o network.o
all: $(STATIC_LIB) $(SHARED_LIB)
$(STATIC_LIB): $(LIB_OBJS)
ar rcs $@ $^
$(SHARED_LIB): $(LIB_OBJS)
$(CC) -shared -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -fPIC -c $< -o $@
Utilisation de la ligne de commande
Commandes de base
# Build default target
make
# Build specific target
make clean
make install
# Build with variables
make DEBUG=1
make CC=clang
# Parallel builds
make -j4
make -j$(nproc)
# Dry run (show commands without executing)
make -n
# Silent mode
make -s
# Keep going on errors
make -k
Déboguement
# Print variables
make -p
# Debug mode
make -d
# Print database
make --print-data-base
# Trace execution
make --trace
Exemples complexes
C++ Projet
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -g
LDFLAGS = -lpthread
SRCDIR = src
OBJDIR = obj
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
OBJECTS = $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
TARGET = myprogram
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp|$(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(OBJDIR):
mkdir -p $(OBJDIR)
clean:
rm -rf $(OBJDIR) $(TARGET)
# Dependency generation
-include $(OBJECTS:.o=.d)
$(OBJDIR)/%.d: $(SRCDIR)/%.cpp|$(OBJDIR)
$(CXX) -MM -MT $(@:.d=.o) $< >`` $@
Projet pluridirectionnel
# Project structure
PROJECT_ROOT = .
SRC_DIRS = src src/utils src/network
INC_DIRS = include
OBJ_DIR = build
BIN_DIR = bin
# Find all source files
SOURCES = $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
OBJECTS = $(SOURCES:%.c=$(OBJ_DIR)/%.o)
TARGET = $(BIN_DIR)/myapp
# Compiler settings
CC = gcc
CFLAGS = -Wall -g $(addprefix -I,$(INC_DIRS))
LDFLAGS = -lm
.PHONY: all clean dirs
all: dirs $(TARGET)
dirs:
@mkdir -p $(OBJ_DIR) $(BIN_DIR)
@mkdir -p $(foreach dir,$(SRC_DIRS),$(OBJ_DIR)/$(dir))
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
Meilleures pratiques
Organisation de Makefile
# Variables at the top
CC = gcc
CFLAGS = -Wall -g
TARGET = myprogram
# Default target first
all: $(TARGET)
# Pattern rules
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Specific rules
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@
# Phony targets at the end
.PHONY: all clean install
clean:
rm -f *.o $(TARGET)
Gestion des erreurs
# Check for required tools
ifeq ($(shell which $(CC)),)
$(error Compiler $(CC) not found)
endif
# Validate variables
ifndef SOURCES
$(error SOURCES variable is not defined)
endif
# Conditional compilation
ifeq ($(shell pkg-config --exists gtk+-3.0; echo $?),0)
CFLAGS += $(shell pkg-config --cflags gtk+-3.0)
LDFLAGS += $(shell pkg-config --libs gtk+-3.0)
else
$(warning GTK+ 3.0 not found, building without GUI)
endif
Conseils de performance
# Use order-only prerequisites for directories
$(OBJECTS):|$(OBJDIR)
# Minimize shell calls
SOURCES := $(shell find src -name '*.c')
# Use built-in rules when possible
# Instead of:
# %.o: %.c
# $(CC) $(CFLAGS) -c $< -o $@
# Just use the built-in rule
# Parallel-safe directory creation
$(OBJDIR):
@mkdir -p $@
Intégration
Avec contrôle de version
# Get version from git
VERSION = $(shell git describe --tags --always --dirty)
CFLAGS += -DVERSION=\"$(VERSION)\"
# Include git info
GIT_COMMIT = $(shell git rev-parse HEAD)
BUILD_DATE = $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
CFLAGS += -DGIT_COMMIT=\"$(GIT_COMMIT)\" -DBUILD_DATE=\"$(BUILD_DATE)\"
Avec les gestionnaires de paquets
# pkg-config integration
CFLAGS += $(shell pkg-config --cflags libssl)
LDFLAGS += $(shell pkg-config --libs libssl)
# Check dependencies
check-deps:
@pkg-config --exists libssl||(echo "libssl not found" && exit 1)
Dépannage
Questions communes
# Missing separator error
# Make sure recipes use tabs, not spaces
# Target not found
make: *** No rule to make target 'foo'. Stop.
# Check target name and dependencies
# Circular dependency
make: Circular dependency dropped.
# Review dependency chain
# Debug makefile
make -p|grep target_name
make -d
Conseils de débogage
# Print variable values
debug:
@echo "CC = $(CC)"
@echo "CFLAGS = $(CFLAGS)"
@echo "SOURCES = $(SOURCES)"
@echo "OBJECTS = $(OBJECTS)"
# Check if file exists
check-file:
@test -f myfile.c && echo "File exists"||echo "File missing"
Ressources
- Manuel officiel : [gnu.org/software/make/manual] (LINK_4)
- GNU Make: [gnu.org/software/make] (LINK_4)
- Tutorial: makefiletutorial.com
- Meilleures pratiques: [clarkgrubb.com/makefile-style-guide] (LINK_4)