Como posso configurar meu makefile para compilações de depuração e lançamento?

175

Eu tenho o seguinte makefile para o meu projeto e gostaria de configurá-lo para versões de lançamento e depuração. No meu código, eu tenho muitas #ifdef DEBUGmacros, então é simplesmente uma questão de definir essa macro e adicionar os -g3 -gdwarf2sinalizadores aos compiladores. Como posso fazer isso?

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

Apenas para esclarecer, quando digo compilações de liberação / depuração, quero poder digitar makee obter uma compilação de liberação ou make debugobter uma compilação de depuração, sem comentar manualmente as coisas no makefile.

samoz
fonte
12
Atenção! $ (CC) = algo é diferente do CC = algo
levif
4
O alvo executável viola a regra de ouro dos makefiles: todo alvo deve atualizar o arquivo nomeando o alvo, no seu caso "executável".
precisa saber é o seguinte
3
^ E se isso não acontecer, ele deve ser declarada.PHONY
underscore_d

Respostas:

192

Você pode usar valores variáveis ​​específicos do destino . Exemplo:

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

Lembre-se de usar $ (CXX) ou $ (CC) em todos os seus comandos de compilação.

Então, 'make debug' terá sinalizadores extras como -DDEBUG e -g onde 'make' não terá.

Em uma nota lateral, você pode tornar seu Makefile muito mais conciso, como outras postagens sugeriram.

David Lin
fonte
42
Você nunca deve alterar CXX ou CC em um Makefile ou BadThingsMayHappen (TM), pois eles contêm o caminho e / ou o nome dos executáveis ​​a serem executados. CPPFLAGS, CXXFLAGS e CFLAGS atendem a esse propósito.
11
Esse conselho é ruim porque combina arquivos de objeto de depuração e não depuração, para que um acabe com uma compilação corrompida.
Maxim Egorushkin
@MaximEgorushkin como consertar isso? Me deparei com esse problema recentemente. Eu tenho uma compilação executável de depuração, que foi vinculada a arquivos de objeto de lançamento. Única solução até agora foi a declarar depuração e versão targest falso
MauriceRandomNumber
3
@MauriceRandomNumber Crie depuração / release em suas próprias pastas. Exemplo: stackoverflow.com/a/48793058/412080
Maxim Egorushkin
43

Essa pergunta apareceu com frequência ao procurar um problema semelhante, portanto, acho que uma solução totalmente implementada é necessária. Especialmente porque eu (e eu diria que outros) lutam para reunir todas as várias respostas.

Abaixo está um Makefile de amostra que suporta vários tipos de compilação em diretórios separados. O exemplo ilustrado mostra compilações de depuração e lançamento.

Apoia ...

  • diretórios de projeto separados para construções específicas
  • seleção fácil de uma construção de destino padrão
  • destino de preparação silenciosa para criar diretórios necessários para a construção do projeto
  • Sinalizadores de Configuração do Compilador Específicos de Construção
  • O método natural do GNU Make para determinar se o projeto requer uma reconstrução
  • regras padrão em vez das regras obsoletas de sufixo

#
# 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
fonte
Como você modifica isso para permitir a criação de arquivos de origem em um diretório diferente daquele em que o Makefile reside?
Jefferson Hudson
@JeffersonHudson Se os arquivos de origem estiverem em um diretório nomeado src, modifique a linha SRCS = file1.c file2.c file3.c file4.cpara ler SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
Zero2cx
3
O que eu não gosto é a duplicação de todas as regras e variáveis ​​para depuração e lançamento. Eu tenho um Makefile semelhante, mas ao estendê-lo, preciso copiar e colar cuidadosamente cada novo item para depuração e lançamento e convertê-lo com cuidado.
BeeOnRope
Essa deve ser a resposta aceita. Eu gostaria de ter visto isso há muito tempo.
Michael Dorst 23/04
42

Se por configurar release / build, você quer dizer que você só precisa de uma configuração por makefile, é simplesmente uma questão e desacoplar CC e CFLAGS:

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

Dependendo se você pode usar o gnu makefile, é possível usar o condicional para tornar isso um pouco mais sofisticado e controlá-lo na linha de comando:

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

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

e então use:

make DEBUG=0
make DEBUG=1

Se você precisar controlar as duas configurações ao mesmo tempo, acho melhor ter diretórios de construção e um diretório de configuração / configuração.

David Cournapeau
fonte
18
Eu não sei se estou fazendo algo estranho, mas para obter o debug if para o trabalho ( ifeq (DEBUG, 1)) para mim, a DEBUGvariável precisava ser envolto em parênteses assim: ifeq ($(DEBUG), 1).
Shanet
25

Observe que você também pode tornar seu Makefile mais simples, ao mesmo 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

Agora você não precisa repetir os nomes de arquivos em todo o lugar. Quaisquer arquivos .l serão passados ​​pelo flex e gcc, quaisquer arquivos .y serão passados ​​pelo bison e g ++, e quaisquer arquivos .cpp apenas pelo g ++.

Basta listar os arquivos .o que você espera que acabem, e o Make fará o trabalho de descobrir quais regras podem satisfazer as necessidades ...

para o registro:

  • $@ O nome do arquivo de destino (aquele antes dos dois pontos)

  • $< O nome do primeiro (ou único) arquivo de pré-requisito (o primeiro após os dois pontos)

  • $^ Os nomes de todos os arquivos de pré-requisito (separados por espaço)

  • $*A raiz (o bit que corresponde ao %curinga na definição de regra.

Stobor
fonte
A seção "para registro" tem um item definido duas vezes com descrições diferentes. De acordo com gnu.org/software/make/manual/make.html#Automatic-Variables , $^é para todos os arquivos de pré-requisito.
Grant Peters
Obrigado por esse Grant - erro de digitação corrigido! (Eu verifiquei sobre o Makefile, e parece que eu usei-o corretamente lá, mas typoed a explicação.)
Stobor
2
Eu gostaria que houvesse mais desses breves guias para escrever Makefiles razoavelmente pequenos, incluindo as variáveis ​​automáticas.
AzP
É bom ter um destino de depuração e liberação sem precisar alterar o Makefile e a capacidade de escolher o padrão com base em sua própria preferência.
1
Esta solução tem o problema de que os arquivos de saída de depuração e liberação são misturados no mesmo diretório. Se eles não forem compatíveis, isso explodirá de maneiras estranhas e maravilhosas, a menos que você tenha cuidado para fazer uma limpeza toda vez que alternar entre depuração e não. Mesmo se eles forem compatíveis, ele não fará o que você espera sem uma limpeza: se você tiver o projeto criado como versão e fizer DEBUG = 1, ele reconstruirá apenas arquivos cuja origem foi alterada, portanto, geralmente obtenha uma compilação "debug" dessa maneira.
BeeOnRope
3

você pode ter uma variável

DEBUG = 0

então você pode usar uma declaração condicional

  ifeq ($(DEBUG),1)

  else

  endif
Tiberiu
fonte
2

Concluindo as respostas anteriores ... Você precisa fazer referência às variáveis ​​que definem informações em seus comandos ...

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
fonte
1
Há uma resposta (agora excluída?) (Que deveria ter sido um comentário em uma resposta) que ifeq (DEBUG, 1)deveria ser anotada ifeq ($(DEBUG), 1). Suponho que possa estar se referindo à sua resposta aqui.
187 Keith M
0

Você também pode adicionar algo simples ao seu Makefile, como

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

Em seguida, compile-o para depuração

make DEBUG=1

Manolete
fonte