Makefile,头依赖项

假设我有一个带规则的 makefile

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

* I want * .O 在头文件更改时重新生成。每当 /include中的任何头文件发生更改时,都必须重新生成目录中的所有对象,而不是生成依赖项列表。

我想不出一个好办法来改变规则来适应这种情况,我愿意接受建议。如果标题列表不需要硬编码,则加分

129243 次浏览

How about something like:

includes = $(wildcard include/*.h)


%.o: %.c ${includes}
gcc -Wall -Iinclude ...

您也可以直接使用通配符,但是我发现我需要它们出现在不止一个地方。

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

如果您使用 GNU 编译器,编译器可以为您组装一个依赖项列表:

depend: .depend


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


include .depend

or

depend: .depend


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


include .depend

where SRCS is a variable pointing to your entire list of source files.

There is also the tool makedepend, but I never liked it as much as gcc -MM

正如我发布的 给你 gcc 可以创建依赖项并同时编译:

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


-include $(DEPS)


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

’-MF’参数指定用于存储依赖项的文件。

The dash at the start of '-include' tells Make to continue when the .d file doesn't exist (e.g. on first compilation).

注意,在 gcc 中似乎有一个关于 -o 选项的 bug。如果将对象文件名设置为 obj/_ file _ _ c.o,那么生成的 文件.d 仍然包含 文件.o,而不是 obj/_ file _ _ 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 $@ $<

tested it with gcc 4.8.3

Martin 的上述解决方案工作得很好,但是不能处理。子目录中的文件。Godric 指出-MT 标志解决了这个问题,但同时也防止了。无法正确写入文件。以下内容将解决这两个问题:

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


-include $(DEPS)


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

大多数答案都出人意料地复杂或错误。然而,简单和健壮的例子已经张贴在其他地方[ Codereview]。不可否认,gnu 预处理器提供的选项有些令人困惑。但是,使用 -MM从构建目标中删除所有目录是有文档记录的,而不是 bug [ Gpp] :

默认情况下,CPP 接受主输入文件的名称,< strong > 删除 any 目录组件 和任何文件后缀,如“ . c”,并将 平台的通常对象后缀。

The (somewhat newer) -MMD option is probably what you want. For completeness an example of a makefile that supports multiple src dirs and build dirs with some comments. For a simple version without build dirs see [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

is equivalent to:

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

如以下所述: 为单个目标生成文件多个依赖项行?

下面这些对我有用:

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


-include $(DEPS)


%.o: %.cpp
$(CXX) $(CFLAGS) -MMD -c -o $@ $<

这里有两句话:

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

只要在 OBJS中有所有对象文件的列表,就可以使用默认的 make 配方。

一个稍微修改版本的苏菲的 回答,它允许输出 * 。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 文件,而不仅仅是文件名。

I don't know why this parameter is NOT needed when using -MMD in combination with -c (as in Sophie's 版本). In this combination it seems to write the full path of the *.o files into the *.d files. Without this combination, -MMD also writes only the pure file names without any directory components into the *.d files. Maybe somebody knows why -MMD writes the full path when combined with -c. I have not found any hint in the g++ man page.