【问题标题】:Makefile automatic dependency not working with GNU Make 4.0 or laterMakefile 自动依赖不适用于 GNU Make 4.0 或更高版本
【发布时间】:2022-01-08 16:35:09
【问题描述】:

我参考this site 创建了以下makefile 来尝试自动生成依赖项。当我使用 GNU Make 3.81(macOS 附带的那个)运行它时,我的 makefile 工作正常。但是,如果我使用较新版本的 GNU Make(4.3,通过 Homebrew 安装)运行它,则每次运行 make 时都会重新创建所有对象和依赖文件,即使源文件没有变化。

CXX          := clang++
CXXFLAGS     := -Wall -Werror -std=c++11
ICUROOT      := ${HOME}/opt/icu4c
XERCESCROOT  := ${HOME}/opt/xercesc
CPPFLAGS     := -I$(ICUROOT)/include -I$(XERCESCROOT)/include
LDFLAGS      := -lxerces-c -licuuc
LDLIBS       := -L$(ICUROOT)/lib -L$(XERCESCROOT)/lib
OBJDIR       := obj
DEPDIR       := $(OBJDIR)/.deps
DEPFLAGS      = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d

SRCS = $(wildcard *.cpp)


.PHONY: all
all: DOMPrint

COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

$(OBJDIR)/%.o: %.cpp
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(DEPDIR)
    $(COMPILE.cc) $(OUTPUT_OPTION) $<

$(DEPDIR): ; @mkdir -p $@

DEPFILES := $(SRCS:%.cpp=$(DEPDIR)/%.d)
$(DEPFILES): ;

DOMPrint: $(addprefix $(OBJDIR)/,DOMPrint.o DOMPrintFilter.o DOMPrintErrorHandler.o DOMTreeErrorReporter.o)
    $(CXX) $(CXXFLAGS) $(LDLIBS) $(LDFLAGS) -o $@ $^


-include $(wildcard $(DEPFILES))

make --debug(partial)打印的调试信息,显示依赖文件比目标文件新,重建目标文件:

GNU Make 4.3
Built for arm-apple-darwin21.1.0
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Updating makefiles....
Updating goal targets....
 File 'all' does not exist.
     Prerequisite 'obj/.deps/DOMPrintErrorHandler.d' is newer than target 'obj/DOMPrintErrorHandler.o'.
    Must remake target 'obj/DOMPrintErrorHandler.o'.
clang++ -MT obj/DOMPrintErrorHandler.o -MMD -MP -MF obj/.deps/DOMPrintErrorHandler.d -Wall -Werror -std=c++11 -I/Users/chunwaichan/opt/icu4c/include -I/Users/chunwaichan/opt/xercesc/include  -c -o obj/DOMPrintErrorHandler.o DOMPrintErrorHandler.cpp
    Successfully remade target file 'obj/DOMPrintErrorHandler.o'.
...

但是,查看下面(部分)make -p 输出中的最后修改时间,依赖文件并不比目标文件更新,但自动变量 ? 确实表明它更新。

obj/DOMPrintErrorHandler.o: DOMPrintErrorHandler.cpp obj/.deps/DOMPrintErrorHandler.d ...
#  Implicit rule search has been done.
#  Implicit/static pattern stem: 'DOMPrintErrorHandler'
#  Last modified 2022-01-08 10:50:25.517904773
#  File has been updated.
#  Successfully updated.
# automatic
# ? := obj/.deps/DOMPrintErrorHandler.d

obj/.deps/DOMPrintErrorHandler.d:
#  Implicit rule search has not been done.
#  Last modified 2022-01-08 09:45:13.890973263
#  File has been updated.
#  Successfully updated.
#  recipe to execute (from 'Makefile', line 32):

我已经用 GNU Make 3.81 到 4.3 测试了这个 makefile,从 4.0 版开始,这个问题发生了。我想知道较新版本的 GNU Make 中的哪些更改/增强功能导致了此问题,以及如何修复我的 makefile 以使其兼容。

【问题讨论】:

  • 您确定在其他地方(例如编译器)不会出现行为差异吗?因为我的第一个倾向是猜测 make 关于 depfile 比目标文件更新的说法是正确的,因此(相对于所提供的 makefile)重建目标文件是正确的。
  • 另请注意,您引用的页面提到这是一个可能的问题并提供了解决方案。向下滚动到“处理异常情况”部分。
  • @JohnBollinger 我在同一个目录中运行 GNU Make 3.81 和 4.3,makefile/compiler(clang++) 也是一样的。我会尝试gcc 看看它是否会给出不同的结果。

标签: makefile gnu-make


【解决方案1】:

我无法在我的 GNU/Linux 系统上重现您使用标准版 GNU make 看到的行为。这是我使用的完整复制案例:

$ cat main.h
#define RET 0

$ cat main.cpp
#include "main.h"
int main() { return RET; }

$ cat Makefile
CXX          := g++
CXXFLAGS     := -Wall -Werror -std=c++11
CPPFLAGS     := -I.
OBJDIR       := obj
DEPDIR       := $(OBJDIR)/.deps
DEPFLAGS      = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d

COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:%.cpp=$(OBJDIR)/%.o)
DEPS = $(SRCS:%.cpp=$(DEPDIR)/%.d)

all: main

$(OBJDIR)/%.o: %.cpp
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(DEPDIR) ; $(COMPILE.cc) $(OUTPUT_OPTION) $<

$(DEPDIR): ; @mkdir -p $@

$(DEPS): ;

main: $(OBJS) ; $(CXX) $(CXXFLAGS) $(LDLIBS) $(LDFLAGS) -o $@ $^

clean: ; rm -rf obj main

-include $(wildcard $(DEPS))

$ make
g++ -MT obj/main.o -MMD -MP -MF obj/.deps/foo.d -Wall -Werror -std=c++11 -I.  -c -o obj/main.o main.cpp
g++ -Wall -Werror -std=c++11   -o main obj/main.o

$ make
make: Nothing to be done for 'all'.

还有点奇怪:我的make -p 的输出没有显示任何自动变量,如您的示例所示:

obj/main.o: main.cpp obj/.deps/main.d main.cpp main.h | obj/.deps
#  Implicit rule search has been done.
#  Implicit/static pattern stem: 'main'
#  Last modified 2022-01-08 14:18:05.5745307
#  File has been updated.
#  Successfully updated.
#  recipe to execute (from 'Makefile', line 19):
    $(COMPILE.cc) $(OUTPUT_OPTION) $<

也许您使用的 GNU 版本有一些本地更改?

如果你从 https://ftp.gnu.org/gnu/make/ 下载 vanilla GNU make 源代码并编译它是否可以正常工作?

【讨论】:

  • 我已经尝试使用从源代码编译的香草 GNU Make,但它仍然无法在 GNU Make 4.0 或更高版本中正常工作。
  • 如果我把编译器改成g++就可以了,而且我也注意到生成的依赖文件略有不同。在clang++创建的所有依赖文件中,每条规则后面都有一个空行。我尝试使用sed -i '/^$/d' *.d 手动删除空行并使用touch *.o 更新目标文件的时间戳,以使目标文件比依赖文件更新并再次运行make。这次不再重建目标文件。
  • 我无法解释这一点:额外的空行没有区别:空行被忽略。当然,如果clang 写出.d 文件的时间戳比.o 文件更新,这将导致这个问题,毫无疑问......如果这是真的,那是clang 中的一个错误。但是没有理由为什么这种行为会在 GNU make 3.82 和 GNU make 4.0 之间发生变化,所以我无法解释为什么它适用于旧版本。
  • @bryccw,不接触所有*.o 文件本身 导致make 不重建这些文件?如果是这样,那么您的测试并没有说明 depfile 中的差异是否与您描述的问题有关。
  • 我假设他们正在修改编译规则,以便在编译完成后只触及刚刚构建的目标文件。但我发现我可能假设太多了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多