假设我有一个带有规则的makefile
%.o: %.c
gcc -Wall -Iinclude ...
我希望在头文件更改时重建 *.o。/include
只要有任何头文件发生变化,就必须重建目录中的所有对象,而不是计算出依赖项列表。
我想不出改变规则以适应这种情况的好方法,我愿意接受建议。如果标题列表不必硬编码,则加分
假设我有一个带有规则的makefile
%.o: %.c
gcc -Wall -Iinclude ...
我希望在头文件更改时重建 *.o。/include
只要有任何头文件发生变化,就必须重建目录中的所有对象,而不是计算出依赖项列表。
我想不出改变规则以适应这种情况的好方法,我愿意接受建议。如果标题列表不必硬编码,则加分
如果您使用的是 GNU 编译器,编译器可以为您组装一个依赖项列表。生成文件片段:
depend: .depend
.depend: $(SRCS)
rm -f "$@"
$(CC) $(CFLAGS) -MM $^ -MF "$@"
include .depend
或者
depend: .depend
.depend: $(SRCS)
rm -f "$@"
$(CC) $(CFLAGS) -MM $^ > "$@"
include .depend
whereSRCS
是一个指向整个源文件列表的变量。
还有那个工具makedepend
,但我从来没有像它那样喜欢它gcc -MM
大多数答案都出奇地复杂或错误。然而,简单而强大的示例已在其他地方发布 [ codereview ]。诚然,gnu 预处理器提供的选项有点令人困惑。但是,记录了从构建目标中删除所有目录的过程,-MM
而不是错误 [ gpp ]:
默认情况下,CPP 采用主输入文件的名称,删除任何 目录组件和任何文件后缀,例如“.c”,并附加平台常用的对象后缀。
(有点新的)-MMD
选项可能是您想要的。为了完整起见,一个支持多个 src 目录和带有一些注释的构建目录的 makefile 示例。对于没有构建目录的简单版本,请参见 [ 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)
此方法之所以有效,是因为如果单个目标有多个依赖行,则依赖项会简单地连接起来,例如:
a.o: a.h
a.o: a.c
./cmd
相当于:
a.o: a.c a.h
./cmd
如前所述:Makefile 单个目标的多个依赖行?
正如我在此处发布的那样, gcc 可以同时创建依赖项并进行编译:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
“-MF”参数指定一个文件来存储依赖关系。
'-include' 开头的破折号告诉Make 在.d 文件不存在时继续(例如在第一次编译时)。
请注意,gcc 中似乎存在关于 -o 选项的错误。如果您将目标文件名设置为 obj/_file__c.o,则生成的文件.d 仍将包含文件.o,而不是 obj/_file__c.o。
怎么样:
includes = $(wildcard include/*.h)
%.o: %.c ${includes}
gcc -Wall -Iinclude ...
您也可以直接使用通配符,但我倾向于发现我在不止一个地方需要它们。
请注意,这只适用于小型项目,因为它假定每个目标文件都依赖于每个头文件。
Martin 的上述解决方案效果很好,但不能处理位于子目录中的 .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 对其进行了测试
这是一个两行:
CPPFLAGS = -MMD
-include $(OBJS:.c=.d)
这适用于默认的 make 配方,只要您在OBJS
.
Sophie答案的略微修改版本,它允许将 *.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 $@
用于确保生成的 *.d 文件中的目标(即目标文件名)包含 *.o 文件的完整路径,而不仅仅是文件名。
我不知道为什么在将 -MMD 与 -c结合使用时不需要此参数(如在 Sophie 的版本中)。在这种组合中,它似乎将 *.o 文件的完整路径写入 *.d 文件。如果没有这种组合,-MMD 也只会将没有任何目录组件的纯文件名写入 *.d 文件。也许有人知道为什么 -MMD 与 -c 结合使用时会写入完整路径。我在 g++ 手册页中没有找到任何提示。
我更喜欢这个解决方案,而不是 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 $@ $<