具有 c 和 cpp 文件的 NMake 推理规则



我有一个同时包含 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 扩展名在 .后缀列表。

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

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

再加上如果一个目标有多个(推理)规则,则只能合理地应用一个规则来创建/更新它。

现在,干净状态的生成逻辑如下(简化):

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

  2. 有那个*.obj,所以让我们不要仔细看看...

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

  4. 很酷,推理规则说.obj可以从.c(或.cpp构建,真的没关系,先找到哪一个,见下文!顺便说一句,这里重要的不是.SUFFIXES顺序,而是 makefile 中规则的顺序)......

  5. 因此,在规则中执行编译器以从其匹配源(即不可扩展的文字*.c)创建*.obj

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

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

  7. 好的,最终目标,并且(即使链接现在对于未定义的外部可能失败)这就是我们所能做的,所以让我们称之为一天。

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

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

  1. 需要构建缺失的$(binPath),让我们看看它取决于什么...

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

  3. 然后,再次出现相同的推理规则(无论哪种;一个项目只能有一个,让我们坚持使用.c)与相关目标.obj匹配,因此请检查相应.c源中的更改......

  4. 它们被发现了,所以"运行编译器",但这次 NMAKE 足够聪明,只能通过$<宏提供更新的 C 源:

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

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

  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的依赖关系(对于库),所以只要至少有一个,NMAKE 就会很高兴,甚至永远不知道它不是完整的列表!

  • 这就是为什么没有为任何新.c或添加到项目中.cpp做任何事情,所以以这种方式使用通配符是在搬起石头砸自己的脚。(这并不意味着构建脚本中没有任何完全合法的通配符情况,顺便说一句。

  • 而且(回顾一下),为了重建丢失的对象,NMAKE(就像gmake等)必须选择一个赢家,如果有多个匹配规则,则忽略其余的。

(仅供参考,gmake甚至有一个专门关于这个通配符陷阱的页面。而且,为了降低风险,与NMAKE不同,它似乎拒绝为*.o通配符构建初始的"完整"对象集,因为我们知道当我们开始添加源的那一刻,它将变得不完整 - 即通配符希望支持的行为!;) )

好吧,要回答我自己的问题,我能想到的最好的方法是编译到 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

最新更新