0

In the post How can I configure my makefile for debug and release builds?, I find the answer https://stackoverflow.com/a/20830354/5922947 perfect!

But it seems work with only one final target.

I've tried unsuccessfully to extend this makefile to my projects where there are several executables as targets.

What changes should I make to this example so that I can have multiple final targets and with

make

or

make all

I can produce all targets (in debug or release modes), or

make test

for a specific target?

EDIT: One of the attempts I made but which did not produce any results, but serves to better illustrate what is intended:

(...)
OBJS = $(SRCS:.c=.o)
EXE=$($(TARGET)_EXE)

sample1_EXE=sample1
sample2_EXE=sample2

sample1: TARGET=sample1
sample1: debug

sample2: TARGET=sample2
sample2: debug

all: prep debug sample1 sample2
  • In the answer you cite, the mode is determined by the target (make debug or make release). It sounds as if you want to specify the mode some other way. There are a couple of ways to do it; do you have a preference? – Beta Jan 02 '22 at 03:00
  • "But it seems work with only one final target." - It works for multiple targets by adding them as prerequisites to the phony rules: `debug: debug1.exe debug2.exe`. Unless I'm mistaken on what you are referring to. – Andreas Jan 02 '22 at 13:04
  • In fact, how the question is posed is not clear. In the example, the debug and release targets are bound to a single variable, for example, EXE=sample1. When doing make debug the executable sample1 will be generated in debug mode. If I want to add a new executable, I will have to create an EXE2 variable and repeat the debug and release rules for the new case. If the number of executables is high, the makefile becomes difficult to manage. The objective is to have the possibility of having several "EXE" variables without having to repeat the debug and release rules for each one of them. – Carlos Magno Jan 02 '22 at 16:42
  • I edited the question with one of the attempts made, which did not produce the intended results, hoping that it better illustrates what was intended. – Carlos Magno Jan 02 '22 at 17:01
  • Maybe my question was also unclear. Imagine that your makefile works exactly as you wish, and you want to build the executable `sailfish` in **debug** mode; what command will you use? `make sailfish MODE=debug`? `make sailfish_debug`? `make debug/sailfish`? Something else? – Beta Jan 02 '22 at 17:41
  • I would like it to work as follows `make sailfish` to create sailfish in debug mode `make sailfish mode=release` otherwise. But also `make catamaran` for another case in debug mode. And `make all` to prepare `sailfish` and `catamaran` in debug mode. – Carlos Magno Jan 02 '22 at 18:23

1 Answers1

1

The problem has two parts: specifying the mode, and coordinating the build.

(This will be tricky, so we will take it in stages.)

First the mode. Setting the value of mode in the command, and using debug as the default, is easy. We put this in the makefile:

mode = debug

And a command like make sailfish mode=release will override it.

Now to use the mode:

mode = debug

ifeq ($(mode),debug)
 BUILDDIR := debug
 CFLAGS += -g -O0 -DDEBUG
else
 ifeq ($(mode),release)
  BUILDDIR := release
  CFLAGS += -O3 -DNDEBUG
 else
  $(error unknown mode: $(mode))
 endif
endif

Note that I added the error statement to catch mistakes like make mode=test and make mode=releas. Such errors are much easier to catch and correct if they cause Make to abort on the spot. (Also note that the leading whitespace is not necessary, it has no effect on execution, but it makes the makefile easier to read.)

Now for the build. Suppose we execute make sailfish, so that the mode is debug. First notice that although we give sailfish as the target, we are not actually building sailfish, we are building debug/sailfish. This is important, so we make sailfish a PHONY target that requires the file we actually want:

.PHONY: sailfish

sailfish: $(BUILDDIR)/sailfish

The relevant objects are sailfish.o and seaThing.o. (You must compose this list yourself, it is almost impossible for Make to deduce it.) We could put the objects in the makefile as a distinct variable:

sailfish_OBJS := sailfish.o seaThing.o

but it will make things simpler later if we make this a target-specific variable:

$(BUILDDIR)/sailfish: $(addprefix $(BUILDDIR)/,sailfish.o seaThing.o)
    $(CC) $(CFLAGS) -o $@ $^

We still need a rule to build the object files, and we notice that the two pattern rules in the linked answer collapse into one:

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

This is enough to build debug/sailfish and release/sailfish. To add the executable catamaran, we add:

.PHONY: catamaran

catamaran: $(BUILDDIR)/catamaran

$(BUILDDIR)/catamaran: $(addprefix $(BUILDDIR)/,catamaran.o boatThing.o seaThing.o)
    $(CC) $(CFLAGS) -o $@ $^

But we notice some redundancy here, so we combine the PHONY declarations:

.PHONY: sailfish catamaran

Then we notice that if we put the names of the executables in a variable:

EXECUTABLES := sailfish catamaran

we can use it in the PHONY declaration:

.PHONY: $(EXECUTABLES)

and we can also combine the two PHONY rules into a static pattern rule:

$(EXECUTABLES): %: $(BUILDDIR)/%

and we can separate the prerequisite lines from the recipes and use one recipe for both:

$(BUILDDIR)/sailfish: $(addprefix $(BUILDDIR)/,sailfish.o seaThing.o)
$(BUILDDIR)/catamaran: $(addprefix $(BUILDDIR)/,catamaran.o boatThing.o seaThing.o)

$(addprefix $(BUILDDIR)/,$(EXECUTABLES)):
    $(CC) $(CFLAGS) -o $@ $^

Now, to add another executable like seagull, we must only add seagull to the EXECUTABLES := ... line, and write another line:

    $(BUILDDIR)/seagull: $(addprefix $(BUILDDIR)/,seagull.o birdThing.o seaThing.o)

A few more refinements are possible, but this should be enough for now.

Beta
  • 96,650
  • 16
  • 149
  • 150