Having trouble understanding $$$$(dir $$$$#) in Makefile using eval - makefile

The Makefile template is as follows:
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
define cc_template
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$#)
#$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$#) $$#"> $$#
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$#)
#echo + cc $$<
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$#
ALLOBJS += $$(call toobj,$(1),$(4))
endef
# compile file: (#files, cc[, flags, dir])
define do_cc_compile
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4))))
endef
I know eval will expand twice, so we should use another $. But why should we use $$$$(dir $$$$#) here? I have tried to understand this, but I failed.

When make parses the file, it expands. If it sees $$ it expands it to $. So $$$$ will expand to $$ (two dollars). The eval, once applied will want to expand that once more to $. Thus you need four dollars (note $$$ would expand to $ followed by the expansion of $(, or whatever the next character happens to be, which would likley result in an error)

Related

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.
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"
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".

Custom Make function doesn't get parameter

I want to add Modules to my build system.
To keep my makefile clean when adding new modules, they all follow the same pattern, so I tried to generalize it with a function:
uc = $(shell echo $1 | tr '[a-z]' '[A-Z]')
define driver-mod
$(eval CFLAGS += -DUSE_$(call uc, $1));
$(eval include $(DRIVERS_SRC)/$1/Makefile.include);
endef
ifneq (,$(filter led,$(USEMODULE)))
$(call driver-mod, led)
endif
ifneq (,$(filter uart,$(USEMODULE)))
$(call driver-mod, uart)
endif
ifneq (,$(filter button,$(USEMODULE)))
$(call driver-mod, button)
endif
(the ifneq is going to be replaced with a $(foreach x, $(USEMODULE), $(call driver-mod, $(x))
However, it seems like $1 in driver-mod is not evaluated, I get
make: *** $(DRIVERS_SRC): Is a directory. Stop.
(doesn't actually output $(DRIVERS_SRC) but it's value, edited for clarity)
When I replace the $1 with e.g. led, it works as expected.
What am I missing?
Turns out I have to escape the $ for eval:
define driver-mod
$(eval CFLAGS += -DUSE_$(call uc, $1));
$(eval include $(DRIVERS_SRC)/\$1/Makefile.include);
endef
works!
Can be simplified as follows:
uc = $(shell echo $1 | tr '[a-z]' '[A-Z]')
define __driver-mod
CFLAGS += -DUSE_$(uc)
include $(DRIVERS_SRC)/$1/Makefile.include
endef
driver-mod = $(eval $(call __driver-mod,$(strip $1)))
$(foreach 1,$(USEMODULE),$(driver-mod))

Makefile macro to generate rules

The following makefile is an example. I'm trying to generate the rules for building targets using a defined macro.
What I get is
make: *** No rule to make target `../x86_64/lib-reloc-debug/libstats.tar', needed by `all'. Stop.
sources=$(wildcard $1/*.cpp)
MODE_SUFFIX=-debug
M=../x86_64/lib$(MODE_SUFFIX)
L=../x86_64/lib-reloc$(MODE_SUFFIX)
dir_c_objects=$(patsubst %.c,%.o,$(wildcard $1/*.c))
dir_cpp_objects=$(patsubst %.cpp,%.o,$(wildcard $1/*.cpp))
dir_objects=$(filter-out $1/test%, $(call dir_c_objects, $1) $(call dir_cpp_objects, $1))
dir_objects_with_tests=$(call dir_c_objects, $1) $(call dir_cpp_objects, $1)
libobjdir=$(subst lib,obj,$(dir $1))
l_prefix=$(foreach file,$(1),$(addprefix $L/, $(file))))
TRACE=$(if $(VERBOSE),,#)
define my_make_archive
$(eval name:=$(strip $1))
$(eval reqs:=$2)
$(eval L_sources:=$(call sources, $(name)))
$(eval objects:=$(call dir_objects, $(name)))
$(eval L_objects:=$(foreach file,$(objects),$(addprefix $L/, $(file))))
$(eval M_objects:=$(foreach file,$(objects),$(addprefix $M/, $(file))))
$(eval L_reqs:=$(foreach file,$(reqs),$(addprefix $L/, $(file))))
$(eval M_reqs:=$(foreach file,$(reqs),$(addprefix $M/, $(file))))
$L/lib$(name).tar: $$(L_sources) $(L_reqs)
tar xvf $L/lib$(name).tar: $(L_sources) $(L_reqs)
endef
$(call my_make_archive, stats,)
all: $L/libstats.tar
echo foo
I was not able to find any good examples of using defines to generate rules.
You're making this much too complicated, and your coding is way out ahead of your testing. Try this:
define my_make_archive
name:=$(strip $1)
reqs:=$(2)
L_sources:=$(name).cpp
L_reqs=$$(addprefix $$L/,$$(reqs))
$(L)/lib$(name).tar: $$(L_sources) $$(L_reqs)
tar xvf $$# $$^
endef
all: $L/libstats.tar
echo foo
$(eval $(call my_make_archive, stats))

Targets and dependencies in Makefiles compilation

I came across a makefile which contained the below code. However i am not able to understand the first line $(OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp, what are the dependencies exactly?
$(OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp
#$(PRINTF) "$(MESG_COLOR)Compiling: $(NO_COLOR) $(FILE_COLOR) %25s$(NO_COLOR)" "$(notdir $<)"
#$(CC) $(CPPFLAGS) -c $< -o $# -MD 2> temp.log || touch temp.err
#if test -e temp.err; \
then $(PRINTF) $(ERR_FMT) $(ERR_STRING) && $(CAT) temp.log; \
elif test -s temp.log; \
then $(PRINTF) $(WARN_FMT) $(WARN_STRING) && $(CAT) temp.log; \
else printf "${OK_COLOR}%30s\n${NO_COLOR}" "[OK]"; \
fi;
#$(RM) -f temp.log temp.err
This is a static pattern rule. It can be used to build any of the targets in $(OBJS), and constructs the names of the prerequisite(s) accordingly.
SRCDIR = sources
OBJDIR = objects
OBJS = objects/foo.o objects/bar.o objects/baz.o
$(OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp
#echo the target is $#, the prereq is $<
If you call this rule with "make objects/foo.o", Make will 1) recognize that this rule applies, since the desired target is a member of the rule's list of targets, 2) match the target name "objects/foo.o" against the target pattern "objects/%.o" to obtain the stem "foo", 3) put that stem into the prereq pattern "sources/%.cpp" to obtain the name of the prereq, "sources/foo.cpp".
This is a static pattern rule. Basically it means "for each word in $(OBJS), define an explicit rule where the target pattern $(OBJDIR)/%.o matches the word and the prerequisite is the expansion of the pattern $(SRCDIR)/%.cpp".
So if OBJS is equal to $(OBJDIR)/foo.o $(OBJDIR)/bar.o $(OBJDIR)/baz.o, then the static pattern rule is equivalent to writing this:
$(OBJDIR)/foo.o : $(SRCDIR)/foo.cpp
#$(PRINTF) ...
...
$(OBJDIR)/bar.o : $(SRCDIR)/bar.cpp
#$(PRINTF) ...
...
$(OBJDIR)/baz.o : $(SRCDIR)/baz.cpp
#$(PRINTF) ...
...

View end result of Makefile "expansion"

I am trying to understand what the end product of this Makefile which uses eval. Is there a way to view the effective end product of what the Makefile looks like as if eval weren't used?
VALID_TOOLCHAINS := newlib glibc pnacl
NACL_SDK_ROOT ?= $(abspath $(CURDIR)/../../..)
include $(NACL_SDK_ROOT)/tools/common.mk
TARGET = nacl_io
DEPS = nacl_io
LIBS = $(DEPS) ppapi pthread
CFLAGS = -Wall
SOURCES = handlers.c \
nacl_io_demo.c \
queue.c
$(foreach dep,$(DEPS),$(eval $(call DEPEND_RULE,$(dep))))
$(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS))))
ifeq ($(CONFIG),Release)
$(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS)))
$(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped))
else
$(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS)))
endif
$(eval $(call NMF_RULE,$(TARGET),))
Yes, I know of two ways:
replace eval with info
run make -p and find your rules explicitly written in the output of that

Resources