我有一个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/
在运行时为每个变量案例生成规则,并使用eval
和call
解析为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) $$@ $$< $$*
现在的问题是:
d3
的规则不能正确评估?如果您想重复此问题,请先运行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.
也没有帮助我找出原因。
答案 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)
令牌的一部分。)