为什么要在我的头文件先决条件的末尾附加“.o”?

Why is make appending '.o' to the end of my header file prerequisite?

我正在尝试使用 make 构建一个包含子目录的项目,我已经使递归 make 部分正常工作,但出于某种原因,它似乎需要源文件依赖项的先决条件并附加 .o给他们然后尝试编译它们,这显然不起作用,为什么要这样做?

有问题的规则如下所示:

operations_cache.o : memory_management/operations_cache.cpp memory_management/operations_cache.hpp \
    function.hpp operations/operation_base.hpp
    cd memory_management && $(MAKE) $@

编译operations目录的规则如下所示:

operation_%.o : function.hpp
    cd operations && $(MAKE) $@

出于某种原因,make 一直试图说 operations/operation_base.hpp.o 是一个有效的目标,即使我没有在 make 文件中的任何地方列出它。我已经阅读了 的文档,但我没有在其中看到任何关于尝试根据先决条件文件名隐式定义对象的内容,所以我非常困惑是什么迫使它这样做。

我得到的错误是这样的:

g++    -c -o main.o main.cpp
g++    -c -o node.o node.cpp
cd memory_management && make unique_table.o
make[1]: Entering directory './memory_management'
g++ -o ../unique_table.o -c unique_table.cpp
make[1]: Leaving directory './memory_management'
cd operations && make operations/operation_base.hpp.o
make[1]: Entering directory './operations'
g++ -o ../operations/operation_base.hpp.o -c 
g++: fatal error: no input files
compilation terminated.
Makefile:10: recipe for target 'operations/operation_base.hpp.o' failed
make[1]: *** [operations/operation_base.hpp.o] Error 1
make[1]: Leaving directory './operations'
Makefile:24: recipe for target 'operations/operation_base.hpp.o' failed
make: *** [operations/operation_base.hpp.o] Error 2

编辑添加完整文件

应用户要求,这是我的 3 个目录的完整 make 文件

./Makefile

CC = g++
objects = main.o node.o unique_table.o operations_cache.o function.o operation_base.o operation_and.o
# shared_lib = nodelib.so

all : edit # $(shared_lib)

edit : $(objects)
    $(CC) -o edit $(objects)

main.o : main.cpp node.hpp memory_management/unique_table.hpp memory_management/operations_cache.hpp

node.o : node.cpp node.hpp

unique_table.o : memory_management/unique_table.cpp memory_management/unique_table.hpp node.hpp
    cd memory_management && $(MAKE) $@

operations_cache.o : memory_management/operations_cache.cpp memory_management/operations_cache.hpp \
    function.hpp operations/operation_base.hpp
    cd memory_management && $(MAKE) $@

function.o : function.cpp function.hpp node.hpp memory_management/unique_table.hpp

operation_%.o : function.hpp
    cd operations && $(MAKE) $@

.PHONY : clean
clean :
    rm edit $(objects)

./operations/Makefile

CC = g++
objects = operation_base.o operation_and.o operation_or.o operation_xor.o operation_restrict.o operation_composition.o operation_satisfy.o operation_satisfy_all.o
proj_dir = ../

operation_base.o : operation_base.cpp operation_base.hpp $(proj_dir)function.hpp

operation_and.o : operation_and.cpp operation_and.hpp operation_base.hpp $(proj_dir)function.hpp

operation_or.o : operation_or.cpp operation_or.hpp operation_base.hpp $(proj_dir)function.hpp

operation_xor.o : operation_xor.cpp operation_xor.hpp operation_base.hpp $(proj_dir)function.hpp

operation_composition.o : operation_composition.cpp operation_composition.hpp operation_base.hpp $(proj_dir)function.hpp

operation_restrict.o : operation_restrict.cpp operation_restrict.hpp operation_base.hpp $(proj_dir)function.hpp

operation_satisfy.o : operation_satisfy.cpp operation_satisfy.hpp operation_base.hpp $(proj_dir)function.hpp

operation_satisfy_all.o : operation_satisfy_all.cpp operation_satisfy_all.hpp operation_satisfy.hpp

%.o : 
    $(CC) -o $(proj_dir)$@ -c $<

./memory_management/Makefile

CC = g++
objects = operations_cache.o unique_table.o
proj_dir = ../

operations_cache.o : operations_cache.cpp operations_cache.hpp \
    $(proj_dir)function.hpp $(proj_dir)operations/operation_base.hpp

unique_table.o : unique_table.cpp unique_table.hpp $(proj_dir)node.hpp

%.o : 
    $(CC) -o $(proj_dir)$@ -c $<

编辑我找到了解决方案

operations_cache.o 行删除 memory_management/operations_cache.cpp memory_management/operations_cache.hpp function.hpp operations/operation_base.hpp 解决了问题,它没有解释为什么会出现错误,但它有效,我仍然有兴趣了解为什么会发生这种情况.

那是因为您的 operations/operation_base.hpp 恰好匹配 operation_%.o 规则并且由于内置规则的事实:

.o:
#  Builtin rule
#  recipe to execute (built-in):
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

它是这样工作的:

  • 检查 operations/operation_base.hpp 是否可以使用显式规则更新(否),
  • 检查所有隐式(模式)规则是否可以解析(候选:内置%: %.o),
  • 检查先决条件operations/operation_base.hpp.o是否可以解决(候选人:operation_%.o: functions.hpp),
  • 检查先决条件 functions.hpp 是否可以解决(是的,文件存在),
  • 遵循规则 operation_%.o: functions.hpp 和 stem operations/base.hpp(执行失败的配方)。

我准备了一个简化的 Makefile 来重现该问题。它要求 functions.hpp 存在并且内置规则已启用。

all: operations/operation_base.hpp

operation_%.o: functions.hpp
        echo Making $@ from $<

输出:

# Only Makefile (no functions.hpp)
$ ls
Makefile
$ make
make: *** No rule to make target 'operations/operation_base.hpp', needed by 'all'.  Stop.

# Create functions.hpp to fulfill pattern rule (this is the case in question)
$ touch functions.hpp
$ make
echo Making operations/operation_base.hpp.o from functions.hpp
Making operations/operation_base.hpp.o from functions.hpp
# The following output comes from built-in rule %: %.o
cc   operations/operation_base.hpp.o   -o operations/operation_base.hpp
make: cc: Command not found
<builtin>: recipe for target 'operations/operation_base.hpp' failed

# The same, but with disabled built-in rules
$ make -r
make: *** No rule to make target 'operations/operation_base.hpp', needed by 'all'.  Stop.

通常当您不知道为什么 make 决定做某事时,运行 它与 -d 调试决策制定和 -p 最终解决 Makefile。它证实了场景:

$ make -d
…
 No implicit rule found for 'all'.
  Considering target file 'operations/operation_base.hpp'.
   File 'operations/operation_base.hpp' does not exist.
   Looking for an implicit rule for 'operations/operation_base.hpp'.
…
   Trying implicit prerequisite 'operations/operation_base.hpp.o'.
   Looking for a rule with intermediate file 'operations/operation_base.hpp.o'.
    Avoiding implicit rule recursion.
    Trying pattern rule with stem 'base.hpp'.
    Trying rule prerequisite 'functions.hpp'.
   Found an implicit rule for 'operations/operation_base.hpp'.
…
     No need to remake target 'functions.hpp'.
   Considering target file 'operations/operation_base.hpp.o'.
    File 'operations/operation_base.hpp.o' does not exist.
     Pruning file 'functions.hpp'.
    Finished prerequisites of target file 'operations/operation_base.hpp.o'.
   Must remake target 'operations/operation_base.hpp.o'.
echo Making operations/operation_base.hpp.o from functions.hpp
…
   Successfully remade target file 'operations/operation_base.hpp.o'.
   Finished prerequisites of target file 'operations/operation_base.hpp'.
  Must remake target 'operations/operation_base.hpp'.
cc   operations/operation_base.hpp.o   -o operations/operation_base.hpp
…
make: cc: Command not found
…
<builtin>: recipe for target 'operations/operation_base.hpp' failed

已解决的片段 Makefile (make -p):

operations/operation_base.hpp: operations/operation_base.hpp.o
#  Implicit rule search has been done.
#  Implicit/static pattern stem: 'operations/operation_base.hpp'
#  Modification time never checked.
#  File has been updated.
#  Failed to be updated.
# automatic
# @ := operations/operation_base.hpp
# automatic
# % :=
# automatic
# * := operations/operation_base.hpp
# automatic
# + := operations/operation_base.hpp.o
# automatic
# | :=
# automatic
# < := operations/operation_base.hpp.o
# automatic
# ^ := operations/operation_base.hpp.o
# automatic
# ? := operations/operation_base.hpp.o
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=1/22=5%
#  recipe to execute (built-in):
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

operations/operation_base.hpp.o: functions.hpp
#  Implicit rule search has been done.
#  Implicit/static pattern stem: 'operations/base.hpp'
#  File is an intermediate prerequisite.
#  File does not exist.
#  File has been updated.
#  Successfully updated.
# automatic
# @ := operations/operation_base.hpp.o
# automatic
# % :=
# automatic
# * := operations/base.hpp
# automatic
# + := functions.hpp
# automatic
# | :=
# automatic
# < := functions.hpp
# automatic
# ^ := functions.hpp
# automatic
# ? := functions.hpp
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=1/13=8%
#  recipe to execute (from 'Makefile', line 4):
        echo Making $@ from $<