Makefile变量会产生不同的结果



我正在为一个业余操作系统项目编写make脚本。在玩这个脚本时,我注意到(几乎)同一个变量的两个不同的变量展开会产生不同的结果(即使它们"直接"放在一起)。我将提供makefile的重要部分以及运行时的结果。

Makefile:

####################
#      KERNEL      #
####################
.PHONY: kernel
KERNEL_OBJS  = $(patsubst %.c,%.o,$(wildcard kernel/*.c))
KERNEL_OBJS += $(patsubst %.asm,%.o,$(wildcard kernel/*.asm))
KERNEL_OBJS += $(DRIVER_OBJS)
KERNEL_NAME  = kernel32.elf
kernel: $(KERNEL_OBJS)
@echo $^
@echo $(KERNEL_OBJS)

####################
#     DRIVERS      #
####################
.PHONY: drivers
DRIVER_OBJS := $(patsubst %.c,%.o,$(wildcard drivers/*/*.c))

通过终端以以下方式执行Makefile(GNU Make 4.2.1):

make kernel

这产生了以下结果:

kernel/kmain.o kernel/boot.o
kernel/kmain.o kernel/boot.o drivers/vga/vga.o

输出行当然来自内核配方中的两条"回显行"。值得一提的是,此代码片段中使用的所有变量在此处使用,在其他更大的make脚本中仅在此处使用。两个常规后缀规则用于构建KERNEL_OBJS,但它们无论如何都不应该改变这里的输出。除了后缀规则之外,这个片段及其变量与脚本的其余部分完全分离。

你知道为什么这两个变量展开式不同吗?你的,迈克尔。

这两个上下文之间的最大区别是,在解析makefile时,先决条件列表会立即展开,但只有在稍后make即将构建该目标时,才会展开配方。

当make第一次解析你的makefile时,它会发现这一行:

kernel: $(KERNEL_OBJS)

它会立即展开此变量。当变量被展开时,DRIVER_OBJS变量还没有被设置,所以它是空字符串,你会得到这个:

kernel: kernel/kmain.o kernel/boot.o

然后make完成对所有makefile的解析,作为解析的一部分,DRIVER_OBJS变量被设置。。。但这对上面的行来说并不重要,因为它已经被扩展了。

现在make决定要构建kernel目标,为了做到这一点,它必须扩展配方:

@echo $^
@echo $(KERNEL_OBJS)

这里$^是先决条件列表:kernel/kmain.o kernel/boot.o。现在KERNEL_OBJS已展开,DRIVER_OBJS已设置,因此您可以获得完整的列表。

有关何时进行扩展的完整详细信息,请参阅How make Reads a Makefile。

您可以激活第二次扩展,然后在依赖项中使用它:

.SECONDEXPANSION:
kernel: $$(KERNEL_OBJS)

最新更新