首页 > 解决方案 > 在一个规则中制作“make”更改跟踪两个目录

问题描述

我正在使用 GNU Make,但如有必要,我愿意更改为其他内容。

我有两个目录ALPHABETICNUMERIC内容如下。

./ALPHABETIC:
A.txt  B.txt  C.txt

./NUMERIC:
1.txt  2.txt  3.txt

和一个程序foo,它将一个文件ALPHABETIC和一个文件作为输入NUMERIC并输出一些东西。

我想创建一个规则,这样:如果一个文件发生c变化ALPHABETIC,重新foo运行. 如果文件更改,请重新运行. 中的每个文件。cNUMERICnNUMERICfoonALPHABETIC

例如:A.txt变化。那么应该发生的是:

foo A.txt 1.txt
foo A.txt 2.txt
foo A.txt 3.txt

让我们说现在2.txt变化。那么应该发生的是

foo A.txt 2.txt
foo B.txt 2.txt
foo C.txt 2.txt

我尝试使用模式规则执行此操作,但没有成功。

标签: makefilegnu-make

解决方案


由于 make 旨在管理从文件生成文件的构建系统,我们首先假设生成一个在主目录中foo A.txt 1.txt命名的文件。A.1.txt您可以尝试以下方法:

.PHONY: all

.DEFAULT_GOAL := all

ALPHA := $(patsubst ALPHABETIC/%.txt,%,$(wildcard ALPHABETIC/*.txt))
NUM   := $(patsubst NUMERIC/%.txt,%,$(wildcard NUMERIC/*.txt))

# $(1): ALPHA
# $(2): NUM
define ALPHANUM_rule
$(1).$(2).txt: ALPHABETIC/$(1).txt NUMERIC/$(2).txt
    @echo foo $(1).txt $(2).txt && \
    touch $$@

all: $(1).$(2).txt
endef
$(foreach a,$(ALPHA),$(foreach n,$(NUM),$(eval $(call ALPHANUM_rule,$(a),$(n)))))

注意使用echoandtouch来模拟你的真实foo命令的效果。最重要的是要理解foreach-eval-call结构,包括为什么某些$符号需要加倍($$)。有关详细说明,请参阅 GNU make 手册中的eval函数

演示(host>是shell提示符):

host> make
foo B.txt 2.txt
foo B.txt 3.txt
foo B.txt 1.txt
foo A.txt 2.txt
foo A.txt 3.txt
foo A.txt 1.txt
foo C.txt 2.txt
foo C.txt 3.txt
foo C.txt 1.txt
host> make
make: Nothing to be done for 'all'.
host> touch NUMERIC/1.txt 
host> make
foo B.txt 1.txt
foo A.txt 1.txt
foo C.txt 1.txt
host> make
make: Nothing to be done for 'all'.
host> touch ALPHABETIC/C.txt
host> make
foo C.txt 2.txt
foo C.txt 3.txt
foo C.txt 1.txt
host> make
make: Nothing to be done for 'all'.

但是如果foo A.txt 1.txt不生成文件怎么办?在这种情况下,最简单的方法就是生成空文件。我们将在单独的目录中创建它们以便于清理:

.PHONY: all

.DEFAULT_GOAL := all

ALPHA   := $(patsubst ALPHABETIC/%.txt,%,$(wildcard ALPHABETIC/*.txt))
NUM     := $(patsubst NUMERIC/%.txt,%,$(wildcard NUMERIC/*.txt))
TAGSDIR := tags

$(TAGSDIR):
    @mkdir -p $@

# $(1): ALPHA
# $(2): NUM
define ALPHANUM_rule
$(TAGSDIR)/$(1).$(2).txt: ALPHABETIC/$(1).txt NUMERIC/$(2).txt | $(TAGSDIR)
    @echo foo $(1).txt $(2).txt && \
    touch $$@

all: $(TAGSDIR)/$(1).$(2).txt
endef
$(foreach a,$(ALPHA),$(foreach n,$(NUM),$(eval $(call ALPHANUM_rule,$(a),$(n)))))

还有两点需要理解:

  • 使用空文件 ( $(TAGSDIR)/A.1.txt...) 来跟踪已经完成的内容和时间。
  • Order-only 先决条件 用于保证在$(TAGSDIR)使用之前创建包含空文件的目录,而不会在每次修改其内容时强制重新构建。

推荐阅读