【问题标题】:Delphi conditional compilation in uses clauseDelphi条件编译在uses子句中
【发布时间】:2014-11-26 22:32:43
【问题描述】:

我正在尝试修改我的 Delphi 2010 代码以在 XE7 中编译(并希望保留在 2010 年编译它的能力)。因此,在包含我的主窗体的单元中,我添加了条件指令。以下在 2010 年运行良好

uses 
  {$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs;

但 XE7 会自动在末尾添加一个 System.Actions 以创建一个 uses 子句,该子句现在声明了 System.Actions 两次(见下文),并给出错误消息 [dcc32 Error] MyForm.pas(10): E2004 Identifier redeclared: 'System.Actions'。为什么 XE7 不接受条件指令中的单位?

uses 
  {$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs,
  System.Actions; // <- automatically added

【问题讨论】:

  • 因为表单相关单元中的 interface uses 子句属于 IDE 本身,它会为放置在表单上的任何组件添加当前版本所需的任何内容。克服这个问题的唯一方法是从表单中删除组件,将声明放在private 部分而不是默认的published 部分,然后在运行时创建组件。您正在与 IDE 本身作斗争,而且您不会赢得这场战斗。
  • 但是我已经拥有了所有需要的单元,不是吗? Delphi 正在处理{$IF CompilerVersion &gt;= 24}System.Actions{$IFEND} 部分。如果我做了一个像 (`{$IF CompilerVersion >= 24}System.Action*s{$IFEND} 这样的“错别字”,它会抱怨。所以,它知道在接口使用中 System.Actions 的存在。可能是IDE 和 Compiler 不同步?
  • IDE 不知道条件定义。它只是知道你在表单上有某个组件,它在uses子句中找不到那个单元,所以它添加了它。正如我所说,接口使用子句属于IDE,而不是你。如果组件在表单上,​​IDE 将确保该单元直接包含在使用子句中。为了清楚起见,我将第三次重复:表单相关单元中的interfaceuses 子句属于IDE,用于放置在表单上的任何组件。
  • 好的。你问,我提供信息。我不会和你争论,但自从 Delphi 2 发布以来我一直在处理这个问题,我需要支持 16 位和 32 位版本的组件之间的条件定义。 :-) 你正在与 IDE 进行一场失败的战斗——它总是会赢。
  • 顺便说一句,我认为您在这里缺少的部分是 IDE 不会编译代码来确定组件的 uses 子句中是(或不是)什么。它只是解析 use 子句以查看它是否具有表单上组件所需的所有单元,这就是为什么它看不到条件句中包含的内容。 (条件句用于编译器,IDE 不为此目的进行编译。)

标签: delphi delphi-2010 conditional-compilation delphi-xe7


【解决方案1】:

正如 Ken 所说,IDE 将修改 interface uses 子句,实现这一点的过程并不复杂(正如您所发现的那样)。同样的问题会影响项目使用条款。不幸的是,在 Form/DataModule 使用子句的情况下,这很难避免。

您可以使用 Unit Alias(请参阅 David Heffernan 的回答),但需要注意,如果您为 IDE 希望添加的单元创建别名,那么 IDE 仍会添加引用所需的单元,因为它不识别别名来标识所需的单元。 System 单元的别名将避免这种情况,因为它已经(隐式)被每个单元使用。

另一种选择是从您的使用列表中删除所有此类条件,而是根据需要创建占位符单元,以便您希望在项目中使用的不同编译器都可以通过从列表中组合的单一用途列表来满足每个 IDE 都坚持要求(IDE 不会从使用列表中删除未使用的单元,这通常是一种抱怨,但在这种情况下实际上有助于解决您的问题)。

在这种情况下,在您的 Delphi 2010 项目中创建一个空的 Actions 单元:

 unit Actions;
 interface
 implementation
 end.

您当然需要确保该单元不在在您的 XE7 版本项目的项目路径中。

实现此目的的一种方法是确保空的 Actions.pas 单元未明确列在 DPR 使用列表中,而是放置在项目源的子文件夹中(例如“占位符” )。然后,您可以将此子文件夹添加到 Delphi 2010 版本而不是 XE7 版本的项目搜索路径中:

 \Project Folder

     project2010.dpr
     project2010.dproj
     projectXE7.dpr
     projectXE7.dproj

     \placeholders
          Actions.pas

如果您发现每个不同版本都需要占位符,那么您将需要单独的占位符文件夹。您可以创建更多特定于版本的子文件夹,例如:

     \placeholders
          \2010
               Actions.pas
          \XE7
               D2010UnitNotPresentInXE7.pas

从创建自动/自我记录组织的角度来看,这种结构可能是可取的。

请注意,这仅在处理 Formsinterface 部分的 uses 子句(或 框架等)。在非可视单元或 实现 部分中,IDE 不会干扰,因此条件编译指令应该不会出现问题。

【讨论】:

  • 但是,单位别名工作得很好。而且可能更简单。
  • 我在这里有点困惑——@Deltics 似乎在说别名不起作用——“你甚至不能使用单位别名”,大卫说你可以。 Deltics,我没看错吗?
  • 何休,大卫是对的。我的观察 re aliases not working 指的是如果您有 IDE 要添加的单元的别名,那么即使您在使用列表中已经有该单元的别名,IDE 仍会添加该单元。我认为避免混叠到另一个现有单元会导致重复单元错误。我应该测试过它。编译器对重复的单元感到满意,如果您有重复的单元名称,则解析器会抱怨重复的 IDENTIFIER。别名使解析器满意,而编译器则从不关心。
  • 我已经更新了答案以做出这种区分并将读者重定向到大卫的答案,因为这比使用占位符单位要简单得多。 :)
  • 我认为需要这种欺骗处理来支持经典别名:WinTypes=Windows,WinProcs=Windows
【解决方案2】:

解决此问题的最简单方法是将unit alias 添加到您的Delphi 2010 项目中。您需要为不同的 Delphi 版本使用不同的 .dproj 文件,但无论如何您都需要这样做。

在 Delphi 2010 项目的单位别名设置中添加:

Actions=System

我使用System 作为别名目标,因为System 单元自动包含在每个Delphi 单元中,因此别名包含是良性的。这是我能想到的让编译器有效地忽略uses子句中的条目的最简单方法。

然后你可以像这样声明你的uses子句:

uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  Actions, Actnlist;

这将在 Delphi 2010 中正常编译,因为别名处理会将 Actions 映射到 System。在 XE7 中你也很好,因为没有别名,并且 IDE 对 Actions 单元的存在感到满意,因此不需要修改 uses 子句。

【讨论】:

  • 从 OP 问题来看,Delphi XE7 没有 Actnlist 单元,因此不清楚为什么 XE7 IDE 会对 uses 子句中缺少单元感到满意。
  • @user246408 XE7 肯定有Actnlist:docwiki.embarcadero.com/Libraries/XE7/en/Vcl.ActnList
  • 好的,那么你和 Deltic 的答案会回答略有不同的问题。
  • @user246408 我不这么认为。用户可以随意使用ActnList,也可以不使用。问题是关于处理 XE7 IDE 强制 Actions 进入使用子句。我自己遇到的一个问题。 Deltics 提供了另一种方式来安排旧版本(其 VCL 没有 Actions 单元)在使用子句中接受 Actions 单元的存在。我认为你误会了什么。
【解决方案3】:

会不会有什么问题

{$IF CompilerVersion < 24}Actnlist,{$IFEND}

或者这是一个学术论点?

附录...

然后在您的 2010 编译路径中添加一个不包含任何内容的虚拟 System.Actions.dcu

我推测 IDE 会坚持插入 uses ... System.Actions,2010 有它想要的,XE7 有它想要的。

但我没有 XE7,所以无法测试。

【讨论】:

  • 是的。 :-) 这有完全相同的问题 - 它不会在 interface uses 子句中用于与在设计时丢弃在表单上的组件相关的单元。 IDE拥有那个uses子句,任何与它的斗争在它开始之前就失败了。 (阅读发布到原始问题的 cmets。)反转条件(以及受影响的单位)不会改变这一事实。
【解决方案4】:

我们遇到了同样的问题... 最简单的方法就是这样做:

{$IF CompilerVersion < 24}{$ELSE}System.Actions,{$IFEND}
{$IF CompilerVersion >= 24}{$ELSE}Actnlist,{$IFEND}

如果您在旧的 IDE 中打开文件,您可能会看到一个错误,上面写着“未找到单元 X”,但它可以正常编译并且不会执行自动添加。 它看起来不太好,但效果很好......

亲切的问候,

伯纳德

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 2017-08-27
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    • 2015-03-18
    • 1970-01-01
    相关资源
    最近更新 更多