I'm trying to understand the following target and why it's done this way:
Top-level makefile and target:
collect:
file_list="file_list.txt"
for makefile in $(ALL_PROJECTS) ; do \
$(MAKE) -s -f $$(basename $$makefile) --directory=$$(dirname $$makefile) GetDependenciesFile RELATIVE_DIR="$$(dirname $$makefile)" DEPENDENCIES_FILE="$$file_list"; \
done;
Makefile that contains the target I'm trying to understand:
.PHONY: GetDependenciesFile
$(if $(filter GetDependenciesFile, $(.OVERRIDEN)),OVERRIDEN,) GetDependenciesFile: DependenciesFile.tmp
ifeq ("$(DEPENDENCIES_FILE)","")
echo "DEPENDENCIES_FILE is not specified"
else
cat DependenciesFile.tmp >> $(DEPENDENCIES_FILE)
endif
DependenciesFile.tmp:
echo "$(ALL_DEPENDENCIES)" >> DependenciesFile.tmp
This is a simplified version of what I'm dealing with (I removed sed
and other steps that are obvious, like sorting and removing duplicates).
I understand that the goal is to traverse all makefiles contained in $(ALL_PROJECTS)
, and within each makefile append the contents of the variable $(ALL_DEPENDENCIES)
to DependenciesFile.tmp
, the contents of which then appended to $(DEPENDENCIES_FILE)
which is file_list.txt
from the top-level target.
What I don't understand is this specific line:
$(if $(filter GetDependenciesFile, $(.OVERRIDEN)),OVERRIDEN,) GetDependenciesFile: DependenciesFile.tmp
I tried to grep OVERRIDEN
in makefiles but couldn't find anything.
I don't understand what's happening when the result of if
is evaluated and why it's placed before the target name.
Any suggestions?
What I don't understand is this specific line:
$(if $(filter GetDependenciesFile, $(.OVERRIDEN)),OVERRIDEN,) GetDependenciesFile: DependenciesFile.tmp
The $(if ...)
is a GNU make
"function" that expands conditionally based on the arguments within (functions and function expansion are a GNU extension to standard make
). The syntax is:
$(if condition,then-part[,else-part])
The condition
part is considered true if stripping preceding and trailing whitespace and then expanding whatever is left results in a non-empty string. In that case, the then-part
is evaluated to produce the expansion of the if
. Otherwise, the else-part
is evaluated to produce the expansion, where not providing that part of the conditional is equivalent to providing an empty string.
In your case, the condition is $(filter GetDependenciesFile, $(.OVERRIDEN))
, the then-part is OVERRIDEN
, and the else-part is (explicitly) empty.
The $(filter ...)
is another GNU make
function. The syntax is:
$(filter pattern...,text)
, where patterns are expressed in a syntax idiosyncratic to GNU make
. The effect is to split the text
on whitespace, and expand to all of the resulting words that match any of the specified patterns, joined by whitespace.
In your case, only one pattern is given, GetDependenciesFile
. The text to be filtered is the expansion of make
variable .OVERRIDEN
. Thus, if GetDependenciesFile
appears as a whole word in the value of $(.OVERRIDEN)
then
$(filter)
expression expands to GetDependenciesFile
,$(if)
true, so$(if)
expands to OVERRIDEN
.Otherwise,
$(filter)
expands to nothing,$(if)
condition is false, so$(if)
expands to nothing.I don't understand what's happening when the result of
if
is evaluated and why it's placed before the target name.
Regular make
rules can have multiple targets, expressed as a whitespace-delimited list. The $(if)
is placed so that when it expands to anything other than whitespace, that will be interpreted as one or more additional targets for the rule.
A rule with multiple targets expresses that each specified target depends on all the specified prerequisites, and that it can be built, individually, via the given recipe. Thus, when your $(if)
expands to OVERRIDEN
, the result is that the rule for GetDependenciesFile
can also be used to build a target named OVERRIDEN
.
The recipe does not actually create a file with either name, so unless such a file is created by other means, each of those targets will always be considered out of date. My best guess is that the intent is to allow the recipe to be executed twice with some of the makefiles, once for target GetDependenciesFile
, and once for target OVERRIDEN
, whereas it is executed at most once with other makefiles. There are other possibilities related to building target OVERRIDEN
, too, depending on the circumstances under which that is done.