(作为同一条船上其他人的参考)问题是*.obj 通配符。
如果一个目标有多个(推理)规则,那么只有一个可以合理地应用于创建/更新它。
现在,干净状态的构建逻辑如下(简化):
需要构建$(binPath),看看它依赖什么...
那里是*.obj,所以我们不要仔细看...
不匹配任何现有文件;检查我们是否可以以某种方式构建它...
1234563不是.SUFFIXES 的顺序,而是makefile 中重要的规则顺序)...
-
因此,在规则中执行编译器以从其匹配源创建*.obj,即不可扩展的文字*.c:
→cl /c /Fo($outdir) *.c
太好了,*.obj 现已准备就绪,继续链接...
好的,最终目标,并且(即使对于未定义的外部因素,链接现在可能会失败)这就是我们所能做的,所以让我们收工吧。
注意,替代规则甚至从未出现过!
现在,对于更新,假设不存在损坏(不完整)的目标,但上面构建的目标文件是,甚至还有一些更新,包括一些 .c 和 .cpp 文件:
需要构建缺失的$(binPath),看看它依赖什么...
又是*.obj,它现在可以匹配现有文件,所以让我们(一一)检查它们是否需要更新...
然后,再次有相同的推理规则(无论哪个;一个项目只能有一个,让我们坚持.c)匹配有问题的.obj目标,因此检查相应@中的更改987654340@ 来源...
-
找到了,所以“运行编译器”,但这次 NMAKE 足够聪明,只通过$< 宏提供更新的 C 源代码:
→cl /c /Fo($outdir) some.c other.c
(注意:它甚至足够聪明(显然,这里有 VS 2019)可以继续并默认使用 batch-mode,即使它不是明确的批处理规则。)
好的,*.obj 现在又是最新的了,继续链接...
最终目标,和以前一样,工作结束,庆祝! (我们现在假设链接已经成功,只是为了下面的其余示例。)
再次重申:其他规则从来不需要/用于任何事情。
现在,奇怪的是,您甚至可以开始一个一个地删除 .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 通配符构建初始“完整”对象集,知道在我们开始添加源的那一刻,它就会变得不完整——即通过该行为希望通配符支持!;))