109

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

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

我希望在头文件更改时重建 *.o。/include只要有任何头文件发生变化,就必须重建目录中的所有对象,而不是计算出依赖项列表。

我想不出改变规则以适应这种情况的好方法,我愿意接受建议。如果标题列表不必硬编码,则加分

4

10 回答 10

125

如果您使用的是 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

于 2010-03-07T00:33:15.320 回答
94

大多数答案都出奇地复杂或错误。然而,简单而强大的示例已在其他地方发布 [ 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 单个目标的多个依赖行?

于 2015-05-09T16:04:16.653 回答
29

正如我在此处发布的那样, 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。

于 2010-03-23T16:27:01.753 回答
23

怎么样:

includes = $(wildcard include/*.h)

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

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

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

于 2010-03-07T00:15:45.857 回答
5

Martin 的上述解决方案效果很好,但不能处理位于子目录中的 .o 文件。Godric 指出 -MT 标志可以解决这个问题,但同时会阻止 .o 文件被正确写入。以下将解决这两个问题:

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

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<
于 2015-04-01T00:05:58.083 回答
3

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

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

用 gcc 4.8.3 对其进行了测试

于 2014-12-15T12:36:29.477 回答
3

这是一个两行:

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

这适用于默认的 make 配方,只要您在OBJS.

于 2018-02-07T22:40:20.297 回答
2

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++ 手册页中没有找到任何提示。

于 2018-12-22T18:24:02.763 回答
1

我更喜欢这个解决方案,而不是 Michael Williamson 接受的答案,它捕获对源 + 内联文件的更改,然后是源 + 标题,最后只捕获源。这里的优点是,如果只进行少量更改,则不会重新编译整个库。对于一个有几个文件的项目来说,这并不是一个重要的考虑因素,如果你有 10 个或 100 个源,你会注意到差异。

COMMAND= gcc -Wall -Iinclude ...

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

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

%.o: %.cpp
    $(COMMAND)
于 2014-06-16T12:44:42.750 回答
1

以下对我有用:

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

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<
于 2016-12-15T19:34:05.610 回答