views:

2924

answers:

6

I have the following makefile for my project, and I'd like to configure it for release and debug builds. In my code, I have lots of #ifdef DEBUG macros in place, so it's simply a matter of setting this macro and adding the '-g3 -gdwarf2' flags to the compilers. How can I do this?

EDIT: Just to clarify, when I say release/debug builds, I want to be able to just type 'make' and get a release build or 'make debug' and get a debug build, without manually commenting out things in the makefile.

$(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
+3  A: 

If by configure release/build, you mean you only need one config per makefile, then it is simply a matter and decoupling CC and CFLAGS:

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

Depending on whether you can use gnu makefile, you can use conditional to make this a bit fancier, and control it from the command line:

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

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

and then use:

make DEBUG=0
make DEBUG=1

If you need to control both configurations at the same time, I think it is better to have build directories, and one build directory / config.

David Cournapeau
A: 

you can have a variable

DEBUG = 0

then you can use a conditional statement

  ifeq ($(DEBUG),1)

  else

  endif
Tiberiu Hajas
+1  A: 

Completing the answers from earlier... You need to reference the variables you define info in your commands...

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
+3  A: 

Note that you can also make your Makefile simpler, at the same time:

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

Now you don't have to repeat filenames all over the place. Any .l files will get passed through flex and gcc, any .y files will get passed through bison and g++, and any .cpp files through just g++.

Just list the .o files you expect to end up with, and Make will do the work of figuring out which rules can satisfy the needs...

for the record:

  • $@ The name of the target file (the one before the colon)

  • $< The name of the first (or only) prerequisite file (the first one after the colon)

  • $^ The names of all the prerequisite files (space separated)

  • $* The stem (the bit which matches the % wildcard in the rule definition.

Stobor
You're "for the record" section has one item defined twice with different descriptions. According to http://www.gnu.org/software/make/manual/make.html#Automatic-Variables, `$^` is for all of the prerequisite files.
Grant Peters
Thanks for that Grant - typo fixed! (I checked over the Makefile, and it appears I used it correctly there, but typoed the explanation.)
Stobor
+4  A: 

You can use Target-specific Variable Values. Example:

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

all: executable

debug: CXX += -DDEBUG -g
debug: CC += -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

Remember to use $(CXX) or $(CC) in all your compile commands.

Then, 'make debug' will have extra flags like -DDEBUG and -g where as 'make' will not.

On a side note, you can make your Makefile a lot more concise like other posts had suggested.

David Lin
The executable target violates the golden rule of makefiles: every target should update the file naming the target, in your case "executable".
JesperE
That was supposed to be a comment to the original post.
JesperE
+1  A: 

I agree with @davidlin, use target-specific variables to set different flags for each config. However, you probably also want to place your output files in separate directories, so that you can build different configs without rebuilding everything. Using @davidlin's example, you could do:

debug: CONFIG=debug

debug: executable

executable: output/$(CONFIG)/myprog

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