为啥这个makefile在'make clean'上执行一个目标

Posted

技术标签:

【中文标题】为啥这个makefile在\'make clean\'上执行一个目标【英文标题】:Why does this makefile execute a target on 'make clean'为什么这个makefile在'make clean'上执行一个目标 【发布时间】:2011-04-12 11:00:17 【问题描述】:

这是我当前的 makefile。

CXX      = g++
CXXFLAGS = -Wall -O3
LDFLAGS  =

TARGET = testcpp
SRCS   = main.cpp object.cpp foo.cpp
OBJS   = $(SRCS:.cpp=.o)
DEPS   = $(SRCS:.cpp=.d)


.PHONY: clean all

all: $(TARGET)

$(TARGET): $(OBJS)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)

.cpp.o:
    $(CXX) $(CXXFLAGS) -c $< -o $@

%.d: %.cpp
    $(CXX) -M $(CXXFLAGS) $< > $@

clean:
    rm -f $(OBJS) $(DEPS) $(TARGET)

-include $(DEPS)

它完美地工作,但有一个例外。如果目录已经是干净的(没有 *.d、*.o)并且我运行“make clean”,它会重新创建依赖项,然后立即删除它们:

[user@server proj]$ make
g++ -M -Wall -O3 foo.cpp > foo.d
g++ -M -Wall -O3 object.cpp > object.d
g++ -M -Wall -O3 main.cpp > main.d
g++ -Wall -O3 -c main.cpp -o main.o
g++ -Wall -O3 -c object.cpp -o object.o
g++ -Wall -O3 -c foo.cpp -o foo.o
g++ -Wall -O3  main.o object.o foo.o -o testcpp
[user@server proj]$ make clean
rm -f main.o object.o foo.o main.d object.d foo.d testcpp
[user@server proj]$ make clean
g++ -M -Wall -O3 foo.cpp > foo.d
g++ -M -Wall -O3 object.cpp > object.d
g++ -M -Wall -O3 main.cpp > main.d
rm -f main.o object.o foo.o main.d object.d foo.d testcpp
[user@server proj]$

我不明白为什么第二个“make clean”会重新生成依赖文件。我怎样才能避免这种情况?对于这个人为的示例来说,这没什么大不了的,但对于一个大型项目来说,这可能非常耗时。

谢谢。

【问题讨论】:

您可能想阅读mad-scientist.net/make/autodep.html,它描述了一种用于跟踪包含依赖项的解决方案,可以避免此问题以及令人惊讶的许多其他并发症。 【参考方案1】:

如果您想跳过多个目标的包含,可以使用filter 函数。

MAKEFILE_TARGETS_WITHOUT_INCLUDE := clean distclean doc

# Include only if the goal needs it
ifeq ($(filter $(MAKECMDGOALS),$(MAKEFILE_TARGETS_WITHOUT_INCLUDE)),)
  -include $(DEPS)
endif

【讨论】:

【参考方案2】:

-include 中的前导 - 表示如果依赖项丢失且无法重新制作,make 不会抱怨,但这并不意味着它不会首先尝试制作它们(并且,在这种情况下,成功)——毕竟,任何有趣或重要的可能都在包含的文件中,所以让我们试试——让他们尝试。我认为没有办法阻止这种情况。

有关include-include 的文档,请参阅here。

【讨论】:

【参考方案3】:

它想要重新生成依赖文件,因为它总是在执行任何其他操作之前尝试重新生成所有的 makefile,包括 -include 的 makefile。 (嗯,实际上,对我来说,它并没有这样做 - 我有 GNU Make 3.81 - 所以可能是你的版本中的一个错误已修复,或者我的优化有而你的没有。但无论如何。)

解决此问题的最简单方法是编写规则,以便它们生成 .d 文件作为常规编译的副作用,而不是给出明确的规则来生成它们。这样,当它们不存在时,Make 不知道如何生成它们,因此它不会尝试(在干净的树中,.cpp.o 规则就足够了,你不需要 em> 头文件依赖项)。查看 Automake 生成的 makefile——一个简单的——看看它是如何完成的。

【讨论】:

【参考方案4】:

这是因为.d 文件被无条件地变为-included。据make 所知,他们可以将依赖项或命令添加到clean 目标。出于这个原因,所有included 文件都是首先构建的,否则您可能会得到一个不正确或失败的构建。要禁用此功能,您需要有条件地包含依赖文件:

ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif

另一种解决方案是使用touch 生成依赖文件,并将它们替换为实际数据作为编译的副作用。这就是 automake 进行依赖跟踪的方式,因为它使一次性构建更快。如果您想走这条路线,请查看gcc-MD-MMD 选项。使用如下模式规则:

%.d:
    @touch $@

最初创建依赖文件。

【讨论】:

请注意:可以一次构建多个目标 - 例如:make all clean 会将 $(MAKECMDGOALS) 设置为 all clean,这仍会调用上述语句的主体。这不会那么糟糕,尽管all 目标确实需要包含$(DEPS)。如果您有另一个可以想象用 clean 调用的目标,那么您必须在 ifneq 语句中更加聪明

以上是关于为啥这个makefile在'make clean'上执行一个目标的主要内容,如果未能解决你的问题,请参考以下文章

make clean 和make distclean的区别

make clean与make distclean的区别

makefile调用makefile错误

make clean,make distclean与make depend的区别

请解释该Makefile文件每一行的含义,并论述输入make命令以及make clean命令后的执行结果:

makefile教程