215 votes

Comment puis-je configurer mon makefile pour les builds debug et release ?

J'ai le makefile suivant pour mon projet, et j'aimerais le configurer pour les builds release et debug. Dans mon code, j'ai beaucoup de #ifdef DEBUG en place, il s'agit donc simplement de définir cette macro et d'ajouter l'élément -g3 -gdwarf2 aux compilateurs. Comment puis-je faire cela ?

$(CC) = g++ -g3 -gdwarf2
$(cc) = gcc -g3 -gdwarf2

all: executable

executable: CommandParser.tab.o CommandParser.yy.o Command.o
    g++ -g -o output CommandParser.yy.o CommandParser.tab.o Command.o -lfl

CommandParser.yy.o: CommandParser.l 
    flex -o CommandParser.yy.c CommandParser.l
    gcc -g -c CommandParser.yy.c

CommandParser.tab.o: CommandParser.y
    bison -d CommandParser.y
    g++ -g -c CommandParser.tab.c

Command.o: Command.cpp
    g++ -g -c Command.cpp

clean:
    rm -f CommandParser.tab.* CommandParser.yy.* output *.o

Juste pour clarifier, quand je parle de versions release/debug, je veux pouvoir simplement taper make et obtenir une version de lancement ou make debug et obtenir une construction de débogage, sans commenter manuellement des choses dans le makefile.

12 votes

Attention ! $(CC) = quelque chose est différent de CC = quelque chose

6 votes

La cible exécutable viole la règle d'or des makefiles : chaque cible doit mettre à jour le fichier qui nomme la cible, dans votre cas "exécutable".

4 votes

Et si ce n'est pas le cas, il faut le déclarer. .PHONY

229voto

David Lin Points 1983

Vous pouvez utiliser Valeurs des variables spécifiques à la cible . Exemple :

CXXFLAGS = -g3 -gdwarf2
CCFLAGS = -g3 -gdwarf2

all: executable

debug: CXXFLAGS += -DDEBUG -g
debug: CCFLAGS += -DDEBUG -g
debug: executable

executable: CommandParser.tab.o CommandParser.yy.o Command.o
    $(CXX) -o output CommandParser.yy.o CommandParser.tab.o Command.o -lfl

CommandParser.yy.o: CommandParser.l 
    flex -o CommandParser.yy.c CommandParser.l
    $(CC) -c CommandParser.yy.c

N'oubliez pas d'utiliser $(CXX) ou $(CC) dans toutes vos commandes de compilation.

Alors, 'make debug' aura des drapeaux supplémentaires comme -DDEBUG et -g alors que 'make' n'en aura pas.

D'ailleurs, vous pouvez rendre votre Makefile beaucoup plus concis comme d'autres messages l'ont suggéré.

49 votes

Vous ne devez jamais changer CXX ou CC dans un Makefile ou BadThingsMayHappen (TM), ceux-ci contiennent le chemin et/ou le nom des exécutables à exécuter. Les CPPFLAGS, CXXFLAGS et CFLAGS servent à cela.

17 votes

Ce conseil est mauvais car il mélange les fichiers d'objets de débogage et de non-débogage, de sorte que l'on se retrouve avec une construction corrompue.

0 votes

@MaximEgorushkin comment réparer cela ? J'ai rencontré ce problème récemment. J'ai une construction exécutable de débogage, qui a été liée avec des fichiers d'objets de libération. La seule solution jusqu'à présent était de déclarer que les targest de débogage et de libération étaient faux.

71voto

ffhaddad Points 576

Cette question est apparue souvent lors de la recherche d'un problème similaire, donc je pense qu'une solution complète est justifiée. D'autant plus que j'ai eu du mal (et je suppose que d'autres aussi) à rassembler les différentes réponses.

Ci-dessous se trouve un exemple de Makefile qui supporte plusieurs types de construction dans des répertoires séparés. L'exemple illustré montre des constructions debug et release.

Supports ...

  • des répertoires de projet séparés pour des constructions spécifiques
  • sélection facile d'une construction cible par défaut
  • silent prep target pour créer les répertoires nécessaires à la construction du projet
  • drapeaux de configuration du compilateur spécifiques à la construction
  • La méthode naturelle de GNU Make pour déterminer si un projet nécessite une reconstruction.
  • plutôt que les règles obsolètes des suffixes.

#
# Compiler flags
#
CC     = gcc
CFLAGS = -Wall -Werror -Wextra

#
# Project files
#
SRCS = file1.c file2.c file3.c file4.c
OBJS = $(SRCS:.c=.o)
EXE  = exefile

#
# Debug build settings
#
DBGDIR = debug
DBGEXE = $(DBGDIR)/$(EXE)
DBGOBJS = $(addprefix $(DBGDIR)/, $(OBJS))
DBGCFLAGS = -g -O0 -DDEBUG

#
# Release build settings
#
RELDIR = release
RELEXE = $(RELDIR)/$(EXE)
RELOBJS = $(addprefix $(RELDIR)/, $(OBJS))
RELCFLAGS = -O3 -DNDEBUG

.PHONY: all clean debug prep release remake

# Default build
all: prep release

#
# Debug rules
#
debug: $(DBGEXE)

$(DBGEXE): $(DBGOBJS)
    $(CC) $(CFLAGS) $(DBGCFLAGS) -o $(DBGEXE) $^

$(DBGDIR)/%.o: %.c
    $(CC) -c $(CFLAGS) $(DBGCFLAGS) -o $@ $<

#
# Release rules
#
release: $(RELEXE)

$(RELEXE): $(RELOBJS)
    $(CC) $(CFLAGS) $(RELCFLAGS) -o $(RELEXE) $^

$(RELDIR)/%.o: %.c
    $(CC) -c $(CFLAGS) $(RELCFLAGS) -o $@ $<

#
# Other rules
#
prep:
    @mkdir -p $(DBGDIR) $(RELDIR)

remake: clean all

clean:
    rm -f $(RELEXE) $(RELOBJS) $(DBGEXE) $(DBGOBJS)

0 votes

Comment modifier ceci pour permettre la construction de fichiers sources dans un répertoire autre que celui dans lequel réside Makefile ?

0 votes

@JeffersonHudson Si les fichiers sources sont dans un répertoire nommé src puis modifiez la ligne SRCS = file1.c file2.c file3.c file4.c à lire SRCS = src/file1.c src/file2.c src/file3.c src/file4.c .

6 votes

Ce que je n'aime pas, c'est la duplication de toutes les règles et variables pour le débogage et la version. J'ai un Makefile similaire mais lorsque je l'étend, je dois soigneusement copier-coller chaque nouvelle chose pour debug et release et la convertir avec soin.

48voto

David Cournapeau Points 21956

Si par configure release/build, vous voulez dire que vous n'avez besoin que d'une configuration par makefile, alors il s'agit simplement de découpler CC et CFLAGS :

CFLAGS=-DDEBUG
#CFLAGS=-O2 -DNDEBUG
CC=g++ -g3 -gdwarf2 $(CFLAGS)

Selon que vous pouvez ou non utiliser gnu makefile, vous pouvez utiliser le conditionnel pour rendre cela un peu plus fantaisiste, et le contrôler depuis la ligne de commande :

DEBUG ?= 1
ifeq ($(DEBUG), 1)
    CFLAGS =-DDEBUG
else
    CFLAGS=-DNDEBUG
endif

.o: .c
    $(CC) -c $< -o $@ $(CFLAGS)

et ensuite utiliser :

make DEBUG=0
make DEBUG=1

Si vous devez contrôler les deux configurations en même temps, je pense qu'il est préférable d'avoir des répertoires de construction, et un répertoire de construction / configuration.

19 votes

Je ne sais pas si je fais quelque chose d'étrange, mais pour que l'instruction if de débogage fonctionne ( ifeq (DEBUG, 1) ) pour moi, le DEBUG doit être entourée de parenthèses comme suit : ifeq ($(DEBUG), 1) .

26voto

Stobor Points 15428

Notez que vous pouvez aussi rendre votre Makefile plus simple, en même temps :

DEBUG ?= 1
ifeq (DEBUG, 1)
    CFLAGS =-g3 -gdwarf2 -DDEBUG
else
    CFLAGS=-DNDEBUG
endif

CXX = g++ $(CFLAGS)
CC = gcc $(CFLAGS)

EXECUTABLE = output
OBJECTS = CommandParser.tab.o CommandParser.yy.o Command.o
LIBRARIES = -lfl

all: $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CXX) -o $@ $^ $(LIBRARIES)

%.yy.o: %.l 
    flex -o $*.yy.c $<
    $(CC) -c $*.yy.c

%.tab.o: %.y
    bison -d $<
    $(CXX) -c $*.tab.c

%.o: %.cpp
    $(CXX) -c $<

clean:
    rm -f $(EXECUTABLE) $(OBJECTS) *.yy.c *.tab.c

Maintenant vous n'avez plus besoin de répéter les noms de fichiers partout. Tout fichier .l passera par flex et gcc, tout fichier .y passera par bison et g++, et tout fichier .cpp par g++.

Faites simplement la liste des fichiers .o que vous souhaitez obtenir, et Make fera le travail de déterminer quelles règles peuvent satisfaire les besoins...

pour le compte rendu :

  • $@ Le nom du fichier cible (celui qui précède les deux points).

  • $< Le nom du premier (ou du seul) fichier de prérequis (le premier après les deux points).

  • $^ Les noms de tous les fichiers prérequis (séparés par des espaces)

  • $* La tige (la partie qui correspond au % joker dans la définition de la règle.

0 votes

Dans votre section "pour mémoire", un élément est défini deux fois avec des descriptions différentes. Selon gnu.org/software/make/manual/make.html#Variables-automatiques , $^ est pour tous les fichiers prérequis.

0 votes

Merci pour cela Grant - la faute de frappe a été corrigée ! (J'ai vérifié le Makefile, et il semble que je l'ai utilisé correctement, mais que j'ai fait une faute de frappe dans l'explication).

2 votes

J'aimerais qu'il y ait plus de ces petits guides pour écrire un Makefile raisonnablement petit, y compris les variables automatiques.

5voto

JesperE Points 34356

Je suis d'accord avec @davidlin, utilisez des variables spécifiques à la cible pour définir des drapeaux différents pour chaque configuration. Cependant, vous voulez probablement aussi placer vos fichiers de sortie dans des répertoires séparés, afin de pouvoir construire différentes configurations sans tout reconstruire. En utilisant l'exemple de @davidlin, vous pourriez le faire :

debug: CONFIG=debug

debug: executable

executable: output/$(CONFIG)/myprog

# Always use "$@" when referring to the target file.
output/$(CONFIG)/myprog: ... 
        $(CXX) ... -o $@

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X