Gauthier
Gauthier

Reputation: 41955

Makefile wildcard for makefile variables, to define generic rules

Background, I suspect XY problem

I have simpler C modules in a directory. I want to write unit tests for these in a sub-directory test/. These unit tests are no more than C programs linking to the module under test, one directory above. I want a Makefile that defines several build targets and lets me build and run the test executables in one step, or separately.

My attempted solution

I've attempted the following:

CC = gcc
CFLAGS = -ggdb -Wall -Wextra -Werror -O3 -std=c99

PARAM_LIST_TARGET = parameter_list_test
PARAM_LIST_SOURCE_FILES = \
    ../parameter_list.c \
    parameter_list_test.c
PARAM_LIST_OBJECT_FILES := $(addsuffix .o,$(basename $(PARAM_LIST_SOURCE_FILES)))

TARGETS = $(PARAM_LIST_TARGET)

all: $(TARGETS)

$(%_TARGET): $(%_OBJECT_FILES)
    $(CC) $(CFLAGS) $^ -o $@

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

clean:
    $(RM) *.o $(TARGETS)

test: all
    @for t in $(TARGETS) ; do ./$$t ; done

This doesn't work, and it's because of the $(%_TARGET): row. Not surprising, I didn't expect it to work, but I hope this illustrates what I'm trying to achieve.

I want to create more chunks of the form _TARGET, _SOURCE_FILES, and _OBJECT_FILES, to test other modules besides PARAM_LIST, for example:

PARAM_LIST_TARGET = parameter_list_test
PARAM_LIST_SOURCE_FILES = \
    ../parameter_list.c \
    parameter_list_test.c
PARAM_LIST_OBJECT_FILES := $(addsuffix .o,$(basename $(PARAM_LIST_SOURCE_FILES)))

OTHER_MODULE_TARGET = other_module_test
OTHER_MODULE_SOURCE_FILES = \
    ../other_module.c \
other_module_test.c
OTHER_MODULE_OBJECT_FILES := $(addsuffix .o,$(basename $(OTHER_MODULE_SOURCE_FILES)))

I understand that % works on filenames, so attempting to use it on variables fails:

$(%_TARGET): $(%_OBJECT_FILES)
        $(CC) $(CFLAGS) $^ -o $@

How can I write a rule that matches the Makefile variables _TARGET to their associated _OBJECT_FILES, without creating one per test target?

Or more importantly, how should I do it totally differently?

Edit: I've seen this, however it seems it's only working with a single source file per executable.

Upvotes: 0

Views: 108

Answers (2)

Gauthier
Gauthier

Reputation: 41955

Defining the dependencies separately seems to work, thanks to this answer:

TARGETS = $(PARAM_LIST_TARGET) $(OTHER_MODULE_TARGET)

all: $(TARGETS)

$(PARAM_LIST_TARGET): $(PARAM_LIST_OBJECT_FILES)
$(OTHER_MODULE_TARGET): $(OTHER_MODULE_OBJECT_FILES)

$(TARGETS):
    $(CC) $(CFLAGS) $^ -o $@

This eliminates the need for a duplicate rule (one per target). Still, the definition of dependencies for each target looks like duplicates, a pattern match for these would be nice.


More than that, the OBJECT_FILES variable becomes unnecessary. This works:

PARAM_LIST_TARGET = parameter_list_test
PARAM_LIST_SOURCE_FILES = \
    ../parameter_list.c \
    parameter_list_test.c
$(PARAM_LIST_TARGET): $(addsuffix .o,$(basename $(PARAM_LIST_SOURCE_FILES))) # The dependencies directly

It would still feel nice to have this last row as one rule for all targets. Something like "for all variables ending with TARGET, build a dependency to the content of the variable with the same name, but ending with SOURCE_FILES instead".

Upvotes: 0

Vroomfondel
Vroomfondel

Reputation: 2898

You can always access make variables by constructing their names:

MY_VAR := "my var"
HIS_VAR := "his var"
HER_VAR := "her var"
CATS_VAR := "cats var"
DOGS_VAR := "dogs var"

ALL_PERSONS := MY HIS HER CATS DOGS
ALL_VARS := $(foreach p,$(ALL_PERSONS),$($(p)_VAR))

$(info $(ALL_VARS))

Output:

$ make
"my var" "his var" "her var" "cats var" "dogs var"

Upvotes: 1

Related Questions