variables - 带有变量的 Makefile 目标未按预期工作
问题描述
如果文件包含的文件尚未解压缩,我想解压文件。如果我指定没有变量 % 的目标,那么它可以工作。但是,我不确定如何使用 %. 还依赖于文件及其对应的以 .tgz 结尾的 tar 压缩文件。这是我到目前为止所拥有的。我可以做,make T2
但做不到。有人可以帮我理解我在这里做错了什么。谢谢。make T1
make T3
TAR_FILE1 := ../a_files.tgz
EXTRACT_DIR1 := ../
EXTRACT_LIST1 := \
TAR_FILE2 := ../b_files.tgz
EXTRACT_DIR2 := ../
EXTRACT_LIST2 := \
F := ../b_files/b.v
TF := ../b_files.tgz
DIR := ../
ET :=
IP_EXTRACT: $(RTL_FILES%)
$(RTL_FILES%): $(IP_TAR_FILE%)
@echo -e "\nTarget $@ with $*:"
gtar zxvfmC $(IP_TAR_FILE%) $(IP_EXTRACT_DIR%) $(IP_EXTRACT_LIST%)
$(F): $(TF)
@echo -e "\nTarget $@ with $*:"
gtar zxvfmC $(TF) $(DIR) $(ET)
$(FILES%): $(TAR_FILE%)
@echo -e "\nTarget $@ with $*:"
gtar zxvfmC $(TAR_FILE%) $(EXTRACT_DIR%) $(EXTRACT_LIST%)
FILES1 := \
../a_files/a.v \
../a_files/a1.v \
../a_files/a2.v
FILES2 := ../b_files/b.v ../b_files/b1.v ../b_files/b2.v
print-% : ; @echo $* = $($*)
T1: $(FILES%)
T2: $(F)
T3: $(FILES1)
clean:
rm -rf ../a_files ../b_files
解决方案
%
make 通配符显然不是你想的那样。在您的 Makefile 中,它几乎无处不在,是一个常规字符,是 make 变量名的一部分。例如,当你写:
$(RTL_FILES%): $(IP_TAR_FILE%)
之间的字符串()
是变量名。而且由于您可能没有定义此类变量,因此它们会扩展为空字符串。
唯一的例外是print-%
规则,它被正确地用于形成模式规则。出于同样的原因,您对$*
自动变量的所有使用都可能是错误的:它总是扩展为空字符串。此外,您的问题不完整,因为您没有解释变量RTL_FILES
, IP_TAR_FILE
, FILES
, TAR_FILE
... 是什么。
无论如何,如果您想“如果文件包含的文件尚未解压缩,则解压缩文件”,有几种可能性。以下假设您使用 GNU make 并且您的../a_files.tgz
tarball 扩展为:
a_files
├── a
└── b
└── b
它还假设您没有包含空格、制表符、特殊字符的文件或目录名称......
一个简单的解决方案
../a_files.tgz
让我们从一个简单的解决方案开始,当且仅当../a_files
目录不存在时您才想解压。否则,您认为 tarball 已被提取。
.PHONY: all
all: | ../a_files
../a_files:
tar -C .. -xvf ../a_files.tgz
请注意目标的仅订单先决条件,all
由|
. 它告诉 make 只有先决条件的存在才重要,而不是它的最后修改时间。在大多数情况下,当先决条件是目录时需要它。
示范:
$ make
tar -C .. -xvf ../a_files.tgz
a_files/
a_files/b/
a_files/b/b
a_files/a
$ make
make: Nothing to be done for 'all'.
更复杂(但更强大)的解决方案
但是,如果您不想将存在a_files
视为等同于“所有文件已被提取”怎么办?例如,如果您希望 Makefile 能够防止意外删除某些文件怎么办?
只要目录存在,上面的“简单解决方案../a_files
”就不会再次解压,即使它的完整内容已被删除:
$ rm -rf ../a_files/*
$ make
make: Nothing to be done for 'all'.
因此,一个不错的解决方案是考虑提取文件的完整列表,而不仅仅是它们的父目录。如果缺少任何一个,您将再次解压缩,否则不会。但事情有点困难,因为我们需要:
获取文件列表。您可以将列表硬连接到 Makefile 中,但这不是很方便。这个列表也可以通过查看 tarball (
tar -tf <tarball>
) 来自动计算,这要归功于 GNU makeshell
函数:LIST := $(shell tar -tf ../a_files.tgz)
告诉 make 所有这些文件都是通过一次执行
tar
配方创建的。这并不容易,因为自然的制作风格是配方的一次执行会创建一个文件。如果我们只是声明 tarball 是所有提取文件的先决条件:FILES := $(addprefix ../,$(LIST)) $(FILES): ../a_files.tgz tar -C .. -xvf $<
make 可以决定运行与
tar
要提取的文件一样多的配方。这不是我们想要的。幸运的是,具有多个目标的模式规则是一个例外,我们可以利用这一点。%
棘手的部分是我们必须在所有单词中至少替换一个常见字符$(FILES)
。让我们替换s
of../a_files/XXX
:LIST := $(patsubst a_files/%,%,$(shell tar -tf ../a_files.tgz)) FILES := $(addprefix ../a_files/,$(LIST)) PATTERN := $(addprefix ../a_file%/,$(LIST))
现在,我们的 make 变量将取值:
LIST := b/ b/b a FILES := ../a_files/b/ ../a_files/b/b ../a_files/a PATTERN := ../a_file%/b/ ../a_file%/b/b ../a_file%/a
如果我们将
$(PATTERN)
其用作规则的目标,make 将考虑所有匹配的目标,对于任何给定的相同%
替换,都是由关联配方的一次执行创建的。告诉 make 不应使用文件的最后修改时间来决定它们对于 tarball 是否是最新的。我们已经在第一个解决方案中看到的仅订购先决条件是一种方法。
总而言之,以下应该做我们想要的:
.PHONY: all
LIST := $(patsubst a_files/%,%,$(shell tar -tf ../a_files.tgz))
FILES := $(addprefix ../a_files/,$(LIST))
PATTERN := $(addprefix ../a_file%/,$(LIST))
all: $(FILES)
$(PATTERN): | ../a_files.tgz
tar -C .. -xvf ../a_files.tgz
示范:
$ make
tar -C .. -xvf ../a_files.tgz
a_files/
a_files/b/
a_files/b/b
a_files/a
$ make
make: Nothing to be done for 'all'.
$ rm ../a_files/a
$ make
tar -C .. -xvf ../a_files.tgz
a_files/
a_files/b/
a_files/b/b
a_files/a
推荐阅读
- sql-server-2019 - Synapse Analytics 与 SQL Server 2019 大数据集群
- reactjs - 我想在主页上显示一个变量
- c - 为什么这些函数具有不同的时间复杂度?
- python-3.x - Python Selenium - 从无序列表中选择列表项
- reactjs - 单击React中的另一个输入框时如何更改输入值
- javascript - 如何在赛普拉斯中存根数据库查询?
- javascript - 使用 GraalVM 代替 Nashorn Engine 来使用 Jvascript 的 eval() 函数
- jquery - 未捕获的 TypeError:$.ajax(...).error 不是 rails 中的函数
- flutter - FormatException:Flutterinsta 包中的意外字符(在字符 1 处)
- python - 根据值从嵌套字典中检索键,其中键名未知