Come posso configurare il mio makefile per le versioni di debug e release?


175

Ho il seguente makefile per il mio progetto e vorrei configurarlo per build di rilascio e debug. Nel mio codice, ho un sacco di #ifdef DEBUGmacro a posto, quindi è semplicemente una questione di impostare questa macro e aggiungere i -g3 -gdwarf2flag ai compilatori. Come posso fare questo?

$(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

Solo per chiarire, quando dico build di rilascio / debug, voglio essere in grado di digitare makee ottenere una build di rilascio o make debugottenere una build di debug, senza commentare manualmente le cose nel makefile.


12
Attenzione! $ (CC) = qualcosa è diverso da CC = qualcosa
levif

4
Il target eseguibile viola la regola aurea dei makefile: ogni target dovrebbe aggiornare il file nominando il target, nel tuo caso "eseguibile".
JesperE,

3
^ E in caso contrario, dovrebbe essere dichiarato.PHONY
underscore_d

Risposte:


192

È possibile utilizzare i valori delle variabili specifici del target . Esempio:

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

Ricorda di usare $ (CXX) o $ (CC) in tutti i tuoi comandi di compilazione.

Quindi, 'make debug' avrà flag extra come -DDEBUG e -g dove 'make' no.

Come nota a margine, puoi rendere il tuo Makefile molto più conciso come altri post avevano suggerito.


42
Non devi mai cambiare CXX o CC all'interno di un Makefile o BadThingsMayHappen (TM), quelli contengono il percorso e / o il nome degli eseguibili da eseguire. CPPFLAGS, CXXFLAGS e CFLAGS servono a questo scopo.

11
Questo consiglio è scadente perché mescola i file oggetto di debug e non di debug, in modo da finire con una build danneggiata.
Maxim Egorushkin,

@MaximEgorushkin come risolverlo? Ho riscontrato questo problema di recente. Ho una build eseguibile di debug, che è stata collegata con i file oggetto di rilascio. L'unica soluzione finora è stata dichiarare il debug e rilasciare il
targeting

3
@MauriceRandomNumber Crea debug / release nelle proprie cartelle. Esempio: stackoverflow.com/a/48793058/412080
Maxim Egorushkin,

43

Questa domanda è emersa spesso quando si cerca un problema simile, quindi ritengo che sia giustificata una soluzione completamente implementata. Soprattutto da quando io (e vorrei assumere gli altri) ho faticato a mettere insieme tutte le varie risposte.

Di seguito è riportato un esempio di Makefile che supporta più tipi di build in directory separate. L'esempio illustrato mostra build di debug e release.

Supporta ...

  • directory di progetto separate per build specifiche
  • facile selezione di una build di destinazione predefinita
  • destinazione di preparazione silenziosa per creare le directory necessarie per la costruzione del progetto
  • flag di configurazione del compilatore specifici della build
  • Il metodo naturale di GNU Make per determinare se il progetto richiede una ricostruzione
  • regole del modello piuttosto che regole del suffisso obsolete

#
# 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)

Come si modifica questo per consentire la creazione di file di origine in una directory diversa da quella in cui risiede Makefile?
Jefferson Hudson,

@JeffersonHudson Se i file di origine si trovano in una directory denominata src, modifica la riga SRCS = file1.c file2.c file3.c file4.cda leggere SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
zero2cx,

3
La cosa che non mi piace è la duplicazione di tutte le regole e le variabili per il debug e il rilascio. Ho un Makefile simile ma quando lo estendo ho bisogno di copiare con cura ogni nuova cosa per il debug e il rilascio e convertirlo con cura.
BeeOnRope,

Questa dovrebbe essere la risposta accettata. Vorrei averlo visto molto tempo fa.
Michael Dorst,

42

Se configurando release / build, vuoi dire che hai solo bisogno di una configurazione per makefile, allora è semplicemente una questione e disaccoppia CC e CFLAGS:

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

A seconda che tu possa usare gnu makefile, puoi usare condizionale per renderlo un po 'più elaborato e controllarlo dalla riga di comando:

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

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

e quindi usa:

make DEBUG=0
make DEBUG=1

Se è necessario controllare entrambe le configurazioni contemporaneamente, penso che sia meglio avere directory di compilazione e una directory / configurazione di build.


18
Non so se sto facendo qualcosa di strano, ma per ottenere il debug di if per il lavoro ( ifeq (DEBUG, 1)) per me, la DEBUGvariabile necessaria avvolto in parentesi in questo modo: ifeq ($(DEBUG), 1).
Shanet,

25

Nota che puoi anche rendere il tuo Makefile più semplice, allo stesso tempo:

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

Ora non devi ripetere i nomi dei file in tutto il luogo. Tutti i file .l verranno passati attraverso flex e gcc, tutti i file .y verranno passati attraverso bison e g ++ e tutti i file .cpp tramite solo g ++.

Basta elencare i file .o che ti aspetti di finire e Make farà il lavoro per capire quali regole possono soddisfare le esigenze ...

per il record:

  • $@ Il nome del file di destinazione (quello prima dei due punti)

  • $< Il nome del primo (o unico) file prerequisito (il primo dopo i due punti)

  • $^ I nomi di tutti i file dei prerequisiti (spazio separato)

  • $*La radice (il bit che corrisponde al %carattere jolly nella definizione della regola.


La sezione "per il record" ha un elemento definito due volte con descrizioni diverse. Secondo gnu.org/software/make/manual/make.html#Automatic-Variables , $^è per tutti i file dei prerequisiti.
Grant Peters,

Grazie per quel Grant - errore di battitura risolto! (Ho controllato il Makefile e sembra che l'abbia usato correttamente lì, ma ho scritto a macchina la spiegazione.)
Stobor

2
Vorrei che ci fossero più di queste brevi guide per scrivere un Makefile ragionevolmente piccolo, comprese le variabili automatiche.
AzP

È bello avere sia un debug che una destinazione di rilascio senza dover cambiare il Makefile e la possibilità di scegliere il default in base alle proprie preferenze.

1
Questa soluzione ha il problema che i file di output di debug e release sono mescolati insieme nella stessa directory. Se non sono compatibili, questo esploderà in modi strani e meravigliosi a meno che tu non stia attento a fare una pulizia ogni volta che cambi tra debug e no. Anche se sono compatibili, non farà ciò che ti aspetti senza una ripulitura: se hai il progetto creato come release, e quindi fai DEBUG = 1, ricostruirà solo i file il cui sorgente è cambiato, quindi generalmente ottenere una build di "debug" in questo modo.
BeeOnRope,

3

puoi avere una variabile

DEBUG = 0

quindi è possibile utilizzare un'istruzione condizionale

  ifeq ($(DEBUG),1)

  else

  endif

2

Completamento delle risposte precedenti ... È necessario fare riferimento alle variabili che si definiscono le informazioni nei comandi ...

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

1
C'è una risposta (ora cancellata?) (Che avrebbe dovuto essere un commento su una risposta) che ifeq (DEBUG, 1)dovrebbe essere notata ifeq ($(DEBUG), 1). Immagino che potrebbe essere stato riferito alla tua risposta qui.
Keith M,

0

Puoi anche aggiungere qualcosa di semplice al tuo Makefile come

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

Quindi compilarlo per il debug

make DEBUG=1

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.