Makefile:添加前缀的顺序会影响如何从公共依赖项构建目标列表
Makefile: order of adding prefix affects how a list of targets is built from a common dependency
动机:
我有一个 C 项目,其中多个 .o 文件要从一个公共文件生成。此主文件使用预处理器指令根据需要有条件地包含其他 .h 文件,具体取决于 makefile 中定义的特定于目标的变量。
我在下面写了这条规则,但根据我应用变量引用的顺序,我会得到不同的结果。
一个小的(大概)变化,两个不同的输出
考虑我的 Makefile 中的两个代码版本。在版本 A 中,我们有以下片段:
MAIN_OBJ:= $(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4)
...省略了不相关的规则(包括一条all:
规则)
$(OBJECT_DIR)/$(MAIN_1): MFLAG = $(METHOD_1_FLAG)
$(OBJECT_DIR)/$(MAIN_2): MFLAG = $(METHOD_2_FLAG)
$(OBJECT_DIR)/$(MAIN_3): MFLAG = $(METHOD_3_FLAG)
$(OBJECT_DIR)/$(MAIN_4): MFLAG = $(METHOD_4_FLAG)
$(OBJECT_DIR)/$(MAIN_OBJ): $(SOURCE_DIR)/$(DEPENDENT_MAIN)
$(CC) -DUSE_$(MFLAG) $(CFLAGS) -o $@ $<
这只会成功构建第一个目标 $(OBJECT_DIR)/$(MAIN_1)
。剩下的三个永远不会被编译并停在那里。
现在在版本 B 中,我们重新定义 MAIN_OBJ
,以便目录前缀包含在目标列表本身中:
MAIN_OBJ:= $(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4)
MAIN_OBJ:= $(addprefix $(OBJECT_DIR)/,$(MAIN_OBJ)
...省略了不相关的规则(再次)
$(OBJECT_DIR)/$(MAIN_1): MFLAG = $(METHOD_1_FLAG)
$(OBJECT_DIR)/$(MAIN_2): MFLAG = $(METHOD_2_FLAG)
$(OBJECT_DIR)/$(MAIN_3): MFLAG = $(METHOD_3_FLAG)
$(OBJECT_DIR)/$(MAIN_4): MFLAG = $(METHOD_4_FLAG)
$(MAIN_OBJ): $(SOURCE_DIR)/$(DEPENDENT_MAIN)
$(CC) -DUSE_$(MFLAG) $(CFLAGS) -o $@ $<
此解决方案有效,并编译了所有 4 个 .o 文件,每个文件都具有正确的 $(MFLAG)
值。
这里发生了什么?
这可能是一个愚蠢的问题,但是为什么版本A只编译一个.o文件?我认识到 B 版通常是编写规则的更好方法。
让我再举一个例子来说明我的困惑。
假设我们想要编写一种更常见的规则类型:使用用于查找依赖项的模式规则从列表中编译目标。
执行与版本 A 类似的操作不会导致成功生成单个 .o:
MY_FILES:= $(wildcard $(SOURCE_DIR)/*.c))
MY_OBJ:= $(patsubst $(SOURCE_DIR)/%.c, %.o, $(MY_FILES))
...
$(OBJECT_DIR)/$(MY_OBJ): $(OBJECT_DIR)/%.o: $(SOURCE_DIR)/%.c
$(CC) $(CFLAGS) -o $@ $<
很明显,以上是个坏主意,你应该这样写:
MY_FILES:= $(wildcard $(SOURCE_DIR)/*.c))
MY_OBJ:= $(patsubst $(SOURCE_DIR)/%.c, $(OBJECT_DIR)/%.o, $(MY_FILES))
...
$(MY_OBJ): $(OBJECT_DIR)/%.o: $(SOURCE_DIR)/%.c
$(CC) $(CFLAGS) -o $@ $<
但我的问题是:
为什么在这种情况下,在规则本身中添加目录前缀导致没有构建任何内容,而在我的 makefile 的版本 A 中,第一个目标已成功创建?
“版本 A”失败,因为 make 只是按照您的要求扩展内容。像这样的变量引用:
$(OBJECT_DIR)/$(MAIN_OBJ): ...
表示“扩展变量 OBJECT_DIR,然后添加一个“/”,然后扩展变量 MAIN_OBJ”。所以你得到:
$(OBJECT_DIR)/$(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4): ...
因此,实际上只有第一个以 OBJECT_DIR 值作为前缀,而不是所有(因为您没有显示所有这些变量的值,所以我没有完成扩展) .
其次,make 始终只构建它在 makefile 中找到的第一个目标(除非您使用命令行或 .DEFAULT
覆盖它)。你没有说你省略的“不相关规则”是什么,但除非其中之一是 all
目标或依赖于所有 MAIN_*
目标的类似目标,否则 make 只会构建第一个是您看到的行为。
ETA 使用各种方法将所有单词放在前面是微不足道的;见 GNU make manual.
一个选项:
$(addprefix $(OBJECT_DIR)/,$(MAIN_OBJ)): ...
另一个选项:
$(MAIN_OBJ:%=$(OBJECT_DIR)/%): ...
另一个选项:
$(patsubst %,$(OBJECT_DIR)/%,$(MAIN_OBJ)): ...
动机:
我有一个 C 项目,其中多个 .o 文件要从一个公共文件生成。此主文件使用预处理器指令根据需要有条件地包含其他 .h 文件,具体取决于 makefile 中定义的特定于目标的变量。
我在下面写了这条规则,但根据我应用变量引用的顺序,我会得到不同的结果。
一个小的(大概)变化,两个不同的输出
考虑我的 Makefile 中的两个代码版本。在版本 A 中,我们有以下片段:
MAIN_OBJ:= $(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4)
...省略了不相关的规则(包括一条all:
规则)
$(OBJECT_DIR)/$(MAIN_1): MFLAG = $(METHOD_1_FLAG)
$(OBJECT_DIR)/$(MAIN_2): MFLAG = $(METHOD_2_FLAG)
$(OBJECT_DIR)/$(MAIN_3): MFLAG = $(METHOD_3_FLAG)
$(OBJECT_DIR)/$(MAIN_4): MFLAG = $(METHOD_4_FLAG)
$(OBJECT_DIR)/$(MAIN_OBJ): $(SOURCE_DIR)/$(DEPENDENT_MAIN)
$(CC) -DUSE_$(MFLAG) $(CFLAGS) -o $@ $<
这只会成功构建第一个目标 $(OBJECT_DIR)/$(MAIN_1)
。剩下的三个永远不会被编译并停在那里。
现在在版本 B 中,我们重新定义 MAIN_OBJ
,以便目录前缀包含在目标列表本身中:
MAIN_OBJ:= $(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4)
MAIN_OBJ:= $(addprefix $(OBJECT_DIR)/,$(MAIN_OBJ)
...省略了不相关的规则(再次)
$(OBJECT_DIR)/$(MAIN_1): MFLAG = $(METHOD_1_FLAG)
$(OBJECT_DIR)/$(MAIN_2): MFLAG = $(METHOD_2_FLAG)
$(OBJECT_DIR)/$(MAIN_3): MFLAG = $(METHOD_3_FLAG)
$(OBJECT_DIR)/$(MAIN_4): MFLAG = $(METHOD_4_FLAG)
$(MAIN_OBJ): $(SOURCE_DIR)/$(DEPENDENT_MAIN)
$(CC) -DUSE_$(MFLAG) $(CFLAGS) -o $@ $<
此解决方案有效,并编译了所有 4 个 .o 文件,每个文件都具有正确的 $(MFLAG)
值。
这里发生了什么?
这可能是一个愚蠢的问题,但是为什么版本A只编译一个.o文件?我认识到 B 版通常是编写规则的更好方法。
让我再举一个例子来说明我的困惑。
假设我们想要编写一种更常见的规则类型:使用用于查找依赖项的模式规则从列表中编译目标。
执行与版本 A 类似的操作不会导致成功生成单个 .o:
MY_FILES:= $(wildcard $(SOURCE_DIR)/*.c))
MY_OBJ:= $(patsubst $(SOURCE_DIR)/%.c, %.o, $(MY_FILES))
...
$(OBJECT_DIR)/$(MY_OBJ): $(OBJECT_DIR)/%.o: $(SOURCE_DIR)/%.c
$(CC) $(CFLAGS) -o $@ $<
很明显,以上是个坏主意,你应该这样写:
MY_FILES:= $(wildcard $(SOURCE_DIR)/*.c))
MY_OBJ:= $(patsubst $(SOURCE_DIR)/%.c, $(OBJECT_DIR)/%.o, $(MY_FILES))
...
$(MY_OBJ): $(OBJECT_DIR)/%.o: $(SOURCE_DIR)/%.c
$(CC) $(CFLAGS) -o $@ $<
但我的问题是:
为什么在这种情况下,在规则本身中添加目录前缀导致没有构建任何内容,而在我的 makefile 的版本 A 中,第一个目标已成功创建?
“版本 A”失败,因为 make 只是按照您的要求扩展内容。像这样的变量引用:
$(OBJECT_DIR)/$(MAIN_OBJ): ...
表示“扩展变量 OBJECT_DIR,然后添加一个“/”,然后扩展变量 MAIN_OBJ”。所以你得到:
$(OBJECT_DIR)/$(MAIN_1) $(MAIN_2) $(MAIN_3) $(MAIN_4): ...
因此,实际上只有第一个以 OBJECT_DIR 值作为前缀,而不是所有(因为您没有显示所有这些变量的值,所以我没有完成扩展) .
其次,make 始终只构建它在 makefile 中找到的第一个目标(除非您使用命令行或 .DEFAULT
覆盖它)。你没有说你省略的“不相关规则”是什么,但除非其中之一是 all
目标或依赖于所有 MAIN_*
目标的类似目标,否则 make 只会构建第一个是您看到的行为。
ETA 使用各种方法将所有单词放在前面是微不足道的;见 GNU make manual.
一个选项:
$(addprefix $(OBJECT_DIR)/,$(MAIN_OBJ)): ...
另一个选项:
$(MAIN_OBJ:%=$(OBJECT_DIR)/%): ...
另一个选项:
$(patsubst %,$(OBJECT_DIR)/%,$(MAIN_OBJ)): ...