1

当我构建我的项目时,我希望在编译进度中看到所有单元重新编译。当我“制作”项目时,我希望只重新编译源代码发生更改的那些单元。当我在完整构建后立即制作时,我希望看到链接发生,而不是别的。

出于某种原因,Delphi 已经开始考虑重新编译某些单元。我注意到过去的主要是idIOHandler.pas- 印地的一部分。它有时会编译其他的——总是 Indy 中的某个单元。我徒劳地在 Indy 源文件夹中搜索带有错误日期戳的文件。

(有时我会看到相反的问题 - 我知道我已经更改的源单元没有重新编译。我把它归结为我的 PC 和保存源的服务器之间的时间差异。)

这不是什么大问题,但我很想听听解释。

4

3 回答 3

3

这完全取决于 Delphi 如何决定需要重新编译的东西。

我已经看到件事导致了这种情况(在完全非 Delphi 的环境中)。由于某种原因,这些文件被赋予了未来的日期,依赖于它们的文件将始终重新编译。

例如,假设由于某种原因myprog.c被赋予了 2027 年的日期。当你第一次编译它(到myprog.o)时,结果给出了今天的 2013 年日期。

下次你去 make 时,日期在那个myprog.c之后myprog.o,因此它会重新编译。

不确定这是否是导致您的问题的原因,但可能值得研究。

您需要注意的另一件事。如果您的单元依赖于接口已更改的另一个单元,它将被重新编译。另请参阅this answer to a different question,但它也提供了与此问题相关的信息。

大多数时候我倾向于做完整的构建,因为我不信任计算机——我对它们进行编程,所以我知道它们有多狡猾:-)

于 2013-04-28T06:48:40.777 回答
2

不幸的是,Delphi 编译器及其依赖项的源代码是封闭源代码,所以这个问题需要推测。但是,如果您有兴趣避免重新编译 Indy 单元,您当然可以只将 DCU 文件放在库路径中,并从搜索路径(项目级别)和库路径(全局 IDE 级别)中删除 Indy 源文件配置。如果您确实跨映射的网络驱动器编译源代码,那么我认为您疯了。我建议您查看 mercurial 和这个很酷的功能,您可以在其中克隆存储库并将所有源代码的副本完全保存在您正在构建的同一台计算机上。这个很酷。

无论如何,编译器......到目前为止我观察到的是:

  1. 对你(和我)看起来像是重新编译的东西实际上可能只是对接口部分的扫描,实际上是构建依赖树的方式。与 C(makefile)或 Java(ant 或 maven)风格的构建环境不同,至少在代码的接口部分设置编译器是找出特定模块的所有依赖项的唯一方法。我认为,即使您从搜索路径中删除了 Indy 单元,您仍然可能会看到编译器进度显示“编译” IdIOHandler.pas,但在这种情况下,它要做的是加载您的接口部分的编译表示来自 DCU 文件的帕斯卡单位,然后决定要读取哪些其他文件。有时 IDE 进度会在单元上显示“正在编译”,但最后不会修改 DCU 文件。

  2. 在经历了一些重大变更后,在为重构或修复大型项目而进行大规模战斗时,我观察到以下情况:猜测下一个语法错误将在哪里破坏编译是非常困难的。湾。编译器在单元 A 中发生致命错误,然后在单元 B 中途中断,然后在单元 A 上,然后在单元 C 上,然后在单元 B 上,这表明编译器与您基于 C 编译器的心理模型完全不同在 makefile 上会是这样的:首先我们编译单元 A,然后我们编译单元 B,然后我们编译单元 D。我所拥有的所有证据表明,真正的过程远比这复杂得多,而且编译器团队的某个人在里面将在文件 A 上调用“单程”,“扫描”其他文件内容或扫描其他文件' 内存中的内部表示(而不是物理文件内容的解析)导致“编译”不是全部或全部或一次全部的事情,而是对所有人都免费的大内存,并且编译器的顺序单元评估由接口构建的一些内存树驱动,然后是每个单元的实现子句。在现实世界的应用程序中预测这个大型循环图的行为超出了我的技能。

于 2013-04-28T13:12:44.830 回答
1

正如 Warren 所说,单元编译顺序是非常不可预测的(从人类的角度来看,我确信编译器是确定性的)并且接口和实现是单独的编译阶段(因为解析实现使用子句可以强制在继续之前编译其他单元与实现),但我希望这两个部分中的每一个都可以一次编译。

偏离这一点的唯一原因(我从未观察到,但从未搜索过)将是 delphi 编译器内的多个“编译”工作线程。

其他复杂因素:

  1. 循环/相互依赖,也是允许的(实现 A 使用 B 而 B 接口使用 A)类型。直接的 A <->B dep 通常可以正确检测到,但较大的循环可能会导致重新编译。
  2. 多个目录中的源和/或(几乎)相同的包含文件的多个副本(indy 为此而臭名昭著)。不同的包含文件可以启用稍微不同的编译器选项,在某些情况下可能会(?)触发重新编译。
  3. 内联,泛型。(即跨接口<->实现分离,在使用代码处理之前可能需要实现信息)
  4. .dpr 中的文件路径有一些影响。例如,选择以多种形式存在的文件的特定副本。(您在新路径中签出,但 dpr 中的绝对路径指向旧签出)。在进行部分构建时,这在命令行构建中尤为明显。(建筑单位,而不是计划/图书馆项目)
  5. (不完全确定。我有时觉得 Delphi 可以找到在 IDE 中打开的文件,即使它们不在搜索路径中。这也可能在多个副本的情况下引发选择性行为)

最重要的两个是#1 和#2,在 FPC 中的效果比在 Delphi 中强。(Delphi 中更好的循环逻辑?)。重新编译 Lazarus 时,过度重新编译是很正常的。

#3 来自理论,#4 有时出现在由批处理文件编译的较大树中。由于内联在形式上是可选的,所以它不应该是一个问题,但是如果它至少试图超出所需的范围,则没有记录。

于 2013-04-28T14:50:09.637 回答