【问题标题】:Make ignoring Prerequisite that doesn't exist忽略不存在的先决条件
【发布时间】:2014-05-30 22:24:32
【问题描述】:

make 继续构建并说当我的依赖文件说对象依赖于已移动的头文件时一切都是最新的。

如果运行make -d 来捕获我看到的评估:

Considering target file `../build/out/src/manager.o'.
     Looking for an implicit rule for `../build/out/src/manager.o'.
     No implicit rule found for `../build/out/src/manager.o'.
      Pruning file `../product/build/config/product.conf'.
      Pruning file `../build/out/opt_cc.txt'.
      Considering target file `../mem/src/manager.c'.
       Looking for an implicit rule for `../mem/src/manager.c'.
       No implicit rule found for `../mem/src/manager.c'.
       Finished prerequisites of target file `../mem/src/manager.c'.
      No need to remake target `../mem/src/manager.c'.
      Pruning file `../mem/mem.h'.
     Finished prerequisites of target file `../build/out/src/manager.o'.
     Prerequisite `../product/build/config/product.conf' is older than target `../build/out/src/manager.o'.
     Prerequisite `../build/out/opt_cc.txt' is older than target `../build/out/src/manager.o'.
     Prerequisite `../mem/src/manager.c' is older than target `../build/out/src/manager.o'.
     Prerequisite `../mem/mem.h' of target `../build/out/src/manager.o' does not exist.
../build/out/src/manager.o'.
     Prerequisite `../mem/mem_in.h' is older than target `../build/out/src/manager.o'.
    No need to remake target `../build/out/src/manager.o'.

所以 make 知道该文件是需要的,但不存在,但不会尝试根据规则创建它或失败。

Prerequisite `../mem/mem.h' of target `../build/out/src/manager.o' does not exist.

为什么会这样,我怎样才能不忽略这条规则?

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    您很可能已经实现了一种自动依赖生成方法,该方法通过在没有规则的情况下为该文件定义目标来告诉 make 基本上忽略这些文件(如果它们不存在)。当我有这个makefile时:

    foo: foo.h ; @echo make $@ from $^
    

    没有foo.h 然后make 告诉我:

    $ make
    make: **** No rule to make target 'foo.h', needed by 'foo'.  Stop.
    

    但是,如果我有 这个 makefile:

    foo: foo.h ; @echo make $@ from $^
    foo.h:
    

    现在 make 非常高兴:

    $ make
    make foo from foo.h
    

    这是许多自动依赖生成实用程序所依赖的记录在案的行为:如果您查看生成的依赖生成文件,您会看到每个头文件都有一个空目标。

    这个想法是,在给定正确的依赖信息的情况下,永远不应该在不修改其他源文件或头文件的情况下重命名或删除头文件,这将导致目标文件无论如何都要重新构建(因此正确地重新创建依赖信息下一次)。

    【讨论】:

    • 依赖文件只是“../build/out/src/manager.o : ../mem/mem.h”。使用 -p 运行 make 我发现 make 已决定标头是中间先决条件?即使在为该特定头文件制作目标时也是如此。该目标未运行
    • 我现在似乎可以通过使用空命令定义规则 '%.h : deps' 并且其中 deps 是虚假目标来实现此功能
    • 来自制作手册:虚假目标不应该是真实目标文件的先决条件;如果是,则每次 make 更新该文件时都会运行其配方。检查版本 3.81 和当前版本。
    • @TJOlaes 我不确定这与这个问题/答案有什么关系?这里没有虚假目标。
    • 我在回复@Oliver 关于他的解决方法的评论,而不是你的答案。
    【解决方案2】:

    我发现的解决方法是使用静态模式规则而不是模式规则。模式规则如下所示:

    %.o : %.c
        *recipe here*
    

    静态模式规则仅适用于目标文件的显式列表,如下所示:

    $(OBJECTS): %.o: %.c
        *recipe here*
    

    其中变量OBJECTS在前面的makefile中定义为目标文件列表(以空格分隔),例如:

    OBJECTS := src/fileA.c src/fileB.c src/fileC.c
    

    请注意,您可以使用各种make 实用程序函数来构建目标文件列表。比如$(wildcard pattern)$(addsuffix)等。

    你没有显示你的 makefile 的相关部分,所以我不能确定这会解决你的情况。但是我得到了与make 忽略不存在的先决条件相同的症状,以及来自make -d 的关于“先决条件不存在”的相同消息,但随后make 对创建先决条件没有任何作用。

    请注意,make 包含大约 90 个内置的隐式规则,其中大部分是模式规则。因此,即使您的 makefile 没有模式规则,这仍然可能会影响您。

    我注意到了一些事情:

    1. 要求明确使先决条件有效。这表明make 知道如何构建先决条件。

    2. 为特定文件添加显式目标语句(不使用模式规则)似乎可以解决问题。 (但当然只针对那个目标...为 100 个目标中的每一个编写规则是行不通的)。

    3. 在我的研究中,我不断遇到关于make 的行为在有无明确目标时有何不同的说法。

    4. 使用make -r 关闭隐式规则数据库没有帮助。

    我的猜测是,静态模式规则是针对整个目标列表的显式目标语句的一种形式。

    对于我正在调查的特定情况,目标文件已经存在,构建目标实际上不需要(不存在的)先决条件,它只在运行时需要。所以也许make 关于不需要构建先决条件来构建目标是正确的。如果您只要求构建目标,那么 make 将做最少的必要工作来完成它。

    这似乎仍然是make 中的一个错误。但这可能只是我的误解,我不是make专家。

    更新:(2016 年 3 月 16 日)。 我目前的理解是 make 将这些必备文件视为中间文件,因此不关心在何时创建它们目标文件已经存在。 Make 将中间文件视为“二等公民”,它仅在需要创建“一等公民”时创建它们,这些文件是 makefile 中定义的显式目标,或者在命令行上作为 make 的参数请求的目标.

    通过使用静态模式规则(而不是模式规则),这些文件现在被列为显式目标,因此不是中间文件,而是“一等公民”必要时创建和更新。

    顺便说一句,这也解释了为什么在没有先决条件的情况下使用 .SECONDARY 没有帮助。这样做可以防止中间文件被删除,但它们仍被视为中间文件,因此make 不会创建它们,除非需要它们来制定目标或目标。

    【讨论】:

    • 补充一点(三年后...),链式规则 上的GNU Make manual's 信息解释了这个问题,与上面的描述相匹配。 Make 将模式匹配的目标视为intermediates,在制作“真实”目标时它可能会忽略(甚至删除)它。你仍然可以定义一个隐式规则,但是对于你真正想要重建的中间体,你必须有一个明确的目标。
    • 对于您不想重建的任何文件,您必须有一个明确的目标是不正确的。如果目标在 makefile 中明确提及,Make 将认为目标是明确的:它不必作为目标。例如,将它们列为其他目标的先决条件就足够了:现在它们不再被视为中间目标。
    【解决方案3】:

    我在我的代码库中研究了这个问题很长一段时间,但找不到与我的情况相匹配的任何原因(或解决方案)(无论我对该主题进行了多少次搜索)。 @owler,带有“更新:(2016 年 3 月 16 日)”附录,提示了语句“[在没有先决条件的情况下使用 .SECONDARY] 可以防止删除中间文件,但它们仍然被视为中间所以 make 不会创建它们,除非需要它们来制定目标或目标。”

    实际上,“中间”文件的另一个特征实际上导致了我遇到的问题。

    生成文件如下所示:

    .SECONDARY:
    
    x.exe: x.o
            $(RECIPESTEP1)
    
    x.o: x.c
            $(RECIPESTEP2)
    

    在我的例子中,x.o 是没有被重建的目标(即使它被明确列为 make 目标 x.exe 的依赖项)。更重要的是(为了理解问题),x.exe 确实已经存在。

    “中间”目标的完整行为描述是“中间”目标将被构建如果需要它们来构建依赖目标AND如果必须重建“中间”目标的依赖项。在我的例子中,'x.c' 文件已经存在并且没有依赖关系,所以 make 决定不需要重建 'x.c'。由于所有目标(包括“x.o”)都被标记为“中级”(由于 .SECONDARY: 目标没有依赖关系)并且不需要重新构建“x.c”,因此 make 假设一个最新的 ' x.o' 已经用于构建 'x.exe'(因为 'x.exe' 已经存在)。因此,make 不会(重新)构建 'x.o'。

    最后,是@Owler 的评论和对https://www.gnu.org/software/make/manual/html_node/Special-Targets.html 参考页面的(仔细)审查最终找到了解决方案。

    请注意,我提供了一个简化的 makefile,但在实际的 makefile 中,x.o 是根据基于 x.c 的隐式规则构建的 - 静态模式规则无法解决我遇到的问题。

    我的解决方案是删除 .SECONDARY: 目标。展望未来,计划是在必要时仅指定 .SECONDARY: 与特定依赖项。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-10
      • 1970-01-01
      • 2022-09-24
      • 1970-01-01
      • 1970-01-01
      • 2011-01-27
      • 2021-11-18
      相关资源
      最近更新 更多