我有一个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
。
请注意以下几行:
$(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.
当我运行make with时,-pRr
我看到以下规则的输出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.
都没有帮助我找出原因。
我必须承认,我从来没有真正理解过更高级的make
操作功能所依据的确切规则(因为当我开始对它们感兴趣时,我改用了CMake)。
因此,我无法为您提供详尽的“为什么”(发生这种情况),而只能为您提供“如何”(解决此问题)。
不分配DIR = $(1)
,$(1)
直接使用:
define AddRule
$(1)/%.txt: %.tar
@mkdir -p $(1)
@tar -xvf $$< > /dev/null
@mv $$*.txt $(1)
endef
$(foreach _, $(DIRS), $(eval $(call AddRule, $_)))
这按预期工作。我只能假定本地分配不能与s一起使用,但是make
可以处理define
s。
正如@ user657267在评论中指出的那样:
DIR = $(1)
(或更确切地说$(DIR)
)失败的原因是因为在内容作为makefile语法eval
扩展之前先扩展所有内容。替换$(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)
令牌的一部分。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句