¿Cómo puedo configurar mi archivo MAKE para depurar y liberar compilaciones?

175

Tengo el siguiente archivo MAKE para mi proyecto y me gustaría configurarlo para las versiones de lanzamiento y depuración. En mi código, tengo muchas #ifdef DEBUGmacros instaladas, por lo que es simplemente una cuestión de configurar esta macro y agregar las -g3 -gdwarf2banderas a los compiladores. ¿Cómo puedo hacer esto?

$(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 para aclarar, cuando digo versiones de lanzamiento / depuración, quiero poder escribir makey obtener una versión de lanzamiento o make debuguna versión de depuración, sin comentar manualmente las cosas en el archivo MAKE.

Samoz
fuente
12
¡Atención! $ (CC) = algo es diferente de CC = algo
levif
44
El objetivo ejecutable viola la regla de oro de los archivos MAKE: cada objetivo debe actualizar el archivo nombrando el objetivo, en su caso "ejecutable".
JesperE
3
^ Y si no lo hace, debería declararse.PHONY
underscore_d

Respuestas:

192

Puede usar valores variables específicos del objetivo . Ejemplo:

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

Recuerde usar $ (CXX) o $ (CC) en todos sus comandos de compilación.

Luego, 'make debug' tendrá banderas adicionales como -DDEBUG y -g donde 'make' no lo hará.

En una nota al margen, puede hacer que su Makefile sea mucho más conciso como lo habían sugerido otras publicaciones.

David Lin
fuente
42
Nunca debe cambiar CXX o CC dentro de un Makefile o BadThingsMayHappen (TM), ya que contienen la ruta y / o el nombre de los ejecutables que se ejecutarán. CPPFLAGS, CXXFLAGS y CFLAGS sirven para este propósito.
11
Este consejo es deficiente porque mezcla archivos de objetos depurados y no depurados, de modo que uno termina con una compilación corrupta.
Maxim Egorushkin
@MaximEgorushkin ¿cómo solucionarlo? Me encontré con este problema recientemente. Tengo una compilación ejecutable de depuración, que se vinculó con archivos de objeto de lanzamiento. Única solución hasta ahora era declarar depurar y liberar targest falsa
MauriceRandomNumber
3
@MauriceRandomNumber Genere depuración / liberación en sus propias carpetas. Ejemplo: stackoverflow.com/a/48793058/412080
Maxim Egorushkin
43

Esta pregunta ha aparecido a menudo cuando se busca un problema similar, por lo que creo que se justifica una solución completamente implementada. Especialmente porque yo (y supondría que otros) me han costado unir todas las respuestas.

A continuación se muestra un Makefile de ejemplo que admite varios tipos de compilación en directorios separados. El ejemplo ilustrado muestra las versiones de depuración y lanzamiento.

Soporta ...

  • directorios de proyectos separados para compilaciones específicas
  • fácil selección de una compilación de destino predeterminada
  • objetivo de preparación silencioso para crear directorios necesarios para construir el proyecto
  • indicadores de configuración del compilador específicos de compilación
  • El método natural de GNU Make para determinar si el proyecto requiere una reconstrucción
  • reglas de patrón en lugar de las reglas de sufijos obsoletos

#
# 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
fuente
¿Cómo modifica esto para permitir la creación de archivos de origen en un directorio diferente al que reside en Makefile?
Jefferson Hudson
@JeffersonHudson Si los archivos fuente están en un directorio llamado src, entonces modifique la línea SRCS = file1.c file2.c file3.c file4.cpara leer SRCS = src/file1.c src/file2.c src/file3.c src/file4.c.
zero2cx
3
Lo que no me gusta es la duplicación de todas las reglas y variables para depurar y liberar. Tengo un Makefile similar, pero cuando lo extiendo necesito copiar y pegar cuidadosamente cada cosa nueva para depurar y liberar y convertirlo cuidadosamente.
BeeOnRope
Esta debería ser la respuesta aceptada. Desearía haber visto esto hace mucho tiempo.
Michael Dorst
42

Si al configurar release / build, quiere decir que solo necesita una configuración por archivo MAKE, entonces es simplemente una cuestión y desacoplar CC y CFLAGS:

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

Dependiendo de si puede usar gnu makefile, puede usar condicional para hacer esto un poco más elegante y controlarlo desde la línea de comandos:

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

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

y luego usa:

make DEBUG=0
make DEBUG=1

Si necesita controlar ambas configuraciones al mismo tiempo, creo que es mejor tener directorios de compilación y un directorio / config de compilación.

David Cournapeau
fuente
18
No sé si estoy haciendo algo extraño, pero para obtener la depuración if para el trabajo ( ifeq (DEBUG, 1)) para mí, la DEBUGvariable necesaria envuelto en paréntesis, así: ifeq ($(DEBUG), 1).
shanet
25

Tenga en cuenta que también puede simplificar su Makefile, al mismo tiempo:

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

Ahora no tiene que repetir nombres de archivos por todas partes. Cualquier archivo .l pasará a través de flex y gcc, cualquier archivo .y pasará a través de bison y g ++, y cualquier archivo .cpp a través de solo g ++.

Simplemente enumere los archivos .o con los que espera terminar, y Make hará el trabajo de descubrir qué reglas pueden satisfacer las necesidades ...

para el registro:

  • $@ El nombre del archivo de destino (el anterior a los dos puntos)

  • $< El nombre del primer (o único) archivo de requisitos previos (el primero después de los dos puntos)

  • $^ Los nombres de todos los archivos de requisitos previos (separados por espacios)

  • $*La raíz (el bit que coincide con el %comodín en la definición de la regla.

Stobor
fuente
Su sección "para el registro" tiene un elemento definido dos veces con diferentes descripciones. De acuerdo con gnu.org/software/make/manual/make.html#Automatic-Variables , $^es para todos los archivos de requisitos previos.
Grant Peters
Gracias por eso Grant - error tipográfico corregido! (Revisé el Makefile, y parece que lo usé correctamente allí, pero escribí la explicación).
Stobor
2
Desearía que hubiera más de estas guías cortas para escribir un Makefiles razonablemente pequeño, incluidas las variables automáticas.
AzP
Es bueno tener un objetivo de depuración y liberación sin tener que cambiar el Makefile, y la posibilidad de elegir el valor predeterminado según sus propias preferencias.
1
Esta solución tiene el problema de que los archivos de salida de depuración y liberación se mezclan en el mismo directorio. Si no son compatibles, esto explotará de maneras extrañas y maravillosas a menos que tenga cuidado de hacer una limpieza cada vez que cambie entre depuración y no. Incluso si son compatibles, no hará lo que espera sin una limpieza: si tiene el proyecto construido como lanzamiento y luego hace DEBUG = 1, solo reconstruirá archivos cuya fuente ha cambiado, por lo que generalmente no obtener una construcción de "depuración" de esa manera.
BeeOnRope
3

puedes tener una variable

DEBUG = 0

entonces puedes usar una declaración condicional

  ifeq ($(DEBUG),1)

  else

  endif
Tiberiu
fuente
2

Completando las respuestas de antes ... Debe hacer referencia a las variables que define información en sus 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
fuente
1
Hay una respuesta (¿ahora eliminada?) (Que debería haber sido un comentario sobre una respuesta) que ifeq (DEBUG, 1)debería indicarse ifeq ($(DEBUG), 1). Supongo que puede haber estado refiriéndose a su respuesta aquí.
Keith M
0

También puede agregar algo simple a su Makefile como

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

Luego compílalo para depurar

make DEBUG=1

Manolete
fuente