gpt4 book ai didi

makefile - GNU 使 : obtain list of primary prerequisites of a rule

转载 作者:行者123 更新时间:2023-12-05 05:26:12 25 4
gpt4 key购买 nike

考虑以下 Makefile:

.SUFFIXES:
.SUFFIXES: .c.o

.PHONY: all

all: foo.o

foo.o: foo.h bar.h xyzzy.h

%.o: %.c
@printf "prerequisites of %s are %s\n" $@ "$^"

除了foo.o,所有文件都存在,输出为:

prerequisites of foo.o are foo.c foo.h bar.h xyzzy.h

正确地,自动变量 $^ 为我们提供了所有先决条件,包括从其他规则中规定的依赖项中获得的条件。

让我们将规则本身给出的先决条件称为主要先决条件,并将来自其他依赖项的先决条件称为次要先决条件

以上,主要先决条件是:

foo.c

次要的是:

foo.h bar.h xyzzy.h

类别很重要,因为主要先决条件是规则实际使用的对象,构建程序需要这些对象。次要先决条件仅涉及增量构建的正确触发,而不涉及完整构建。即使我们删除依赖行,从头开始的完整构建也可以工作:

foo.o: foo.h bar.h xyzzy.h

这反射(reflect)在我们的 Makefile 结构中。我们通常不会使用如下规则编写 Makefiles:

foo.o: foo.c foo.h bar.h xyzzy.h
# commands

foo.c 之后的额外先决条件在其他地方被分解出来,通常是一个完全独立的依赖 makefile,由工具生成,并且可以完全删除,而不会影响执行完整操作的能力从头开始构建。

问题是:我们如何才能只获得主要先决条件的列表,而不包括次要先决条件?

这应该可以通过通用方式实现,无需任何硬编码。例如,如果我将一些食谱行定义为宏,它们可以在多个规则中重复使用。

define RULE_BODY
@printf "the primary prerequisites of target %s are %s\n" $@ [what goes here?]
endef

%.o: %.c
$(call RULE_BODY)

我不想将参数传递给 RULE_BODY,因为它应该“只知道”,就像它知道目标和全部先决条件一样。

请注意,模式规则的使用是转移注意力:我们可以将 %.o: %.c 替换为 foo.o: foo.c

最佳答案

一个可能的解决方案是添加一个中间依赖节点,它捕获次要先决条件,并将它们表示为单个先决条件。虚假先决条件具有某种可识别的词汇形式,可以根据该形式将其过滤掉:

概念验证,紧密基于问题中的 Makefile:

.SUFFIXES:
.SUFFIXES: .c.o

all: foo.o

secondary_foo.o: foo.h bar.h xyzzy.h
echo $^ > $@

foo.o: secondary_foo.o

define RULE_BODY
@printf "prerequisites of %s are %s\n" $@ "$^"
@printf "primary prerequisites of %s are %s\n" $@ "$(filter-out secondary_$@,$^)"
@printf "secondary prerequisites of %s are %s\n" $@ "$(shell cat secondary_$@)"
endef

%.o: %.c
$(call RULE_BODY)
touch $@

输出:

prerequisites of foo.o are foo.c secondary_foo.o
primary prerequisites of foo.o are foo.c
secondary prerequisites of foo.o are foo.h bar.h xyzzy.h
touch foo.o

不幸的是,构建目录中散落着这些中间文件。即使以其他方式处理次要先决条件的传播,secondary_foo.o 文件仍然不能成为虚假目标;至少它必须是一个空的时间戳文件。


以下替代解决方案更为复杂,需要计算变量、eval,并使用技巧将依赖项存储在用于生成规则的变量中。但是,它的优点是不会生成时间戳文件的激增。

.SUFFIXES:
.SUFFIXES: .c.o

OBJS := foo.o bar.o

all: $(OBJS)

# These variables give secondary dependencies for the objectg files,
# in place of rules. These would typeically be "farmed out" to
# a machine-generated dependency makefile which is included:
DEP_foo.o := foo.h bar.h xyzzy.h
DEP_bar.o := bar.h xyzzy.h

define RULE_BODY
@printf "\n"
@printf "prerequisites of %s are %s\n" $@ "$^"
@printf "primary prerequisites of %s are %s\n" $@ "$(filter-out $(DEP_$@),$^)"
@printf "secondary prerequisites of %s are %s\n" $@ "$(DEP_$@)"
endef

%.o: %.c
$(call RULE_BODY)

# Now the trickery: generate the dependency rules from OBJS and DEP_ vars:

# $(NL) provides newline, so we can insert newline into eval expansions
define NL


endef

# For each object <obj>, generate the rule <obj>: $(DEP_<obj>)
$(eval $(foreach obj,$(OBJS),$(obj): $(DEP_$(obj))$(NL)))

输出:

prerequisites of foo.o are foo.c foo.h bar.h xyzzy.h
primary prerequisites of foo.o are foo.c
secondary prerequisites of foo.o are foo.h bar.h xyzzy.h

prerequisites of bar.o are bar.c bar.h xyzzy.h
primary prerequisites of bar.o are bar.c
secondary prerequisites of bar.o are bar.h xyzzy.h

缺点是必须将任何额外的依赖项插入到变量中,而不是通过普通规则断言。例如,假设我们想要在 config.make makefile 被触及时重新编译所有的 $(OBJS)。我们不能只这样做:

$(OBJS): config.make   # Oops, config.make is now considered primary

相反,我们坚持使用 DEP_ 变量方案并按如下方式进行:

$(eval $(foreach obj,$(OBJS),DEP_$(obj) += config.make$(NL)))

换句话说,遍历$(OBJS),并为每个添加DEP_变量生成一个+=变量赋值>config.make,后跟一个换行符,然后 eval 整个事情就好像它是 Makefile 文本一样。

当上面的 eval 被插入到我们的 Makefile 中时(在现有的 eval 之前,而不是之后)输出显示 config.make 已作为次要先决条件添加到 foo.obar.o 中:

prerequisites of foo.o are foo.c foo.h bar.h xyzzy.h config.make
primary prerequisites of foo.o are foo.c
secondary prerequisites of foo.o are foo.h bar.h xyzzy.h config.make

prerequisites of bar.o are bar.c bar.h xyzzy.h config.make
primary prerequisites of bar.o are bar.c
secondary prerequisites of bar.o are bar.h xyzzy.h config.make

这是一个可行的解决方案,它避免了临时文件,但对于 Makefile 维护者来说更难理解。

另请注意,由于 GNU Make 允许在变量名中使用句点和斜杠,因此类似以下内容不是问题:

DEP_libs/parser/scan.o := config.h libs/parser/parser.h ...

在规则中,libs/parser/scan.o$@ 目标,$(DEP_$@) 很好地给出了我们 config.h libs/parser/parser.h ...

最后,请注意依赖生成器可以生成代码并将其粘贴到依赖生成文件中,而不是 eval 行。也就是说,按照这些行生成文件:

DEP_foo.o := foo.h bar.h xyzzy.h config.make  # config.make tacked on
foo.o: $(DEP_foo.o) # also generated

DEP_bar.o := ... # and so forth
bar.o: $(DEP_bar.o)

关于makefile - GNU 使 : obtain list of primary prerequisites of a rule,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27406585/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com