使用eval时,Makefile规则不是目标

时间:2016-02-18 08:45:08

标签: makefile gnu-make

我有一个makefile,它将tar文件提取到运行时确定的可变数量的文件夹中(在下面的示例中,这是硬编码的):

firstTarget: start
.PHONY: start

DIRS = d1 d2 d3

TAR_FILES = $(wildcard ?.tar)
TAR_FILE_NAMES := $(TAR_FILES:%.tar=%)
FILES = $(foreach _, $(DIRS), $(TAR_FILE_NAMES:%=$_/%.txt))

define AddRule
DIR = $(1)
$(DIR)/%.txt: %.tar
    @echo $(1) $(DIR) $$@ $$< $$*
    @mkdir -p $(DIR)
    @tar -xvf $$< > /dev/null
    @mv $$*.txt $(DIR)
endef
$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))
#$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))

start: $(FILES)
    @echo "Finished"

setup:
    @touch a.txt
    @tar -cvf a.tar a.txt > /dev/null
    @rm a.txt

clean:
    @rm -rf d1/ d2/ d3/

在运行时为每个变量案例生成规则,并使用evalcall解析为make指令。

请注意以下几行:

$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))
#$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))

当第二行被注释掉时,我得到以下输出和错误:

d2 d1 d1/a.txt a.tar a
d3 d2 d2/a.txt a.tar a
make: *** No rule to make target 'd3/a.txt', needed by 'start'.  Stop.

当我使用-pRr生成make时,我看到d3/a.txt规则的以下输出:

# Not a target:
d3/a.txt:
#  Implicit rule search has been done.
#  File does not exist.
#  File has not been updated.

将此与d1/a.txt的规则进行比较:

d1/a.txt: a.tar
#  Implicit rule search has been done.
#  Implicit/static pattern stem: 'a'
#  Last modified 2016-02-18 09:36:24
#  File has been updated.
#  Successfully updated.
# automatic
# @ := d1/a.txt
# automatic
# % := 
# automatic
# * := a
# automatic
# + := a.tar
# automatic
# | := 
# automatic
# < := a.tar
# automatic
# ^ := a.tar
# automatic
# ? := a.tar
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=1/25=4%
#  recipe to execute (from 'Makefile', line 18):
    @echo  d2 d1 $@ $< $*
    @mkdir -p d1
    @tar -xvf $< > /dev/null
    @mv $*.txt d1

添加第二行,意味着每个规则被调用和评估两次,它可以正常工作:

d2 d1 d1/a.txt a.tar a
d3 d2 d2/a.txt a.tar a
d1 d3 d3/a.txt a.tar a
Finished

通过d3/a.txt查看时,d1/a.txt的规则也类似于make -pRr的上述规则。

值得注意的是,在规则中我输出的内容如下:

@echo $(1) $(DIR) $$@ $$< $$*

现在的问题是:

  1. 为什么d3的规则不能正确评估?
  2. 为什么$(1)在规则中不等于$(DIR)?这很奇怪,因为$(1)被分配给$(DIR)。
  3. 为什么调用和评估规则会使其正常工作?
  4. 如果您想重复此问题,请先运行make setup,然后make

    更新1:18/02/2016

    @DevSolar的答案解决了上述问题,但让我意识到我的测试用例并不是真正问题的完美表现。在实际问题中,动态规则的参数不在目标的开头:

    FILES = $(foreach _, $(DIRS), $(TAR_FILE_NAMES:%=somedir/$_/%.txt))
    
    define AddRule
    somedir/$(1)/%.txt: %.tar
        @echo $(1) $$@ $$< $$*
        @mkdir -p somedir/$(1)
        @tar -xvf $$< > /dev/null
        @mv $$*.txt $(1)
    endef
    

    请注意,目标现在为somedir/$(1)/%.txt: %.tar。这导致make 3.81中出现以下错误:

    Makefile:17: warning: overriding commands for target `somedir'
    Makefile:17: warning: ignoring old commands for target `somedir'
    Makefile:17: warning: overriding commands for target `somedir'
    Makefile:17: warning: ignoring old commands for target `somedir'
    

    有趣的是,make 4.1还有其他话要说:

    Makefile:17: *** mixed implicit and normal rules: deprecated syntax
    Makefile:17: warning: overriding recipe for target 'somedir/'
    Makefile:17: warning: ignoring old recipe for target 'somedir/'
    Makefile:17: *** mixed implicit and normal rules: deprecated syntax
    Makefile:17: warning: overriding recipe for target 'somedir/'
    Makefile:17: warning: ignoring old recipe for target 'somedir/'
    Makefile:17: *** mixed implicit and normal rules: deprecated syntax
    make: *** No rule to make target 'somedir/d1/a.txt', needed by 'start'.  Stop.
    

    也没有帮助我找出原因。

1 个答案:

答案 0 :(得分:2)

我必须承认,我从未真正理解make更高级功能的确切规则(因为当我开始对它们感兴趣时,我转而使用CMake )。

所以我无法为您提供精心设计的&#34;为什么&#34; (这是否会发生),但只有&#34;如何&#34; (我解决这个问题。)

不要指定DIR = $(1),直接使用$(1)

define AddRule
$(1)/%.txt: %.tar
    @mkdir -p $(1)
    @tar -xvf $$< > /dev/null
    @mv $$*.txt $(1)
endef
$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))

这可以按预期工作。我只能假设本地作业不能同时使用,make会处理define

  

正如@ user657267在评论中指出的那样:

     

DIR = $(1)(或更准确地说$(DIR))失败的原因是因为eval在内容扩展为makefile语法之前扩展所有内容。用$(DIR)替换$$(DIR)也可以解决问题。

<强>更新

通过查看$(1)define实际解析的内容来解决您的更新问题:

define AddRule
$(warning '$(1)')
...

输出:

Makefile:21: ' d1'
Makefile:21: ' d2'
Makefile:21: ' d3'

罪魁祸首:

$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))
                                            ^

解决方案:

$(foreach _, $(DIRS), $(eval $(call AddRule,$_)))

(请注意$_之前删除的空格,该空格成为$(1)令牌的一部分。)