目录树中具有多个依赖项的 Makefile
Makefile with multiple dependencies in directory tree
即使有很多这方面的主题,我也找不到解决方案。
我想在 GNU make Makefile 中结合两种技术,以使用文件从目录树中创建几个 Debian 软件包:
- 根据相应目录中的文件树创建 Debian 软件包,例如a.deb 依赖于目录 a/ 内的所有文件,所以如果 a/foo/bar 被更改,将生成 a.deb
- 对具有相同构建规则和操作的多个包执行此操作,例如目标 a.deb 取决于目录 a/ 中的文件树,b.deb 来自 b/ 直到 z.deb 来自 z/
以下看起来不错,但没有找到先决条件文件:
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
%.deb: $(shell find % -type f)
$(TARGETS):
@echo "#### Building $@ due to \"$?\" ####"
仅出于测试目的,我用简单的 "echo" 而不是 "find" 测试了规则,这工作正常(但当然只依赖于目录中的目标,而不是完整的文件树):
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
%.deb: $(shell echo %/ )
$(TARGETS):
@echo "#### Building $@ due to \"$?\" ####"
如果我将目标文件名硬编码到 "find" 内的规则中,它会起作用:
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
a.deb: $(shell find a/ -type f)
b.deb: $(shell find b/ -type f)
z.deb: $(shell find z/ -type f)
$(TARGETS):
@echo "#### Building $@ due to \"$?\" ####"
但现在我必须维护包名称的更改或在文件中的 3 个位置添加新包以保持一致。
维护makefile既烦人又危险,有没有更好的办法?
`
您的解决方案揭示了对 make 如何计算变量和函数的误解。您必须了解 make 分两个不同的步骤进行:首先,它解析所有的 makefile 并为已定义的变量和规则构建一个内部数据库。其次,在解析所有 makefile 后,它会遍历相关目标并尝试构建过时的目标。重要的是,规则的 target 和 prerequisite 部分中的所有变量和函数扩展都发生在第一步:解析 makefile。另一方面,模式直到第二步才会扩展:构建目标(因为直到我们尝试构建目标,我们才真正可以将模式与我们想要构建的内容相匹配)。
所以,这个:
%.deb: $(shell find % -type f)
将 运行 文字 shell 函数 find % -type f
,然后使用该函数的输出作为模式 %.dep
的先决条件列表。由于您几乎可以肯定没有任何文件或目录的字面意思是 %
,此查找命令的结果是空的,并且没有定义先决条件。
同时这:
%.deb: $(shell echo %/ )
只是一种非常慢、性能很差的写入方式:
%.deb: %/
为什么调用 shell 只是为了回显静态字符串?
要完成这项工作,您需要使用 metaprogramming. For example, you can use secondary expansion:
的某些方面
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): $$(shell find $$(patsubst %.deb,%,$$@)/ -type f)
@echo "#### Building $@ due to \"$?\" ####"
即使有很多这方面的主题,我也找不到解决方案。 我想在 GNU make Makefile 中结合两种技术,以使用文件从目录树中创建几个 Debian 软件包:
- 根据相应目录中的文件树创建 Debian 软件包,例如a.deb 依赖于目录 a/ 内的所有文件,所以如果 a/foo/bar 被更改,将生成 a.deb
- 对具有相同构建规则和操作的多个包执行此操作,例如目标 a.deb 取决于目录 a/ 中的文件树,b.deb 来自 b/ 直到 z.deb 来自 z/
以下看起来不错,但没有找到先决条件文件:
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
%.deb: $(shell find % -type f)
$(TARGETS):
@echo "#### Building $@ due to \"$?\" ####"
仅出于测试目的,我用简单的 "echo" 而不是 "find" 测试了规则,这工作正常(但当然只依赖于目录中的目标,而不是完整的文件树):
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
%.deb: $(shell echo %/ )
$(TARGETS):
@echo "#### Building $@ due to \"$?\" ####"
如果我将目标文件名硬编码到 "find" 内的规则中,它会起作用:
TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
a.deb: $(shell find a/ -type f)
b.deb: $(shell find b/ -type f)
z.deb: $(shell find z/ -type f)
$(TARGETS):
@echo "#### Building $@ due to \"$?\" ####"
但现在我必须维护包名称的更改或在文件中的 3 个位置添加新包以保持一致。
维护makefile既烦人又危险,有没有更好的办法?
`
您的解决方案揭示了对 make 如何计算变量和函数的误解。您必须了解 make 分两个不同的步骤进行:首先,它解析所有的 makefile 并为已定义的变量和规则构建一个内部数据库。其次,在解析所有 makefile 后,它会遍历相关目标并尝试构建过时的目标。重要的是,规则的 target 和 prerequisite 部分中的所有变量和函数扩展都发生在第一步:解析 makefile。另一方面,模式直到第二步才会扩展:构建目标(因为直到我们尝试构建目标,我们才真正可以将模式与我们想要构建的内容相匹配)。
所以,这个:
%.deb: $(shell find % -type f)
将 运行 文字 shell 函数 find % -type f
,然后使用该函数的输出作为模式 %.dep
的先决条件列表。由于您几乎可以肯定没有任何文件或目录的字面意思是 %
,此查找命令的结果是空的,并且没有定义先决条件。
同时这:
%.deb: $(shell echo %/ )
只是一种非常慢、性能很差的写入方式:
%.deb: %/
为什么调用 shell 只是为了回显静态字符串?
要完成这项工作,您需要使用 metaprogramming. For example, you can use secondary expansion:
的某些方面TARGETS = a.deb b.deb z.deb
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): $$(shell find $$(patsubst %.deb,%,$$@)/ -type f)
@echo "#### Building $@ due to \"$?\" ####"