BACKGROUND
We use Makefiles as one part of our regression suite. We have some default rules:
%.tmp_expect: ...
@echo "Rule that produces results from the current tool version"
%.tmp_expect_filter: %.tmp_expect
@echo "Take $*.tmp_expect and remove content specific to the machine/date"
%.tmp_expect_filter_sort: %.tmp_expect_filter
@echo "Rule to sort $*.tmp_expect_filter"
Then these rules are used by the top level rules:
%.do: %.tmp_expect_filter_sort
@echo "Create output using current version and diff against expected output"
@diff $< $*.expect
%.genexpect: %.tmp_expect_filter_sort
@echo "Generate 'goldstandard' expected output"
@cp $< $*.expect
The ‘genexpect’ rule is called once for a test case and it generates what we call the ‘goldstandard’ output for the test. As development continues, the ‘do’ rule will check that the current version generates the same output as the ‘goldstandard’.
All of these rules are stored in their own special file “Make.Test” and then we include that file into the Makefiles in the directory with the regression tests.
PROBLEM
Sometimes we need a slightly different output than provided by the default %.tmp_expect. We do this by having the local Makefile implement it’s own %.tmp_expect rule. This works on all platforms we’re interested in: Windows, Linux & Solaris.
If, however, we don’t want the output to be sorted, I would have thought that we could add a rule such as:
%.tmp_expect_filter_sort: %.tmp_expect_filter
@cp $< $@
This is where things become quite strange. Depending on the platform and if this rule appears before or after the inclusion of the ‘Make.Test’ file, it may or may not be selected.
So my questions are:
- How does make decide which rule to use for a given target?
- How should I write my rules so that I have control over which ones are selected?
I’ve discovered a way to do this using more recent versions of make (from version 3.81).
There is a new special target called .SECONDEXPANSION which can be used with a variable to help select between rules.
The rules can be updated to include a variable expansion as part of the name of the prerequisites:
My rough understanding of this is that after the target has been determined, the $* is replaced in the prerequisite and it is then expanded a second time.
For example, the in the target
bar.tmp_expect_filter_sort,$*is replaced withbarresulting in%.tmp_expect_filter$(bar_FILTER_RULE). Unlessbar_FILTER_RULEis actually set it will expand to nothing and the default rule will be used.To have it use a special rule, we can define the variable in our local Makefile:
The second expansion now results in
%.tmp_expect_filter_sort_customand so the custom rule will be used.