【问题标题】:NMake inference rules with both c and cpp files带有 c 和 cpp 文件的 NMake 推理规则
【发布时间】:2017-07-10 01:45:39
【问题描述】:

我有一个包含 c 和 cpp 文件的项目,并且我一直在使用 NMake 进行构建。我的问题是,如果我有两个推理规则,每个文件类型一个,

{$(dirSrc)}.c{$(dirObj)}.obj:
    cl /nologo /c /EHsc /Fo$(dirObj)\ $<

{$(dirSrc)}.cpp{$(dirObj)}.obj:
    cl /nologo /c /EHsc /Fo$(dirObj)\ $<

$(binPath): $(dirObj)\*.obj
    link /nologo /dll /out:$(binPath) $(dirObj)\*.obj

只有 c 文件被编译,大概是因为 .c 扩展名在 .SUFFIXES 列表中排在第一位。

我当然可以简单地将 c 文件的扩展名更改为 cpp,但我想知道是否有人知道调用这两个规则的方法。

【问题讨论】:

  • 为什么投反对票?

标签: nmake


【解决方案1】:

好吧,回答我自己的问题,我能想到的最好的办法是编译到 2 个单独的目录,然后在运行链接器时指向这两个目录。

{$(dirSrc)}.c{$(dirObj)\c}.obj:
    cl /nologo /c /EHsc /Fo$(dirObj)\c\ $<

{$(dirSrc)}.cpp{$(dirObj)\cpp}.obj:
    cl /nologo /c /EHsc /Fo$(dirObj)\cpp\ $<

$(binPath): $(dirObj)\c\*.obj $(dirObj)\cpp\*.obj
    link /nologo /dll /out:$(binPath) $(dirObj)\c\*.obj $(dirObj)\cpp\*.obj

【讨论】:

    【解决方案2】:

    (作为同一条船上其他人的参考)问题是*.obj 通配符。

    如果一个目标有多个(推理)规则,那么只有一个可以合理地应用于创建/更新它。

    现在,干净状态的构建逻辑如下(简化)

    1. 需要构建$(binPath),看看它依赖什么...

    2. 那里是*.obj,所以我们不要仔细看...

    3. 不匹配任何现有文件;检查我们是否可以以某种方式构建它...

    4. 1234563不是.SUFFIXES 的顺序,而是makefile 中重要的规则顺序)...
    5. 因此,在规则中执行编译器以从其匹配源创建*.obj,即不可扩展的文字*.c

      cl /c /Fo($outdir) *.c

    6. 太好了,*.obj 现已准备就绪,继续链接...

    7. 好的,最终目标,并且(即使对于未定义的外部因素,链接现在可能会失败)这就是我们所能做的,所以让我们收工吧。

    注意,替代规则甚至从未出现过!

    现在,对于更新,假设不存在损坏(不完整)的目标,但上面构建的目标文件是,甚至还有一些更新,包括一些 .c.cpp 文件:

    1. 需要构建缺失的$(binPath),看看它依赖什么...

    2. 又是*.obj,它现在可以匹配现有文件,所以让我们(一一)检查它们是否需要更新...

    3. 然后,再次有相同的推理规则(无论哪个;一个项目只能有一个,让我们坚持.c)匹配有问题的.obj目标,因此检查相应@中的更改987654340@ 来源...

    4. 找到了,所以“运行编译器”,但这次 NMAKE 足够聪明,只通过$&lt; 宏提供更新的 C 源代码:

      cl /c /Fo($outdir) some.c other.c

      (注意:它甚至足够聪明(显然,这里有 VS 2019)可以继续并默认使用 batch-mode,即使它不是明确的批处理规则。)

    5. 好的,*.obj 现在又是最新的了,继续链接...

    6. 最终目标,和以前一样,工作结束,庆祝! (我们现在假设链接已经成功,只是为了下面的其余示例。)

    再次重申:其他规则从来不需要/用于任何事情。

    现在,奇怪的是,您甚至可以开始一个一个地删除 .obj 文件(只要还有一些),让 NMAKE 不为所动:

    'test.dll' is up-to-date
    

    什么?!为什么丢失的不重建?

    你知道吗?让我们把它们都删掉吧……而且,只是为了好玩,用一个假文件替换它们,方法是(从任何地方)复制一些随机文件,将其重命名为fake.obj

    'test.dll' is up-to-date
    

    耶稣!这毫无意义!

    好的,让我们结束吧。然后创建一个全新的.c 文件。那肯定会触发重建!

    'test.dll' is up-to-date
    

    不可能! :-o 到底发生了什么?!也许添加一个新的.cpp 然后......

    'test.dll' is up-to-date
    

    我的天啊!...真是个垃圾!不可思议,这 NMAKE 的东西……对吧?

    嗯...首先,对于fake.obj,没有匹配名称的来源,不适用任何规则:NMAKE 不能为此“发明”来源,所以它永远不会被重建,它只是坐在那里,作为定时炸弹,直到下一轮链接,链接器最终会捡起它并找出它,结束所有的乐趣。 :)

    致所有其他“异常”:

    • 任何现有的 .obj 文件都会满足*.obj 的依赖(对于lib),所以只要至少有一个,NMAKE 就会很高兴,甚至永远不知道这不是完整的清单!

    • 这就是为什么对于添加到项目中的任何新 .c.cpp 什么都不做的原因,因此以这种方式使用通配符是在自取其辱。 (这并不意味着构建脚本中没有任何完全合法的通配符案例,顺便说一句。)

    • 并且(回顾一下),为了重建一个 missing 对象,NMAKE(就像 gmake 等)必须选择一个获胜者,如果有多个匹配规则,则忽略其余的.

    (仅供参考,gmake 甚至有一个专门关于此 wildcard pitfall 的页面。而且,为了降低风险,与 NMAKE 不同,它似乎拒绝为 *.o 通配符构建初始“完整”对象集,知道在我们开始添加源的那一刻,它就会变得不完整——即通过该行为希望通配符支持!;))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-27
      • 2020-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多