【问题标题】:make include directive and dependency generation with -MM使用 -MM 生成包含指令和依赖项
【发布时间】:2023-03-16 14:33:01
【问题描述】:

如果包含的目标已过期或不存在,我希望包含指令触发构建规则。

目前makefile看起来是这样的:

program_NAME := wget++
program_H_SRCS := $(wildcard *.h)
program_CXX_SRCS := $(wildcard *.cpp)
program_CXX_OBJS := ${program_CXX_SRCS:.cpp=.o}
program_OBJS := $(program_CXX_OBJS)

DEPS = make.deps

.PHONY: all clean distclean

all: $(program_NAME) $(DEPS)

$(program_NAME): $(program_OBJS)
    $(LINK.cc) $(program_OBJS) -o $(program_NAME)

clean:
    @- $(RM) $(program_NAME)
    @- $(RM) $(program_OBJS)
    @- $(RM) make.deps

distclean: clean

make.deps: $(program_CXX_SRCS) $(program_H_SRCS)
    $(CXX) $(CPPFLAGS) -MM $(program_CXX_SRCS) > make.deps

include $(DEPS)

问题在于,似乎 include 指令在构建 make.deps 的规则之前执行,这实际上意味着如果 make.deps 不存在,make 要么没有获得依赖列表,要么总是从中获取 make.deps上一个版本,而不是当前版本。

例如:

$ make clean 
$ make
makefile:32: make.deps: No such file or directory
g++  -MM addrCache.cpp connCache.cpp httpClient.cpp wget++.cpp > make.deps
g++    -c -o addrCache.o addrCache.cpp
g++    -c -o connCache.o connCache.cpp
g++    -c -o httpClient.o httpClient.cpp
g++    -c -o wget++.o wget++.cpp
g++      addrCache.o connCache.o httpClient.o wget++.o -o wget++

编辑

我阅读了docs for the include directive,听起来如果包含目标不存在,它将继续处理父makefile尝试构建目标,但我并不完全清楚这是如何工作的:

如果包含的 makefile 不能 在这些目录中的任何一个中找到, 生成警告消息,但它 不是立即致命的错误; 处理包含 包括继续。一旦有了 读完makefile,make会 尝试重新制作任何过时的东西 或者不存在。请参阅如何 Makefile 被重新制作。只有在它之后 试图找到一种方法来重新制作 makefile 并失败,将制作 将丢失的 makefile 诊断为 致命错误。

回答

这是对我接受的答案的修改。缺少的一件事是依赖文件也依赖于源文件,除非将它们添加到包含的 deps 文件中,否则它们不会重新生成:

%.d: $(program_CXX_SRCS)
    @ $(CXX) $(CPPFLAGS) -MM $*.cpp | sed -e 's@^\(.*\)\.o:@\1.d \1.o:@' > $@

sed.d 文件的名称添加到每个依赖行的开头,如下所示:

foo.d foo.o: foo.cpp foo.h bar.h baz.h

我从这篇关于递归 make 的危险的惊人论文中得到了这个想法:

Recursive Make Considered Harmful

我还将以下内容添加到 makefile:

clean_list += ${program_SRCS:.c=.d}

# At the end of the makefile
# Include the list of dependancies generated for each object file
# unless make was called with target clean
ifneq "$(MAKECMDGOALS)" "clean"
-include ${program_SRCS:.c=.d}
endif

【问题讨论】:

标签: c++ g++ makefile


【解决方案1】:

我花了一段时间才明白的重要一点是,之前构建的 make.deps 已经足够好了。想一想:对于给定的目标文件,依赖文件列表可以更改的唯一方法是如果……旧的依赖文件之一已被更改。如果是这种情况,那么旧的 make.deps 将导致该目标文件被重建,如果重建目标文件也会重建 make.deps,那么一切都会是最新的。 您不必在检查必须重新构建哪些对象之前重新构建 make.deps。

【讨论】:

    【解决方案2】:

    您依赖隐式规则来编译您的 .cpp 文件。您必须重新定义它以使用将创建依赖文件的 -MM 和 -MF 标志。

    %.o: %.cpp
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ -MM -MF $@.d
    

    然后,您必须在 Makefile 中包含这些依赖文件,使用 -include 当依赖文件不存在时(第一次或清理后)不会出错。

    program_DEPS := $(program_OBJS:.o=.o.d)
    -include $(program_DEPS)
    

    记得在 clean 规则中为依赖文件添加 rm 命令。

    【讨论】:

    • 没有必要将.d 文件添加到清理规则中,因为include 语句只会导致它们被重新生成。
    • 您可能会发现我对您答案的修改很有用,请参阅我的问题的结尾。
    【解决方案3】:

    include 指令的工作方式与它们在 C 和 C++ 中的工作方式相同——它们在其他任何事情发生之前就被处理,以构建“真正的”makefile,然后再进行处理。具体来说,它们会在任何规则被触发之前进行处理。

    【讨论】:

    • @Neil Butterworth:我在上面的编辑中看到的文档似乎有点模棱两可。听起来如果它们不存在或已过时,它将构建或重建包含的目标,但它会重新包含它们吗?不是很清楚。
    • @Robert S. Barnes:是的,它将重新包含它们并重新运行 Make。一个简单的实验可以证实这一点。 (附注:当您评论他的回答时,您不必“@Neil Butterworth”。)
    猜你喜欢
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多