Makefile,头依赖项

时间:2010-03-07 00:09:36

标签: dependencies makefile header-files

假设我有一个带有规则

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

我想在头文件发生变化时重建* .o。每当/include中的任何头文件发生更改时,都不会计算出依赖项列表,而是必须重建dir中的所有对象。

我想不出改变规则的好方法来容纳这个,我愿意接受建议。如果标题列表不必硬编码,则奖励积分

10 个答案:

答案 0 :(得分:111)

如果您使用的是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是指向整个源文件列表的变量。

还有工具makedepend,但我从不喜欢它gcc -MM

答案 1 :(得分:53)

大多数答案都非常复杂或错误。然而,其他地方[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 multiple dependency lines for a single target?

答案 2 :(得分:24)

当我发布here时,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。

答案 3 :(得分:22)

如下:

includes = $(wildcard include/*.h)

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

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

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

答案 4 :(得分:4)

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

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

-include $(DEPS)

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

答案 5 :(得分:3)

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

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

用gcc 4.8.3进行测试

答案 6 :(得分:1)

这是一个双线:

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

这适用于默认的制作配方,只要您在OBJS中有所有目标文件的列表。

答案 7 :(得分:0)

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

COMMAND= gcc -Wall -Iinclude ...

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

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

%.o: %.cpp
    $(COMMAND)

答案 8 :(得分:0)

以下适用于我:

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

-include $(DEPS)

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

答案 9 :(得分:0)

Sophie answer的稍作修改的版本,它允许将* .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 结合使用时不需要此参数(如Sophie的version一样)。通过这种组合,似乎可以将* .o文件的完整路径写入* .d文件。如果没有这种组合,-MMD还将仅将纯文件名而不将任何目录组成部分写入* .d文件。也许有人知道为什么-MMD与-c结合使用时会写入完整路径。我在g ++手册页中找不到任何提示。