Makefile、头文件依赖

发布于 2024-08-24 07:15:53 字数 237 浏览 4 评论 0原文

假设我有一个 makefile,其规则

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

是我希望在头文件更改时重建 *.o。每当 /include 中的任何头文件发生更改时,都必须重新构建该目录中的所有对象,而不是制定依赖项列表。

我想不出一个很好的方法来改变规则来适应这一点,我愿意接受建议。如果标头列表不必进行硬编码,则有加分

Let's say I have a makefile with the rule

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

I want *.o to be rebuilt whenever a header file changes. Rather than work out a list of dependencies, whenever any header file in /include changes, then all objects in the dir must be rebuilt.

I can't think of a nice way to change the rule to accomodate this, I'm open to suggestions. Bonus points if the list of headers doesn't have to be hard-coded

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

囚我心虐我身 2024-08-31 07:15:53

如果您使用 GNU 编译器,编译器可以为您组装依赖项列表。 Makefile 片段:

depend: .depend

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

include .depend

或者

depend: .depend

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

include .depend

其中 SRCS 是指向整个源文件列表的变量。

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

If you are using a GNU compiler, the compiler can assemble a list of dependencies for you. Makefile fragment:

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

坐在坟头思考人生 2024-08-31 07:15: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 
lt; -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

如以下所述: 为单个目标创建多个依赖行?

Most answers are surprisingly complicated or erroneous. However simple and robust examples have been posted elsewhere [codereview]. Admittedly the options provided by the gnu preprocessor are a bit confusing. However, the removal of all directories from the build target with -MM is documented and not a bug [gpp]:

By default CPP takes the name of the main input file, deletes any
directory components and any file suffix such as ‘.c’, and appends the
platform's usual object suffix.

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 
lt; -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

This method works because if there are multiple dependency lines for a single target, the dependencies are simply joined, e.g.:

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

is equivalent to:

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

as mentioned at: Makefile multiple dependency lines for a single target?

触ぅ动初心 2024-08-31 07:15:53

正如我发布的此处 gcc 可以同时创建依赖项和编译:

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

-include $(DEPS)

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

“-MF”参数指定一个用于存储依赖项的文件。

“-include”开头的破折号告诉 Make 在 .d 出现时继续文件不存在(例如在第一次编译时)。

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

As I posted here gcc can create dependencies and compile at the same time:

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

-include $(DEPS)

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

The '-MF' parameter specifies a file to store the dependencies in.

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

Note there seems to be a bug in gcc regarding the -o option. If you set the object filename to say obj/_file__c.o then the generated file.d will still contain file.o, not obj/_file__c.o.

生生漫 2024-08-31 07:15:53

怎么样:

includes = $(wildcard include/*.h)

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

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

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

How about something like:

includes = $(wildcard include/*.h)

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

You could also use the wildcards directly, but I tend to find I need them in more than one place.

Note that this only works well on small projects, since it assumes that every object file depends on every header file.

梦回梦里 2024-08-31 07:15:53

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

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

-include $(DEPS)

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

Martin's solution above works great, but does not handle .o files that reside in subdirectories. Godric points out that the -MT flag takes care of that problem, but it simultaneously prevents the .o file from being written correctly. The following will take care of both of those problems:

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

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) 
lt;
    $(CC) $(CFLAGS) -o $@ 
lt;
ゞ花落谁相伴 2024-08-31 07:15:53

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

    $(CC) $(CFLAGS) -MD -o $@ 
lt;

用 gcc 4.8.3 测试它

This will do the job just fine , and even handle subdirs being specified:

    $(CC) $(CFLAGS) -MD -o $@ 
lt;

tested it with gcc 4.8.3

近箐 2024-08-31 07:15:53

这是一个两行代码:

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

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

Here's a two-liner:

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

This works with the default make recipe, as long as you have a list of all your object files in OBJS.

过期以后 2024-08-31 07:15:53

Sophie 的 answer 的稍微修改版本,允许将 *.d 文件输出到不同的文件夹(我只会粘贴生成依赖文件的有趣部分):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ 
lt; -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c 
lt; -o $@

请注意,该参数

-MT $@

用于确保生成的 *.d 文件中的目标(即目标文件名)包含 *.o 文件的完整路径,而不仅仅是文件名。

我不知道为什么在将 -MMD 与 -c 结合使用时不需要此参数(如 Sophie 的 版本)。在这种组合中,它似乎将 *.o 文件的完整路径写入 *.d 文件中。如果没有此组合,-MMD 也仅将不带任何目录组件的纯文件名写入 *.d 文件。也许有人知道为什么 -MMD 与 -c 结合使用时会写入完整路径。我在 g++ 手册页中没有找到任何提示。

A slightly modified version of Sophie's answer which allows to output the *.d files to a different folder (I will only paste the interesting part that generates the dependency files):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ 
lt; -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c 
lt; -o $@

Note that the parameter

-MT $@

is used to ensure that the targets (i.e. the object file names) in the generated *.d files contain the full path to the *.o files and not just the file name.

I don't know why this parameter is NOT needed when using -MMD in combination with -c (as in Sophie's version). 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.

饮惑 2024-08-31 07:15:53

我更喜欢这个解决方案,而不是迈克尔·威廉姆森接受的答案,它捕获对源+内联文件的更改,然后是源+标头,最后仅捕获源。这样做的优点是,如果只进行了一些更改,则不需要重新编译整个库。对于具有几个文件的项目来说这并不是一个重要的考虑因素,但如果您有 10 个或 100 个源,您就会注意到差异。

COMMAND= gcc -Wall -Iinclude ...

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

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

%.o: %.cpp
    $(COMMAND)

I prefer this solution, over the accepted answer by Michael Williamson, it catches changes to sources+inline files, then sources+headers, and finally sources only. Advantage here is that the whole library is not recompiled if only a a few changes are made. Not a huge consideration for a project with a couple of files, bur if you have 10 or a 100 sources, you will notice the difference.

COMMAND= gcc -Wall -Iinclude ...

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

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

%.o: %.cpp
    $(COMMAND)
沦落红尘 2024-08-31 07:15:53

以下对我有用:

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

-include $(DEPS)

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

The following works for me:

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

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ 
lt;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文