Makefile:添加前缀的顺序会影响如何根据公共依赖项构建目标列表

时间:2016-07-04 21:00:35

标签: makefile gnu

动机:

我有一个C项目,其中从公共文件生成多个.o文件。此主文件使用预处理程序指令根据需要有条件地包含其他.h文件,具体取决于makefile中定义的特定于目标的变量。

我在下面写了这条规则,但根据我应用变量引用的顺序,我会得到不同的结果。

一个小的(ish)变化,两个不同的输出

考虑我的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中,第一个目标是否成功完成?

1 个答案:

答案 0 :(得分:2)

“版本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将仅构建第一个是你看到的行为。