Makefile,头文件依赖关系

比方说,我有一个makefile的规则

%.o: %.c gcc -Wall -Iinclude ... 

我希望* .o在头文件改变的时候重build。 而不是制定一个依赖关系列表,只要/include任何头文件发生变化,就必须重builddir中的所有对象。

我想不出一个很好的方法来改变这个规则,我愿意提供build议。 如果标题列表不需要硬编码,奖励点数

如果您使用的是GNU编译器,则编译器可以为您组合一系列依赖项。 Makefile片段:

 depend: .depend .depend: $(SRCS) rm -f ./.depend $(CC) $(CFLAGS) -MM $^ -MF ./.depend; include .depend 

要么

 depend: .depend .depend: $(SRCS) rm -f ./.depend $(CC) $(CFLAGS) -MM $^ > ./.depend; include .depend 

其中SRCS是一个指向整个源文件列表的variables。

也有工具makedepend ,但我从来没有像gcc -MM那样喜欢它

正如我在这里发布gcc可以创build依赖关系,并在同一时间编译:

 DEPS := $(OBJS:.o=.d) -include $(DEPS) %.o: %.c $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $< 

'-MF'参数指定一个文件来存储依赖关系。

在'-include'开始处的短划线告诉Make在.d文件不存在时继续(例如在第一次编译时)。

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

大多数的答案是惊人的复杂或错误。 但是简单而健壮的例子已经在别处发布了[ codereview ]。 不可否认,GNU预处理器提供的选项有点混乱。 但是,使用-MM从构build目标中删除所有目录是有logging的,而不是bug [ gpp ]:

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

(较新) -MMD选项可能是你想要的。 为了完整起见,一个支持多个src dirs的makefile文件的示例,以及一些注释构builddirs。 对于没有构build目录的简单版本,请参阅[ codereview ]。

 CXX = clang++ CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow # Final binary BIN = mybin # Put all auto generated stuff to this build dir. BUILD_DIR = ./build # List of all .cpp source files. CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp) # All .o files go to build dir. OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o) # Gcc/Clang will create these .d files containing dependencies. DEP = $(OBJ:%.o=%.d) # Default target named after the binary. $(BIN) : $(BUILD_DIR)/$(BIN) # Actual target of the binary - depends on all .o files. $(BUILD_DIR)/$(BIN) : $(OBJ) # Create build directories - same structure as sources. mkdir -p $(@D) # Just link all the object files. $(CXX) $(CXX_FLAGS) $^ -o $@ # Include all .d files -include $(DEP) # Build target for every single object file. # The potential dependency on header files is covered # by calling `-include $(DEP)`. $(BUILD_DIR)/%.o : %.cpp mkdir -p $(@D) # The -MMD flags additionaly creates a .d file with # the same name as the .o file. $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@ .PHONY : clean clean : # This should remove all generated files. -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP) 

这个方法是有效的,因为如果单个目标有多个依赖关系,那么这个依赖关系就简单地join了,例如:

 ao: ah ao: ac ./cmd 

相当于:

 ao: ac ah ./cmd 

如上所述: Makefile多个依赖线为单个目标?

感觉如何?

 includes = $(wildcard include/*.h) %.o: %.c ${includes} gcc -Wall -Iinclude ... 

你也可以直接使用通配符,但我倾向于发现我需要在多个地方。

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

上面的Martin解决scheme效果很好,但不处理驻留在子目录中的.o文件。 Godric指出,-MT标志可以解决这个问题,但同时也会阻止.o文件的正确写入。 以下将关注这两个问题:

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

这样做会很好,甚至可以处理指定的子目录:

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

使用gcc 4.8.3进行testing

我更喜欢这个解决scheme,而不是Michael Williamson接受的答案,它捕获源+内联文件,源+头和最终源的更改。 这里的优点是,如果只做了一些改动,整个库不会被重新编译。 对于一个有两个文件的项目来说,这不是一个巨大的考虑,如果你有10个或100个源文件,你会注意到它们之间的区别。

 COMMAND= gcc -Wall -Iinclude ... %.o: %.cpp %.inl $(COMMAND) %.o: %.cpp %.hpp $(COMMAND) %.o: %.cpp $(COMMAND) 

以下为我工作:

 DEPS := $(OBJS:.o=.d) -include $(DEPS) %.o: %.cpp $(CXX) $(CFLAGS) -MMD -c -o $@ $<