Dependencies Makefile,头依赖项

Dependencies Makefile,头依赖项,dependencies,makefile,header-files,Dependencies,Makefile,Header Files,假设我有一个带有规则的makefile %.o: %.c gcc -Wall -Iinclude ... 我希望在头文件更改时重新生成*.o。每当/include中的任何头文件发生更改时,都必须重新生成目录中的所有对象,而不是列出依赖项列表 我想不出一个好办法来改变规则来适应这一点,我愿意接受建议。如果标题列表不需要硬编码,那么就可以获得额外的积分,比如: includes = $(wildcard include/*.h) %.o: %.c ${includes} gcc -Wa

假设我有一个带有规则的makefile

%.o: %.c
 gcc -Wall -Iinclude ...
我希望在头文件更改时重新生成*.o。每当
/include
中的任何头文件发生更改时,都必须重新生成目录中的所有对象,而不是列出依赖项列表


我想不出一个好办法来改变规则来适应这一点,我愿意接受建议。如果标题列表不需要硬编码,那么就可以获得额外的积分,比如:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...
您也可以直接使用通配符,但我倾向于在多个地方使用通配符


请注意,这只适用于小型项目,因为它假定每个对象文件都依赖于每个头文件。

如果您使用的是GNU编译器,编译器可以为您汇编依赖项列表。生成文件片段:

depend: .depend

.depend: $(SRCS)
        rm -f "$@"
        $(CC) $(CFLAGS) -MM $^ -MF "$@"

include .depend

其中,
SRCS
是指向整个源文件列表的变量

还有工具
makedepend
,但我从来没有像
gcc-MM

那样喜欢它,因为我发布的gcc可以同时创建依赖项和编译:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
DEPS:=$(OBJS:.o=.d)
-包括美元(DEPS)
%.o:%.c
$(CC)$(CFLAGS)-MM-MF$(patsubst%.o,%.d,$@)-o$@$<
“-MF”参数指定用于存储依赖项的文件

“-include”开头的破折号告诉Make在.d文件不存在时继续(例如,在第一次编译时)


注意gcc中似乎有一个关于-o选项的bug。如果将对象文件名设置为obj/_file\uu c.o,则生成的文件.d仍将包含file.o,而不是obj/_file\uu c.o。

与Michael Williamson接受的答案相比,我更喜欢此解决方案,它捕获对源+内联文件、源+头以及最后仅源的更改。这里的优点是,如果只做了一些更改,则不会重新编译整个库。对于一个只有几个文件的项目来说,这不是一个很大的考虑因素,但是如果你有10个或100个源,你会注意到其中的差异

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)

这将很好地完成工作,甚至可以处理指定的子目录:

    $(CC) $(CFLAGS) -MD -o $@ $<
$(CC)$(CFLAGS)-MD-o$@$<

使用GCC4.8.3对其进行了测试,上面Martin的解决方案效果很好,但不能处理驻留在子目录中的.o文件。哥德里克指出,-MT标志解决了这个问题,但它同时阻止了.o文件的正确写入。以下内容将解决这两个问题:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<
DEPS:=$(OBJS:.o=.d)
-包括美元(DEPS)
%.o:%.c
$(CC)$(CFLAGS)-MM-MT$@-MF$(patsubst%.o,%.d,$@)$<
$(CC)$(CFLAGS)-o$@$<

大多数答案出人意料地复杂或错误。然而,其他地方已经发布了简单而有力的示例[]。诚然,gnu预处理器提供的选项有点混乱。但是,使用
-MM
从生成目标中删除所有目录是有文档记录的,而不是错误[]:

默认情况下,CPP采用主输入文件的名称,删除任何 目录组件和任何文件后缀,如“.c”,并附加 平台的常用对象后缀

(稍新的)
-MMD
选项可能就是您想要的。为完整性起见,请举一个makefile示例,该示例支持多个src dir和带有一些注释的build dir。有关没有生成目录的简单版本,请参见[]

CXX=clang++
CXX_标志=-Wfatal errors-Wall-Wextra-Wpedantic-Wconversion-Wshadow
#最终二进制
BIN=mybin
#将所有自动生成的内容放入此生成目录。
构建目录=/构建
#所有.cpp源文件的列表。
CPP=main.CPP$(通配符dir1/*.CPP)$(通配符dir2/*.CPP)
#所有.o文件都转到build dir。
OBJ=$(CPP:%.CPP=$(构建目录)/%.o)
#Gcc/Clang将创建这些包含依赖项的.d文件。
DEP=$(对象:%.o=%.d)
#以二进制文件命名的默认目标。
$(BIN):$(构建目录)/$(BIN)
#二进制文件的实际目标-取决于所有.o文件。
$(构建目录)/$(BIN):$(OBJ)
#创建生成目录-与源相同的结构。
mkdir-p$(@D)
#只需链接所有对象文件。
$(CXX)$(CXX_标志)$^-o$@
#包括所有.d文件
-包括美元(DEP)
#为每个对象文件构建目标。
#本文介绍了对头文件的潜在依赖性
#通过调用`-include$(DEP)`。
$(构建目录)/%.o:%.cpp
mkdir-p$(@D)
#-MMD标志还创建了一个.d文件,其中包含
#与.o文件同名。
$(CXX)$(CXX_标志)-MMD-c$<-o$@
.假冒:干净
清洁:
#这将删除所有生成的文件。
-rm$(构建目录)/$(BIN)$(OBJ)$(DEP)
此方法之所以有效,是因为如果单个目标有多个依赖关系行,则这些依赖关系只是简单地连接在一起,例如:

a.o: a.h
a.o: a.c
    ./cmd
相当于:

a.o: a.c a.h
    ./cmd

如所述:

以下内容适用于我:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<
DEPS:=$(OBJS:.o=.d)
-包括美元(DEPS)
%.o:%.cpp
$(CXX)$(CFLAGS)-MMD-c-o$@$<
这里有一个两行:

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

这适用于默认的make配方,只要您在Sophie's的一个稍加修改的版本中有一个所有对象文件的列表,该版本允许将*.d文件输出到另一个文件夹(我将只粘贴生成依赖项文件的有趣部分):

用于确保生成的*.d文件中的目标(即对象文件名)包含*.o文件的完整路径,而不仅仅是文件名


我不知道为什么在将-MMD与-c组合使用时不需要这个参数(如在Sophie中)。在这种组合中,它似乎将*.o文件的完整路径写入*.d文件。如果没有这种组合,-MMD也只将没有任何目录组件的纯文件名写入*.d文件。也许有人知道为什么-MMD与-c组合时会写入完整路径。我在g++手册页中没有找到任何提示。

谢谢,我认为这比这项工作所需要的要复杂得多
CPPFLAGS = -MMD
-include $(OBJS:.c=.d)
$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
-MT $@