我一直在设计一个多配置Makefile(一个支持单独的'debug'和'release'目标),并且遇到了一个奇怪的问题,这似乎是GNU make中的一个bug .
当隐式规则中引用这些变量时,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 $@
指定目标'debug'时,CONFIG设置为'debug',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上使用make-3.82进行了测试 . 所有都表现出相同的行为 .
我发现很难相信我会是第一个遇到这个问题的人,所以我猜我可能只是做错了什么 - 但我会说实话:我不明白我是怎么回事 . :)
是否有任何制作大师可以在这个问题上阐明一些?谢谢!
3 回答
基本上,Make计算出依赖项的DAG,并创建在运行任何规则之前必须运行的规则列表 . 分配特定于目标的值是Make在运行规则时所做的事情,稍后会出现 . 这是一个严重的限制(我和其他人之前曾抱怨过),但我不会把它称为bug,因为它在文档中有描述 . 根据GNUMake手册:
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)."
而“目标配方的上下文”意味着命令,而不是先决条件:
10.5.3 Automatic variables:"[Automatic variables] cannot be accessed directly within the prerequisite list of a rule."
有几种方法可以解决这个问题 . 你可以使用Secondary Expansion,如果你的Make GNUMake版本有它(3.81不知道't, I don't知道3.82) . 或者你可以没有特定于目标的变量:
正如Beta指出的那样,这确实不是make中的错误,因为文档中描述了限制(我想我一定错过了那个特定的部分 - 抱歉) .
无论如何,我实际上能够通过做一些更简单的事情来解决这个问题 . 由于我只需要根据目标分配变量,我发现我可以使用$(MAKECMDGOALS)变量来正确扩展构建目录 . 消除$(CONFIG)变量并重写Makefile,如下所示完全符合我的要求:
然后给出正确的结果:
如果在命令行上指定了多个目标(因为$(MAKECMDGOALS)包含一个以空格分隔的列表),这当然会中断,但处理这个并不是太大的问题 .
以下是如何在没有
MAKECMDGOALS
内省的情况下解决问题 . 问题基本上是您在Makefile
中指定的规则构成静态图 . 特定于目标的分配在规则实体执行期间使用,但在编译期间不使用 .解决方案是获取对规则编译的控制:使用GNU Make的类似宏的构造来生成规则 . 然后我们完全控制:我们可以将可变材料粘贴到目标,先决条件或配方中 .
这是我的
Makefile
版本现在,请注意优势:我可以一次构建
debug
和release
目标,因为我已经从模板中实例化了两个规则!此外,我已经自由地将宏参数添加到
cc
命令行中,以便模块接收MODE
宏,告诉他们如何编译它们 .我们可以使用变量间接来设置不同的
CFLAGS
或其他 . 看看如果我们像这样补丁上面会发生什么:跑:
最后,我们可以将它与
MAKECMDGOALS
结合起来 . 我们可以检查MAKECMDGOALS
并过滤掉那里没有指定的构建模式 . 如果调用make release
,我们不需要扩展debug
规则 . 补丁:请注意,我通过将
debug:
和release:
目标滚动到BUILDRULE
宏来简化操作 .