2
votes

I am trying to build a Makefile that can build both release and debug targets by specifying a target rather than a variable (such as make debug=1, not great) I have a condensed simplified example here that emulates what I am trying to achieve:

ifdef debug
    BINARY=my_binary_debug
    MODULE_1_BIN=abc_debug
    MODULE_2_BIN=xyz_debug
    export DBG=1
else
    BINARY=my_binary
    MODULE_1_BIN=abc
    MODULE_2_BIN=xyz
endif

FLAG=something

.PHONY: all debug clean

all: bin/$(BINARY).bin

bin/$(BINARY).bin: module_1/$(MODULE_1_BIN).bin module_2/$(MODULE_2_BIN).bin
    cat module_1/$(MODULE_1_BIN).bin $(FLAG) module_2/$(MODULE_2_BIN).bin > $@

module_1/$(MODULE_1_BIN).bin:
    $(MAKE) -C module_1

module_2/$(MODULE_2_BIN).bin:
    $(MAKE) -C module_2


clean:
    rm bin/*.bin
    $(MAKE) -C module_1 clean
    $(MAKE) -C module_2 clean

This example would have me use make debug=1 but I don't really like it and feel this implementation could be better. One way is to use target specific variables , but I am not entirely sure how to use them when the dependencies also need to change their names when a debug target is built. Something like:

debug: export DBG:=1
debug: bin/$(BINARY)_debug.bin
debug: module_1/$(MODULE_1_BIN)_debug.bin
debug: module_2/$(MODULE_2_BIN)_debug.bin

bin/$(BINARY).bin bin/$(BINARY)_debug.bin: module_1/$(MODULE_1_BIN).bin module_2/$(MODULE_2_BIN).bin
        cat module_1/$(MODULE_1_BIN).bin module_2/$(MODULE_2_BIN).bin > $@

Here the target will work alright as the bin/$(BINARY).bin target wont need to be built when debug is being built.But I am not sure how to handle the dependencies module_1/$(MODULE_1_BIN).bin and module_2/$(MODULE_2_BIN).bin in the recipe when building debug

2

2 Answers

1
votes

A previous version of this answer tried to use target-specific variables, but they are of limited use, because they are used in recipes, but are ignored when it comes to specifying targets and prerequisites.

Implicit rules make use of patterns, but capturing both my_binary and my_binary_debug with the implicit target my_binary% doesn’t work, because patterns don’t match the empty string.

In this case however, implicit rules work, because all your targets and dependencies have the common ending .bin, which is nonempty. The string that matches % is avalaible in the automatic variable $*. So this is my solution:

.PHONY: all debug clean

FLAG=something 

# Maybe still needed by subdirectory makefiles
debug: export DBG:=1

all: bin/my_binary.bin
debug: bin/my_binary_debug.bin

# % will match both ".bin" and "_debug.bin"
# It won’t match the empty string.
bin/my_binary%: module_1/abc% module_2/xyz%
    cat module_1/abc$* $(FLAG) module_2/xyz$* > $@

# Maybe, by specifying the target in the sub-make commandline,
# you can get rid of the DBG variable altogether.
module_1/abc%:
    $(MAKE) -C module_1 $@

module_2/xyz%:
    $(MAKE) -C module_2 $@

clean:
    rm bin/*.bin
    $(MAKE) -C module_1 clean
    $(MAKE) -C module_2 clean

If the recipe to build bin/my_binary.bin can be rewritten with all modules one after another and $(FLAG) at the beginning, some more semplifications are possible:

.PHONY: all debug clean

MODULES = module_1/abc module_2/xyz
FLAG    = something 

# Maybe still needed by subdirectory makefiles
debug: export DBG:=1

all: bin/my_binary.bin
debug: bin/my_binary_debug.bin

bin/my_binary%: $(addsuffix %,$(MODULES))
    cat $(FLAG) $^ > $@

module_1/abc%:
    $(MAKE) -C module_1 $@

module_2/xyz%:
    $(MAKE) -C module_2 $@

clean:
    rm bin/*.bin
    for m in $(MODULES); do $(MAKE) -C $$(dirname $$m) clean; done
1
votes

As I said before, you cannot use target-specific variables in prerequisites for that target, only in recipes for that target and recursive prerequisites. I think the best you can do, according to what you want, is this:

RELEASE_SUFFIX:=#
DEBUG_SUFFIX:=_debug

BINARY:=my_binary
MODULE_1_BIN:=abc
MODULE_2_BIN:=xyz

FLAG=-u

.PHONY: all debug

all: bin/$(BINARY).bin

debug: export DBG:=1
debug: BINARY+=$(DEBUG_SUFFIX)
debug: bin/$(BINARY).bin


define rules

bin/$(BINARY)$($(1)): module_1/$(MODULE_1_BIN)$($(1)).bin module_2/$(MODULE_2_BIN)$($(1)).bin
    cat $(FLAG) $$^ > $$@

module_1/$(MODULE_1_BIN)$($(1)).bin:
    $(MAKE) -C $$(@D)

module_2/$(MODULE_2_BIN)$($(1)).bin:
    $(MAKE) -C $$(@D)

endef


$(foreach suffix, RELEASE_SUFFIX DEBUG_SUFFIX, $(eval $(call rules,$(suffix))))