1

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:

  1. How does make decide which rule to use for a given target?
  2. How should I write my rules so that I have control over which ones are selected?
4

2 回答 2

2

1) 从GNU Make 手册中,如果多个模式规则与目标匹配(并且它们匹配的部分长度相同),Make 将选择第一个出现的规则。因此,如果您include Make.test在特殊规则之前,则该Make.test规则将优先。

我不知道为什么您的结果将取决于平台,除非您的平台具有不同版本的 Make。

2)手册提出了一种我称之为“粗鲁但有效”的方法;而不是include Make.test,添加此规则:

%: force
         @$(MAKE) -f Make.test $@
force: ;

换句话说,当且仅当没有针对特定目标的本地规则时,使用 重新调用 Make Make.test。是的,这是Recursive Make;不,它不会杀了你,它只是有点效率低下。

于 2012-05-01T13:56:16.897 回答
1

我发现了一种使用最新版本的 make (从版本 3.81 开始)来执行此操作的方法。

有一个名为.SECONDEPANSION的新特殊目标可与变量一起使用以帮助在规则之间进行选择。

可以更新规则以包括变量扩展作为先决条件名称的一部分:

%.tmp_expect_filter: %.tmp_expect
    @echo "Take $*.tmp_expect and remove content specific to the machine/date"


.SECONDEXPANSION:
%.tmp_expect_filter_sort: %.tmp_expect_filter$$($$*_FILTER_RULE)
    @cat '$<' | sort -n -t: -k 2 -k 3 > '$@'

我对此的粗略理解是,目标确定后,先决条件中的$*被替换,然后第二次扩展。

例如,目标中的bar.tmp_expect_filter_sort,$*被替换为bar结果%.tmp_expect_filter$(bar_FILTER_RULE)。除非bar_FILTER_RULE实际设置,否则它将扩展为空,并且将使用默认规则。

要让它使用特殊规则,我们可以在本地 Makefile 中定义变量:

bar_FILTER_RULE=_custom
%.tmp_expect_filter_sort_custom:
    @echo My custom rule here....

第二个扩展现在会产生结果,%.tmp_expect_filter_sort_custom因此将使用自定义规则。

于 2012-05-03T15:22:12.610 回答