【问题标题】:Delphi Compile and Build produce different binary on same projectDelphi 编译和构建在同一个项目上产生不同的二进制文件
【发布时间】:2011-01-02 01:38:19
【问题描述】:

在新的 VCL 应用程序中,编译构建操作生成相同的二进制文件和映射文件(即使“包含版本信息”在 .exe 文件末尾也有细微差别在项目中”选项已关闭 - 已经讨论过)。映射文件是相同的字节到字节。但是当我添加任何第三方组件时,Build 和 Compile 生成的二进制文件和 map(!) 文件有很大不同!

在两个版本的 Delphi 上测试:
- 版本 7.0(构建 8.1)
- CodeGear™ RAD Studio 2007 版本 11.0.2902.10471(+2007 年 12 月更新)

重现步骤:

  1. 创建新的 VCL 应用程序。可能添加任何本机 Delphi 组件(我尝试了 Standart、Additional、Win32 和 System 选项卡中的所有组件)。
  2. 在项目选项的链接器选项卡上打开详细映射文件。
  3. 构建项目
  4. 重命名输出 .exe 和 .map 文件(例如:project1.exe 到 project1b.exe 和 project1.map 到 project1b.map)。
  5. 编译项目
  6. 重命名输出 .exe 和 .map 文件(例如:project1.exe 到 project1c.exe 和 project1.map 到 project1c.map)。
  7. 比较第 4 步和第 6 步中的文件。(我使用 WinMerge 2.12.4.0)。

我们几乎没有不同的 .exe 文件和完全相同的 .map 文件。然后,如果我们再次重复所有步骤,但在项目中使用第三方组件(我尝试 ODAC、DOA、DevExpress 和自制),我们会得到更多不同的 .exe 和不同的 .map 文件。

为什么?有什么建议吗?

更新
关于我是如何发现这个的以及它为什么让我感兴趣的一些信息:
项目是使用 MSBuild 从简单脚本构建的。当在项目中通过 ITE 添加翻译(带有资源的 dll)时,我发现当项目构建时(来自脚本或来自 IDE) - 翻译版本工作错误 - 按钮、标签等上的一些文本来自错误的位置(字面意思是来自另一个按钮、标签)。当项目从 IDE 编译时 - 一切正常。所以我开始比较 Build 和 Compile 输出...

【问题讨论】:

  • 不知道,但是从更简单的 3rd 方组件开始您的测试,然后才去更复杂的组件。还要确保来源保持 100% 相同。不同的USES子句(包括顺序!)可能会改变单元初始化表的布局,从而改变二进制文件。
  • 源保持 100% 相同 - 我只构建和编译。
  • 您还会发现 Build 和 Build 生成不同的二进制文件。它与编译和构建之间的区别无关。
  • @Rob:Build 和 Build 生成不同的二进制文件是什么意思?你的意思是编译和构建?
  • 不,瑞恩。 Max 说如果你构建然后编译,你会得到不同的结果。但是如果你构建然后再次构建,你仍然会得到不同的结果。在这种情况下,编译和构建之间的区别无关紧要。

标签: delphi build compilation map-files


【解决方案1】:

如果您在项目中使用编译器定义并且只是更改了这些,如果您进行编译,您将不会看到对 dcu 和生成的模块(exe 或 dll)的任何更改。如果您进行完全重建,编译器定义将用于新创建的 dcu 和模块。

我在一个大型项目组中看到了这一点,我们在不同项目中使用具有不同定义的模块,并且所有 dcu 都存储在同一目录中。

Ergo:在这种情况下,编译器不会强制依赖定义。

也许您确实看到了同样的问题。

【讨论】:

  • 我认为在这种情况下不会 - 即使在新的新 vcl 应用程序中,我的问题也会重复。
【解决方案2】:

您所看到的只是编译器内置 make 逻辑的产物。当您进行构建时,它会告诉编译器构建所有可用的源。因此,Delphi 处理每个源文件,并为它找到源的使用列表中的每个单元构建该文件。它递归地执行此操作。当您进行编译时,只会加载现有的 .dcu 文件,如果发现它们是最新的,则什么也不做。这实际上会导致发现单元的顺序不同,因为每个 .dcu 都会有效地“扁平化”使用列表。由于这些单元以不同的顺序被发现和加载,它们依次以不同的顺序链接。这就是为什么您的地图文件看起来如此不同的原因。给定相同的源,如果您连续进行两次构建或连续两次编译,则映射文件应该相同。

造成差异的其他原因更为常见,包括 PE 标头时间戳以及其他填充和对齐位。

【讨论】:

  • @Allen:感谢艾伦提供的详细信息,我不知道它的 DCU 部分,但这是有道理的。谢谢你解释。那么是否有可能允许编译器使用编译器提示来指示编译器始终以特定顺序链接 DCU 或 PAS 文件的单元?
  • 那会破坏整个目的,不是吗?此外,典型的 Delphi 应用程序链接到您可能完全不知道的单元甚至存在。链接顺序由编译器符号表本身的内部顺序决定。这是编译器如何工作的关键。为什么不简单地为您的“官方”构建使用自动构建过程,这些构建总是从原始源构建(即构建系统上不存在 dcus)?
  • @Allen:我使用自动构建过程。但是当我向我的项目添加翻译时(使用 Delphi 集成翻译环境 - 带有资源的 dll),它坏了 - 当使用带有翻译资源的 dll 时,UI 看起来像是一些文本标题来自错误的地方。因此,当我(或脚本)进行构建时 - 翻译不起作用。当我编译时 - 翻译工作正常。经过一番调查,我发现了编译和构建之间的区别。我很尴尬……
  • 您是否使用编译器中的 --drc 选项来生成 .drc 文件?这是一个编译器生成的文件,描述了所有资源字符串及其标识符。该文件可以通过 brcc32 处理和运行以生成已翻译的资源字符串。
  • 没有。我只是使用 Project->Languages->Add... (或 File->New->Other...->Delphi Projects->Resource DLL Wizard)和 IDE 生成新项目(带有资源的 dll - 不仅带有资源字符串,而且具有来自 dfm 中所有对象的已发布属性)基于主项目。
【解决方案3】:

我相信这个答案有两个部分。

IIRC,您看到的部分问题是编译器在进行编译/构建之前不会将内存归零。因此,出于对齐目的,未初始化内存中留下的任何内容都将成为输出中的填充物。

我似乎还记得应用程序的 pe 标头信息中包含一个日期时间戳。这将导致每次的差异。

我不是确认这一点的最佳人选,但我似乎从过去的讨论中回想起了这一点。

像 Allen Bauer 或 Barry Kelly 这样的人可能能够提供更好/更准确的信息。

【讨论】:

  • 致 Ryan:关于日期时间戳,您是对的 - 同一项目上的每个构建都会生成不同的可执行文件(差异位于文件末尾附近)但映射文件相同。但是 Build 和 Compile .exe 的区别比较复杂,.map 文件之间存在差异(比如一些函数放在不同的地址)。
猜你喜欢
  • 1970-01-01
  • 2022-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
相关资源
最近更新 更多