How can I create make pattern rule that can expand the target as a dependency? - gnu-make

I'm trying to create a makefile that has rule something like:
~/$ cat > Makefile << EOF
FILES=a b c
$(FILES): % : src/%/%
#echo "$# $<"
EOF
Running the command gives:
~/$ make a
make: *** No rule to make target `src/a/%', needed by `a'. Stop.
And what I'd like to see is:
~/$ make a
a src/a/a
How do a create rule to expand the second %?

In this case,
secondary expansion
might help.
For example:
FILES=a b c
.SECONDEXPANSION:
$(FILES): % : src/$$*/$$*
#echo "$# $<"
If your GNU make's version is 3.80 or lower, $* might not work.
In that case, $# and some text manipulation might be needed instead.

Related

Makefile, iterate over dirs and exclude some

In a toplevel (unix, GNU) Makefile I want to iterate over a list of subdirs and execute tasks in there but exclude some of them. In this case exclude all subdirs starting with an underscore (_)
The following is working in iterating but ignores the exclusion regex. It still visits dir "_exclude".
In particular I think it ignores the "start of string" ^ (or it takes it literally). I have tried other regexes and they do work. Any idea how to fix that? Or indeed if you have any idea how to eliminate the shelling out or not using GNU makefile extensions?
SHELL=/bin/bash
EXCLUDE_DIRS_REGEX=^_
# later addition:
ALLMAKEFILES = $(shell find . -maxdepth 2 -type f -name Makefile)
SUBDIRS = $(filter-out ./,$(dir $(ALLMAKEFILES)))
all:
for dir in $(SUBDIRS); do \
if [[ "$$dir" =~ $(EXCLUDE_DIRS_REGEX) ]]; then continue; fi; \
make -C $$dir all; \
done
You have two problems to solve:
compute the list of target directories
call make in each of them
For the first one a mixture of make and shell functions or built-ins could be something like:
REGEX := ^_
ALLMAKEDIRS := $(patsubst %/,%,$(dir $(wildcard */Makefile)))
SUBDIRS := $(shell for d in $(ALLMAKEDIRS); \
do ! [[ "$$d" =~ $(REGEX) ]] && echo "$$d"; done)
I kept the REGEX just in case you have more complex regular expressions or you would like it to be easily modified. But of course if it is exactly ^_ and you never change it the following is simpler:
SUBDIRS := $(filter-out _%,$(patsubst %/,%,$(dir $(wildcard */Makefile))))
For the second one your approach is not very make-ish. A better approach would be to have one rule per subdirectory. The following is an example:
.PHONY: all $(SUBDIRS)
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# all
It has several advantages:
there is no need to loop over subdirectories in a complicated recipe, make will do it for you,
make can launch several jobs in parallel (see the -j option) and this will speed-up your build.
Note: calling make in a Makefile is not recommended. Prefer $(MAKE). See this section of the manual for the details.

Can a rule be declared in a define in gnu-make?

I thought that I could use the define directive in make to make multiple alike rules
define TESTPRG
${1}:
echo Build ${1}
endef
$(call TESTPRG,x)
But I get this output
$ make
make: *** No rule to make target 'echo', needed by '
x'. Stop.
It looks like the newline in my define is lost
$ make -n -p | grep echo
make: *** No rule to make target 'echo', needed by '
x'. Stop.
echo Build ${1}
x: echo Build x
echo:
Am I just trying to do something that the define is not supposed to be used for.
Kjeld
This is exactly what define is supposed to be used for. You just need to remember to $(eval) it as a part of Makefile:
$ cat Makefile
define TESTPRG
${1}:
echo Build ${1}
endef
$(eval $(call TESTPRG,x))
$ make
echo Build x
Build x

Set a variable inside prerequisites or eval function

I have this code, and it works, but as you see I do the substitute three times, I would like to set a variable to the value, but with no success
$($(PKG)-py-valgrind-tests-status): $($(PKG)-swig-dlib)
$($(PKG)-py-valgrind-tests-status): $(OBJ_OUTPUT_DIR)%.valgrind_passed: %.py
#echo env $(PKG-TEST-HELPER-ENV) valgrind $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v ; \
env $(PKG-TEST-HELPER-ENV) $(VALGRIND) $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v \
|| (cat $(subst valgrind_passed,valgrind.log,$#); exit 1)
#touch $#
the problematic line $(subst valgrind_passed,valgrind.log,$#)
I tried:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $(subst valgrind_passed,valgrind.log,$#)
and
.SECONDEXPANSION:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#)
and(inside the recipe)
$(eval LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#))
but for all, if I write
--log-file=$(LOG-FILE)
log file come's up empty.
I have no more ideas on how to go forward,
appreciate the help, thanks!
Are you sure you're using GNU make? What version are you using (run make --version)?
There's nothing wrong with your first attempt, using a target-specific variable. If this doesn't work then you've got something wrong or different about your makefile that you haven't explained. Maybe if you showed the actual complete section of the makefile with the target-specific variable being set and used we might see what's wrong.
Your second attempt can't work because secondary expansion applies only the prerequisites, not target-specific variables (but, as above, it's not needed anyway).
Your third attempt might work but again, without seeing exactly what you do with the eval we can't say for sure. You don't need to double the $ before the subst function; it can be expanded first and it will still work.

Autodependency generation in makefiles

I am trying to understand how autodependency is generated in makefiles in the given link, i cannot understand the following piece of code:
DEPDIR = .deps
df = $(DEPDIR)/$(*F)
SRCS = foo.c bar.c ...
%.o : %.c
#$(MAKEDEPEND); \
cp $(df).d $(df).P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
rm -f $(df).d
$(COMPILE.c) -o $# $<
-include $(SRCS:%.c=$(DEPDIR)/%.P)
I got it from this link. I know it will generate dependency files but I am not able to understand what this line does:
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
Can somebody please explain me this code, so many wildcards give me butterflies, i am new to makefiles.
That is a number of distinct commands so break it up.
-e 's/#.*//'
Delete everything that starts with a # (Comments? Pre-processor directives?)
-e 's/^[^:]*: *//'
Delete everything up to a : on any that has : it.
-e 's/ *\\$$//'
Delete line-continuation slashes (and spaces before them) from the end of lines.
-e '/^$$/ d'
Delete any blank lines.
-e 's/$$/ :/'
Add : to the end of every line.
This adds explicit targets for every listed dependency file so that make "knows" how to build them to avoid the "No rule to make target" errors. The reasoning here is explained in your link in an earlier section.
Briefly, this creates a .P file with the original prerequisites list, then adds targets to it by taking each line, removing any existing target information and any line continuation (\) characters, then adding a target separator (:) to the end. This works with the values for MAKEDEPEND I suggest below; it’s possible you will need to modify the translation for other dependency generators you might use.
This is not meant to be an answer to your actual question, but since you said you were new to GNU make, I think spreading the words that a simpler way to handle auto dependencies exists won't do any harm.
Nowadays compilers like GCC or Clang can do this for you while compiling your code !
Simply pass them a preprocessor flag:
# Preprocessor flags
CPPFLAGS += -MMD
And include the generated files into the Makefile:
-include $(wildcard *.d)
And you're done.
You can learn more about the preprocessor options here for GCC, Clang simply mirror these options.
A relatively good example lies here too.

Call command in Unix Make

I'm trying to understand a makefile, can anyone tell me what the following line does:
#echo cp -f --preserve=mode,timestamps $(call $1,$<) $(call $1,$#)
Especially I don't get what is the significance of $1 and call here.
There's no way to know what this does, since it's completely out of context.
It looks to me like this value is supposed to be passed to another instance of $(call ...). So for example if your makefile has:
QUOTE = '$1'
COPY = #echo cp -f --preserve=mode,timestamps $(call $1,$<) $(call $1,$#)
then later you would see something like:
foo: bar ; $(call COPY,QUOTE)
The first call would expand to the COPY value with $1 replaced with QUOTE, so it would be:
#echo cp -f --preserve=mode,timestamps $(call QUOTE,bar) $(call QUOTE,foo)
then that gets expanded, and you end up with:
#echo cp -f --preserve=mode,timestamps 'bar' 'foo'
But without more information we can't say more.
The 'call' command is a GNU extension in GNUmake; it is not supported in POSIX make or most other makes. It basically expands a macro with arguments. Something like
$(call A,b,c,d)
will expand the macro A with the arguments b, c, and d. The arguments are assigned to the temporary macros $(1), $(2), ... which may be present in the definition of A
See the GNUmake documentation

Resources