C 从Makefile内部调用另一个Makefile的规则

C 从Makefile内部调用另一个Makefile的规则,c,makefile,compilation,C,Makefile,Compilation,我有一个Makefile,如果某些对象文件不存在,我可以从中运行另一个Makefile的命令 规则如下: $(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON) +$(MAKE) -C $(COMMONDIR) 其中变量在相同的Makefile中定义如下: COMMONDIR := ../common SOURCESCOMMON := $(wildcard $(COMMONDIR

我有一个
Makefile
,如果某些对象文件不存在,我可以从中运行另一个
Makefile
的命令

规则如下:

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
其中变量在相同的
Makefile
中定义如下:

COMMONDIR     := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON  := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))
这个规则很好用,但在一天结束时,该规则唯一需要的真正输入是另一个
Makefile
,因此我尝试了:

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
但这不起作用,为什么

为了完整起见,这里是完整的
Makefile

CC = gcc

INC_PATH = -I../common/

SOURCEDIR := ./
SOURCES := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR  :=./obj
OBJECTS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

COMMONDIR     := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON  := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

# OBJS_LOC is in current working directory,
EXECUTABLE := ../server
# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: $(EXECUTABLE)

clean:
    $(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)

# Linking the executable from the object files
# $^   # "src.c src.h" (all prerequisites)
$(EXECUTABLE): $(OBJECTS) $(OBJECTSCOMMON)
    $(CC) $(WARNING) $^ -o $@

-include $(DEPENDS) $(DEPENDSCOMMON)

$(OBJDIR):
    mkdir -p $(OBJDIR)

$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
    $(CC) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $@

$(OBJDIRCOMMON):
    mkdir -p $(OBJDIRCOMMON)

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

从中我了解到另一个
Makefile
的执行是成功的。然而,在那之后,它试图执行这个命令,
gcc../common/obj/error.d.o-o../common/obj/error.d
,这是错误的,但我不知道是哪个规则以及它为什么生成它。

应该有&而不是管道吗

      $(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | (OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR

你为什么做错了

配方A

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
和配方B

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)
具有本质上不同的含义,肯定不会产生相同的行为

食谱A上写着:

  • 任何目标
    $(OBJDIRCOMMON)/file.o
    如果不存在,则必须是最新的 或者早于
    $(COMMONDIR)/file.c
    $(COMMONDIR)/Makefile
  • 如果目标
    $(OBJDIRCOMMON)/file.o
    必须是最新的,则
    $(OBJDIRCOMMON)
    必须首先更新
  • 要使目标
    $(OBJDIRCOMMON)/file.o更新,请在shell中运行
    $(make)-C$(COMMONDIR)
    的扩展
  • 食谱B上写着:

  • 任何目标
    $(OBJDIRCOMMON)/file.o
    如果不存在,则必须是最新的 或者早于
    $(COMMONDIR)/Makefile
  • 如果目标
    $(OBJDIRCOMMON)/file.o
    必须是最新的,则
    $(OBJDIRCOMMON)
    必须首先更新
  • 要使目标
    $(OBJDIRCOMMON)/file.o更新,请在shell中执行
    $(make)-C$(COMMONDIR)
    的扩展
  • 请注意,标准A.1与标准B.1不同。将执行配方A 如果
    $(OBJDIRCOMMON)/file.o
    早于
    $(OBJDIRCOMMON)/file.c
    。配方B将不起作用。 配方B放弃目标文件对相应源文件的依赖关系, 并告诉Make只有在满足以下条件时才能重新生成
    $(OBJDIRCOMMON)/file.o
    早于
    $(COMMONDIR)/Makefile

    在一天结束时,规则所需的唯一真正输入是另一个Makefile

    这里您所说的“规则”实际上是命令行(扩展自)
    $(MAKE)-C$(COMMONDIR)
    。 这个命令的输入是一件事;执行它的标准是另一个

    您所做的是如何导致您看到的错误的

    这更棘手。让我们复制一下

    这是一个玩具笔:

    $ ls -R
    .:
    app  common
    
    ./app:
    foo.c  main.c  Makefile
    
    ./common:
    bar.c  Makefile
    
    这里,
    /app/Makefile
    正是您的配方A的Makefile<代码>/common/Makefile
    , 您没有发布的内容只是:

    obj/bar.o: bar.c
        gcc -MMD -MP -c -I. $< -o $@
    
    这很好

    现在,我将
    /app/Makefile
    更改为使用recipeB,然后重新构建

    $ make
    gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
    gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
    gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server
    
    还是很好。。。但是等一下!那一个没有调用
    /common
    make 毕竟,这是变化可能会影响的因素。更好的
    清洁

    $ make clean
    rm -f ./obj/foo.o ./obj/main.o ./obj/foo.d ./obj/main.d ../server
    
    然后重试:

    $ make
    gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
    gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
    gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server
    
    没有区别?啊,那是因为这个Makefile的
    clean
    无法删除所有
    生成的文件
    生成:它省略了
    。/common/obj/bar.o
    。所以我只想:

    $ rm ../common/obj/*
    
    再来一次:

    $ make
    make -C ../common
    make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
    gcc -MMD -MP -c -I. bar.c -o obj/bar.o
    make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
    gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d
    gcc: error: ../common/obj/bar.d.o: No such file or directory
    gcc: fatal error: no input files
    compilation terminated.
    
    这是你的秘密

    当我清除
    。/common/obj
    文件时,我不仅删除了其中的所有对象文件 还有依赖项文件
    。/common/obj/bar.d
    。现在Make正试图通过运行以下命令来重新制作:

    gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d
    
    $(MAKE) -C $(COMMONDIR)
    
    为什么?为了回答这个问题,我们首先将
    /app/Makefile
    更改回使用recipeA -考虑它完成-然后做:< /P>
    $ make --print-data-base > out.txt
    
    它将所有从阅读中收集到的信息转储到
    out.txt
    中 makefiles(
    Makefile
    及其递归包含的所有makefiles
    -s, 在这种情况下,只需自动生成
    .d
    文件)

    让我们看看数据库对
    。/common/obj/bar.d
    的看法。它说:

    # Not a target:
    ../common/obj/bar.d:
    # Implicit rule search has been done.
    # Last modified 2019-01-11 16:01:33.199263608
    # File has been updated.
    # Successfully updated.
    
    当然,我们不希望
    。/common/obj/bar.d
    成为目标,它也不是目标 因为读取了所有makefile并考虑了其所有内置规则, 而且它能找到的所有文件,Make都看不到
    。/common/obj/bar.d
    必须对这些文件中的任何文件进行更新。好

    现在让我们回到< <强> > b>强> <代码> ./APP/MaMeCuff再次-考虑它完成了 再次强调:

    $ make --print-data-base > out.txt
    
    再次查看
    out.txt
    中有关
    。/common/obj/bar.d
    。这次我们发现:

    ../common/obj/bar.d: ../common/obj/bar.d.o
    # Implicit rule search has been done.
    #  Implicit/static pattern stem: '../common/obj/bar.d'
    # Last modified 2019-01-11 16:01:33.199263608
    # File has been updated.
    # Successfully updated.
    #  recipe to execute (built-in):
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    所以这次
    。/common/obj/bar.d
    是一个目标!它取决于
    。/common/obj/bar.d.o
    ! 制作它的配方是:

        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    当然,这将扩展到:

    gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d
    
    多亏了配方B,我们是如何做到这一点的

    首先,它考虑的是makefile中的任何规则还是 内置规则使它可以直接从任何现有文件中生成
    。/common/obj/bar.d
    , 然后画了一张空白

    接下来,它考虑了这些规则中是否有任何一条给了它一条解决问题的途径 将
    。/common/obj/bar.d
    从。中间文件是一个不存在但自身可以生成的文件 从现有文件中,通过它已读取的任何规则或其内置规则。这 它终于找到了出路

    Make的内置模式规则之一是:

    %: %.o
    #  recipe to execute (built-in):
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    你可以在
    out中找到它
    
    make -C ../common
    make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
    gcc -MMD -MP -c -I. bar.c -o obj/bar.o
    make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
    
    ../common/obj/bar.d: ../common/obj/bar.d.o
        $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
    
    gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d
    
    make -C ../common
    
    gcc: error: ../common/obj/bar.d.o: No such file or directory
    
    $(OBJDIRCOMMON)/bar.d.o: $(OBJDIRCOMMON)/bar.d.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
        +$(MAKE) -C $(COMMONDIR)