0

I'm trying to write a makefile which supports release mode and debug mode, while in debug mode, I want to link an extra object debug.o to override functions for debugging.

For example:

CFLAGS = -Wall -I$(INCPATH)

INCPATH = include
TARGET = foo
OBJ = foo.o bar.o

# release mode
all: build run clear

# debug mode
debug: CFLAGS += -g -DDEBUG
debug: OBJ += debug.o
debug: build gdb-run clear

# link objects
build: $(OBJ)
    gcc $(CFLAGS) -o $(TARGET) $(OBJ)

# compile source code
%.o: %.c $(INCPATH)/*.h
    gcc $(CFLAGS) -c $@ $<

# default run mode
run:
    ./$(TARGET)

# debug run mode
gdb-run:
    gdb --args $(TARGET)

clear:
    rm -f $(OBJ) $(TARGET)

I expected it expand $(OBJ) to foo.o bar.o debug.o when I call make debug, but instead it only expands to foo.o bar.o, because targets are expanded immediately when parsed.

I've tried using .SECONDEXPANSION:, but couldn't work it out.

And I've also tried $(eval OBJ += debug.o), but that resulted in expanding $(OBJ) to foo.o bar.o debug.o even while running make all.

Is this possible, or should I just work around?

edit: fixed a typo, thanks to @matt

Claudio
  • 10,614
  • 4
  • 31
  • 71
Tsiruan
  • 23
  • 1
  • 1
  • 5

2 Answers2

0

I use the GNU make "conditional" mechanism with a make variable named MODE for this. In your case, what about

MODE = RELEASE
OBJ = foo.o bar.o
ifeq ($(MODE),DEBUG)
   OBJ += debug.o
endif
[...]

Then build with either

make    # a MODE=RELEASE build by default

or

make MODE=DEBUG
Jens
  • 69,818
  • 15
  • 125
  • 179
0

I expected it expand $(OBJ) to foo.o bar.o debug.o

It does this inside the recipe, but not in the prerequisite list.

6.11 Target-specific Variable Values

As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).

So you have to stick with conditionals to achieve your goal.

BTW. CFLAGS = -Wall -I(INCPATH) is a typo. %.o: %.c $(INCPATH)/*.h is plainly wrong - use $(wildcard ...) when really needed. Also change all occurences of build to $(TARGET), as it's really foo what you're building. Then revise your makefile - it's probably not a good idea to clean everything after every run.

Matt
  • 13,674
  • 1
  • 18
  • 27
  • How is `CFLAGS = -Wall -I(INCPATH)` wrong? That is what I use exactly in my makefile, and it compiled with no complains. I also found an answer [here](https://stackoverflow.com/questions/973146/how-to-include-header-files-in-gcc-search-path). I clean the binaries every time, because `make debug` won't recompile the objects with the `-DDEBUG` flag if there are left over binaries from `make all`, what is the better way of doing it? – Tsiruan May 13 '19 at 15:31
  • @Tsiruan `How is CFLAGS = -Wall -I(INCPATH) wrong?` Must be `-I$(INCPATH)` obviously. `what is the better way of doing it?` Just `make -B` will do the full rebuild without any problems. However, it's better to put release and debug binaries into different directories. – Matt May 13 '19 at 15:31