GNU make 中的错误:特定于目标的变量未在隐式规则中扩展?
我一直致力于设计一个多配置 Makefile(支持单独的“调试”和“发布”目标的文件),并且遇到了一个奇怪的问题,它似乎是 GNU make 中的一个错误。
当在隐式规则中引用这些变量时,GNU make 似乎没有正确扩展特定于目标的变量。下面是一个简化的 Makefile,它显示了这个问题:
all:
@echo specify configuration 'debug' or 'release'
OBJS := foo.o bar.o
BUILDDIR = .build/$(CONFIG)
TARGET = $(addprefix $(BUILDDIR)/,$(OBJS))
debug: CONFIG := debug
release: CONFIG := release
#CONFIG := debug
debug: $(TARGET)
release: $(TARGET)
clean:
rm -rf .build
$(BUILDDIR)/%.o: %.c
@echo [$(BUILDDIR)/$*.o] should be [$@]
@mkdir -p $(dir $@)
$(CC) -c $< -o $@
当指定要制作的目标“调试”时,CONFIG 设置为“调试”,并且 BUILDDIR 和 TARGET 同样正确扩展。但是,在从对象构建源文件的隐式规则中,$@ 会展开,就好像 CONFIG 不存在一样。
以下是使用此 Makefile 的输出:
$ make debug
[.build/debug/foo.o] should be [.build//foo.o]
cc -c foo.c -o .build//foo.o
[.build/debug/bar.o] should be [.build//bar.o]
cc -c bar.c -o .build//bar.o
这表明 BUILDDIR 正在正常扩展,但生成的 $@ 却没有。如果我然后注释掉目标变量规范并手动设置 CONFIG := debug (上面的注释行),我会得到我所期望的:
$ make debug
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c bar.c -o .build/debug/bar.o
我已经在 Gentoo 和 MinGW 上使用 make-3.81 进行了测试,并在 Gentoo 和 MinGW 上使用 make-3.82 进行了测试根图。所有人都表现出相同的行为。
我发现很难相信我会是第一个遇到这个问题的人,所以我猜我可能只是做错了什么——但我会说实话:我不知道我怎么会这样。 :)
是否有任何制作专家可以阐明这个问题?谢谢!
I have been working on designing a multiple configuration Makefile (one that supports separate 'debug' and 'release' targets), and have come across a strange problem which appears to be a bug in GNU make.
It seems that GNU make is not expanding target-specific variables properly when those variables are referenced in an implicit rule. Here is a simplified Makefile which shows this issue:
all:
@echo specify configuration 'debug' or 'release'
OBJS := foo.o bar.o
BUILDDIR = .build/$(CONFIG)
TARGET = $(addprefix $(BUILDDIR)/,$(OBJS))
debug: CONFIG := debug
release: CONFIG := release
#CONFIG := debug
debug: $(TARGET)
release: $(TARGET)
clean:
rm -rf .build
$(BUILDDIR)/%.o: %.c
@echo [$(BUILDDIR)/$*.o] should be [$@]
@mkdir -p $(dir $@)
$(CC) -c lt; -o $@
When specifying the goal 'debug' to make, CONFIG is set to 'debug', and BUILDDIR and TARGET are likewise expanded properly. However, in the implicit rule to build the source file from the object, $@ is expanded as if CONFIG does not exist.
Here is the output from using this Makefile:
$ make debug
[.build/debug/foo.o] should be [.build//foo.o]
cc -c foo.c -o .build//foo.o
[.build/debug/bar.o] should be [.build//bar.o]
cc -c bar.c -o .build//bar.o
This shows that BUILDDIR is being expanded fine, but the resulting $@ is not. If I then comment out the target variable specification and manually set CONFIG := debug (the commented line above), I get what I would expect:
$ make debug
[.build/debug/foo.o] should be [.build/debug/foo.o]
cc -c foo.c -o .build/debug/foo.o
[.build/debug/bar.o] should be [.build/debug/bar.o]
cc -c bar.c -o .build/debug/bar.o
I've tested this with both make-3.81 on Gentoo and MinGW, and make-3.82 on Gentoo. All exhibit the same behavior.
I find it difficult to believe that I would be the first to come across this problem, so I'm guessing I'm probably just doing something wrong -- but I'll be honest: I don't see how I could be. :)
Are there any make gurus out there that might be able to shed some light on this issue? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
基本上,Make 计算出依赖关系的 DAG,并创建一个必须在运行任何规则之前运行的规则列表。分配特定于目标的值是 Make 在运行规则时执行的操作(稍后进行)。这是一个严重的限制(我和其他人之前已经抱怨过),但我不会将其称为错误,因为它在文档中进行了描述。根据 GNUMake 手册:
6.11 特定于目标的变量值: “与自动变量一样,这些值仅在目标配方的上下文中可用(以及其他特定于目标的分配)。”
“目标配方的上下文”指的是命令,而不是先决条件:
10.5.3 自动变量:“无法在规则的先决条件列表中直接访问[自动变量]。”
有几种方法可以解决这个问题。您可以使用 辅助扩展,如果您的 Make 版本GNUMake 有它(3.81 没有,我不知道 3.82)。或者您可以不使用特定于目标的变量:
Basically, Make works out the DAG of dependencies and creates a list of rules that must be run before running any rule. Assigning a target-specific value is something that Make does when running a rule, which comes later. This is a serious limitation (which I and others have complained about before), but I wouldn't call it a bug since it is described in the documentation. According to the GNUMake manual:
6.11 Target-specific Variable Values: "As with automatic variables, these values are only available within the context of a target's recipe (and in other target-specific assignments)."
And "the context of a target's recipe" means the commands, not the prereqs:
10.5.3 Automatic variables: "[Automatic variables] cannot be accessed directly within the prerequisite list of a rule."
There are a couple of ways around this. You can use Secondary Expansion, if your version of Make GNUMake has it (3.81 doesn't, I don't know about 3.82). Or you can do without target-specific variables:
正如 Beta 所指出的,这确实不是 make 中的错误,因为文档中描述了限制(我想我一定错过了那个特定部分 - 抱歉)。
无论如何,我实际上可以通过做一些更简单的事情来解决这个问题。由于我需要的只是根据目标分配一个变量,所以我发现我可以使用 $(MAKECMDGOALS) 变量以便正确扩展构建目录。消除 $(CONFIG) 变量并按如下方式重写 Makefile 正是我所需要的:
然后给出正确的结果:
如果在命令行上指定了多个目标,这当然会中断(因为 $(MAKECMDGOALS) 包含空格分隔的列表),但处理它并不是什么大问题。
As Beta has pointed out, this indeed isn't a bug in make since the limitation is described in the documentation (I guess I must have missed that particular part -- sorry).
In any case, I was actually able to work around this issue by doing something even simpler. Since all I need is to assign a variable based on the goal, I found that I can use the $(MAKECMDGOALS) variable in order to expand the build directory properly. Eliminating the $(CONFIG) variable and rewriting the Makefile as the following does exactly what I need:
This then gives the proper result:
This will of course break if there are multiple goals specified on the command line (since $(MAKECMDGOALS) contains a space-separated list), but dealing with that isn't too much of a problem.
以下是如何在不进行
MAKECMDGOALS
自省的情况下解决问题。问题基本上是您在 Makefile 中指定的规则构成了静态图。特定于目标的分配在规则体的执行期间使用,但在其编译期间不使用。解决这个问题的方法是控制规则编译:使用 GNU Make 的类似宏的结构来生成规则。然后我们就可以完全控制:我们可以将可变材料粘贴到目标、先决条件或配方中。
这是我的
Makefile
版本现在,请注意优点:我可以一次性构建
debug
和release
目标,因为我已经实例化了这两条规则均来自模板!此外,我还随意将宏参数添加到 cc 命令行中,以便模块接收一个 MODE 宏,该宏告诉它们如何编译它们。
我们可以使用变量间接来设置不同的
CFLAGS
或其他什么。如果我们像这样修补上面的内容,看看会发生什么:运行:
最后,我们可以将其与
MAKECMDGOALS
结合起来。我们可以检查 MAKECMDGOALS 并过滤掉其中未指定的构建模式。如果调用了makerelease
,我们就不需要扩展debug
规则。补丁:请注意,我通过将
debug:
和release:
目标滚动到BUILDRULE
宏中来简化事情。Here is how to solve the problem without
MAKECMDGOALS
introspection. Tha problem is basically that the rules you specify inMakefile
constitute a static graph. The target-specific assignments are used during the execution of rule bodies, but not during their compilation.The solution to this is to grab control over rule compilation: use GNU Make's macro-like constructs to generate the rules. Then we have full control: we can stick variable material into the target, prerequisite or recipe.
Here is my version of your
Makefile
Now, notice the advantage: I can build both
debug
andrelease
targets in one go, because I have instantiated both rules from the template!Furthermore, I have taken liberty to add the macro argument into the
cc
command line also, so that the modules receive aMODE
macro which tells them how they are being compiled.We can use variable indirection to set up different
CFLAGS
or whatever. Watch what happens if we patch the above like this:Run:
Finally, we can combine that with
MAKECMDGOALS
. We can inspectMAKECMDGOALS
and filter out the build modes which are not specified there. Ifmake release
is called, we don't need thedebug
rules to be expanded. Patch:Note that I simplified things by rolling the
debug:
andrelease:
targets into theBUILDRULE
macro.