Comment puis-je configurer mon makefile pour les versions de débogage et de publication?

175

J'ai le makefile suivant pour mon projet et j'aimerais le configurer pour les versions de version et de débogage. Dans mon code, j'ai beaucoup de #ifdef DEBUGmacros en place, il s'agit donc simplement de définir cette macro et d'ajouter le-g3 -gdwarf2 indicateurs aux compilateurs. Comment puis-je faire ceci?

$(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 de version / débogage, je veux pouvoir simplement taper makeet obtenir une version de version ou make debuget obtenir une version de débogage, sans commenter manuellement les choses dans le fichier makefile.

Samoz
la source
12
Attention! $ (CC) = quelque chose est différent de CC = quelque chose
levif
4
La cible exécutable viole la règle d'or des makefiles: chaque cible doit mettre à jour le fichier nommant la cible, dans votre cas "exécutable".
JesperE
3
^ Et si ce n'est pas le cas, il devrait être déclaré.PHONY
underscore_d

Réponses:

192

Vous pouvez utiliser des valeurs de variable 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.

Ensuite, «make debug» aura des indicateurs supplémentaires comme -DDEBUG et -g alors que «make» ne le sera pas.

D'un autre côté, vous pouvez rendre votre Makefile beaucoup plus concis comme d'autres articles l'avaient suggéré.

David Lin
la source
42
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. CPPFLAGS, CXXFLAGS et CFLAGS servent cet objectif.
11
Ce conseil est médiocre car il mélange des fichiers objets de débogage et non débogués, de sorte que l'on se retrouve avec une construction corrompue.
Maxim Egorushkin
@MaximEgorushkin comment résoudre ce problème? Je suis tombé sur ce problème récemment. J'ai une version exécutable de débogage, qui était liée aux fichiers d'objet de version. La seule solution à ce jour était de déclarer le débogage et de publier targest bidon
MauriceRandomNumber
3
@MauriceRandomNumber Construisez le débogage / la version dans ses propres dossiers. Exemple: stackoverflow.com/a/48793058/412080
Maxim Egorushkin
43

Cette question est souvent apparue lors de la recherche d'un problème similaire, je pense donc qu'une solution entièrement mise en œuvre est justifiée. D'autant plus que moi (et je suppose que d'autres) ont eu du mal à rassembler toutes les différentes réponses.

Voici un exemple de Makefile qui prend en charge plusieurs types de build dans des répertoires séparés. L'exemple illustré montre les versions de débogage et de publication.

Les soutiens ...

  • répertoires de projet séparés pour des versions spécifiques
  • sélection facile d'une construction cible par défaut
  • cible de préparation silencieuse pour créer les répertoires nécessaires à la construction du projet
  • indicateurs 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
  • règles de modèle plutôt que les règles de suffixe obsolètes

#
# 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)
ffhaddad
la source
Comment modifiez-vous cela pour permettre la création de fichiers source dans un répertoire autre que celui dans lequel réside Makefile?
Jefferson Hudson le
@JeffersonHudson Si les fichiers source se trouvent dans un répertoire nommé src, 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.
zero2cx
3
Ce que je n'aime pas, c'est la duplication de toutes les règles et variables pour le débogage et la publication. J'ai un Makefile similaire, mais lors de son extension, je dois soigneusement copier-coller chaque nouvelle chose pour le débogage et le publier et le convertir soigneusement.
BeeOnRope
Cela devrait être la réponse acceptée. J'aurais aimé voir ça il y a longtemps.
Michael Dorst
42

Si par configure release / build, vous voulez dire que vous n'avez besoin que d'une seule configuration par makefile, alors c'est simplement une question et découpler CC et CFLAGS:

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

Selon que vous pouvez utiliser gnu makefile, vous pouvez utiliser conditionnel pour rendre cela un peu plus sophistiqué et le contrôler à partir de la ligne de commande:

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

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

puis utilisez:

make DEBUG=0
make DEBUG=1

Si vous avez besoin de 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 / config.

David Cournapeau
la source
18
Je ne sais pas si je fais quelque chose d' étrange, mais pour obtenir le débogage instruction if au travail ( ifeq (DEBUG, 1)) pour moi, la DEBUGvariable de parenthèses avait besoin enveloppé dans comme ceci: ifeq ($(DEBUG), 1).
shanet
25

Notez que vous pouvez également simplifier votre Makefile, 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

Vous n'avez plus besoin de répéter les noms de fichiers partout. Tous les fichiers .l passeront par flex et gcc, tous les fichiers .y passeront par bison et g ++, et tous les fichiers .cpp par uniquement g ++.

Listez simplement les fichiers .o avec lesquels vous vous attendez à vous retrouver, et Make se chargera de déterminer quelles règles peuvent satisfaire les besoins ...

pour mémoire:

  • $@ Le nom du fichier cible (celui avant les deux-points)

  • $< Le nom du premier (ou 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 racine (le bit qui correspond au %caractère générique dans la définition de règle.

Stobor
la source
Votre section "pour l'enregistrement" a un élément défini deux fois avec des descriptions différentes. Selon gnu.org/software/make/manual/make.html#Automatic-Variables , $^est pour tous les fichiers prérequis.
Grant Peters
Merci pour cette subvention - faute de frappe corrigée! (J'ai vérifié le Makefile, et il semble que je l'ai utilisé correctement là-bas, mais j'ai tapé l'explication.)
Stobor
2
J'aurais aimé qu'il y ait plus de ces petits guides pour écrire des Makefiles raisonnablement petits, y compris les variables automatiques.
AzP
Il est agréable d'avoir à la fois une cible de débogage et de publication sans avoir à changer le Makefile, et la possibilité de choisir la valeur par défaut en fonction de vos propres préférences.
1
Cette solution a le problème que les fichiers de sortie de débogage et de version sont mélangés ensemble dans le même répertoire. S'ils ne sont pas compatibles, cela explosera de manière étrange et merveilleuse à moins que vous ne preniez soin de faire un nettoyage à chaque fois que vous changez entre le débogage et non. Même s'ils sont compatibles, il ne fera pas ce que vous attendez sans un nettoyage: si vous avez le projet construit en tant que version, puis faites DEBUG = 1, il ne reconstruira que les fichiers dont la source a changé, donc vous ne serez généralement pas obtenir une compilation "debug" de cette façon.
BeeOnRope
3

vous pouvez avoir une variable

DEBUG = 0

alors vous pouvez utiliser une instruction conditionnelle

  ifeq ($(DEBUG),1)

  else

  endif
Tiberiu
la source
2

Compléter les réponses précédentes ... Vous devez référencer les variables que vous définissez info dans vos commandes ...

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

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

all: 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

CommandParser.tab.o: CommandParser.y
    bison -d CommandParser.y
    $(CXX) -c CommandParser.tab.c

Command.o: Command.cpp
    $(CXX) -c Command.cpp

clean:
    rm -f CommandParser.tab.* CommandParser.yy.* output *.o
Stobor
la source
1
Il y a une réponse (maintenant supprimée?) (Qui aurait dû être un commentaire sur une réponse) qui ifeq (DEBUG, 1)devrait être noté ifeq ($(DEBUG), 1). Je suppose que cela faisait peut-être référence à votre réponse ici.
Keith M
0

Vous pouvez également ajouter quelque chose de simple à votre Makefile tel que

ifeq ($(DEBUG),1)
   OPTS = -g
endif

Puis compilez-le pour le débogage

make DEBUG=1

Manolete
la source