Delphi编译和构建在同一个项目上生成不同的二进制文件

7
在一个新的VCL应用程序中,编译构建操作会产生相同的二进制和映射文件(即使“在项目中包括版本信息”选项已关闭,.exe文件末尾可能会有轻微差异-已经讨论过)。映射文件是一模一样的。但是,当我添加任何第三方组件时,由Build和Compile产生的二进制和映射(!)文件明显不同!
在两个Delphi版本上进行测试: - 版本7.0(Build 8.1) - CodeGear™ RAD Studio 2007 Version 11.0.2902.10471 (+December 2007 Update)
重现步骤:
1. 创建新的VCL应用程序。可能添加任何本机Delphi组件(我尝试了来自标准、附加、Win32和系统选项卡的所有组件)。 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和selfmade),我们会得到更多不同的.exe和不同的.map文件。
为什么?有什么建议吗?
更新 关于我如何发现这个问题以及为什么它引起我的兴趣的一些信息: 项目是通过MSBuild从简单脚本构建的。当在项目中添加了ITE(具有资源的dll)翻译时,我发现当项目被构建(从脚本或IDE中)时 - 翻译版本工作不正常 - 某些文本在按钮、标签等上获取了错误的位置(字面上来自另一个按钮、标签)。当从IDE编译项目时 - 一切都好。所以我开始比较Build和Compile输出...

没有头绪,但是从更简单的第三方组件开始测试,然后再转向更复杂的组件。同时确保源代码完全相同。不同的USE子句(包括顺序!)可能会改变单元初始化表的布局,从而影响二进制文件。 - Marco van de Voort
源代码保持100%不变 - 我只进行构建和编译。 - user244027
дҪ иҝҳдјҡеҸ‘зҺ°*Build*е’ҢBuildз”ҹжҲҗдёҚеҗҢзҡ„дәҢиҝӣеҲ¶ж–Ү件гҖӮиҝҷдёҺзј–иҜ‘е’Ңжһ„е»әзҡ„еҢәеҲ«ж— е…ігҖӮ - Rob Kennedy
@Rob:你说的 Build 和 Build 产生不同的二进制文件是什么意思?你是指 Compile 和 Build 吗? - Vivian Mills
1
不,Ryan。Max说如果你先构建再编译,你会得到不同的结果。但是如果你先构建再次构建,你仍然会得到不同的结果。在这种情况下,编译和构建之间的区别是无关紧要的。 - Rob Kennedy
显示剩余2条评论
3个回答

12
您所看到的只是编译器内置制造逻辑的一种结果。当您进行构建时,会告诉编译器构建所有可用的源文件。因此,Delphi将处理每个源文件,并为其找到源代码的uses列表中的每个单元构建该文件。它会递归地执行此操作。当您进行编译时,仅加载现有的.dcu文件,如果它们已被发现为最新版本,则不进行任何操作。这实际上可能导致发现单位的顺序不同,因为每个.dcu将有效地“展平”使用列表。由于单位是以不同的顺序发现和加载的,因此它们以不同的顺序链接。这就是为什么您的映射文件看起来如此不同的原因。给定相同的源代码,如果连续进行两次构建或两次编译,则映射文件应该是相同的。

其他差异的原因更加普通,包括PE头时间戳和其他位填充和对齐等内容。


3
那不是完全违背初衷吗?此外,典型的 Delphi 应用程序会链接到你可能完全不知道其存在的单元。链接顺序是由编译器符号表的内部排序确定的,这对编译器的工作至关重要。为什么不为你的“官方”构建使用自动化构建过程,这些构建始终从原始源代码构建(即构建系统上不存在 dcu 文件)? - Allen Bauer
你是否在编译器中使用了 --drc 选项来生成 .drc 文件?这是一个由编译器生成的文件,描述了所有资源字符串及其标识符。该文件可以通过 brcc32 处理并运行,以生成翻译后的资源字符串。 - Allen Bauer
不,我只是使用Project->Languages->Add...(或File->New->Other...->Delphi Projects->Resource DLL Wizard),IDE会生成一个新项目(dll与资源 - 不仅包含resourcestring,还包括dfm中所有对象的已发布属性)基于主项目。 - user244027
@Allen: 顺便说一下,帮助文件没有描述资源ID可能会如何改变。使用更新的项目(+3个新的资源字符串)和旧的资源DLL是否可以?如果我不添加资源字符串/编辑DFM,而是编辑源代码并进行多次构建 - ID会改变吗? - Alex
@Alex,是的,资源ID可能会发生变化。最明显的是,链接顺序的更改(由于上述行为)可能会改变实际的字符串ID分配。编译器总是从最高值向下分配资源字符串ID。而手动管理资源字符串,则通常会逐个计数。这使您能够手动管理某些字符串并使用资源字符串。这将最小化字符串ID冲突的风险。 - Allen Bauer
显示剩余2条评论

3
我认为这个答案有两个部分。
首先,据我所知,问题的一部分是编译器在进行编译/构建之前不会将内存清零。因此,未初始化内存中留下的任何内容都会成为输出中的填充物,以实现对齐。
其次,我也记得在应用程序的pe头信息中包含了日期时间戳,这将导致每次都有所不同。
我不是最适合确认这一点的人,但这是我从过去的讨论中记得的。
像Allen Bauer或Barry Kelly这样的人可能能够提供更好/更准确的信息。

回复 Ryan:你所说的日期时间戳是正确的——同一项目上的每个构建都会生成不同的可执行文件(差异位于文件末尾附近),但地图文件相同。但是,编译和构建.exe之间的区别更加复杂,并且存在.map文件之间的差异(例如,某些函数放置在不同的地址处)。 - user244027

0
如果您在项目中使用编译器定义,并只是更改了这些定义,如果进行编译,您将看不到dcu文件和生成的模块(exe或dll)的任何更改。如果进行完整重建,编译器定义将用于新创建的dcu文件和模块中。
我曾在一个大型项目组中见过这种情况,我们在不同的项目中使用不同的定义,并且所有的dcu文件都存储在同一个目录中。
因此:在这种情况下,编译器不会强制执行对定义的依赖关系。
也许您遇到了相同的问题。

我认为这不是问题所在 - 即使在新的清新的vcl应用程序中,我的问题仍然会重复出现。 - user244027

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接