Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi conditional compilation in uses clause

I am trying to modify my Delphi 2010 code to compile in XE7 (and want to retain the ability to compile it in 2010). So in the unit that houses my mainform I added conditional directives. The following works fine in 2010

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

But XE7 automatically adds a System.Actions at the end to create a uses clause that now has System.Actions declared twice (see below), and gives an error message [dcc32 Error] MyForm.pas(10): E2004 Identifier redeclared: 'System.Actions'. Why is XE7 not accepting the unit from within the conditional directive ?

uses 
  {$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs,
  System.Actions; // <- automatically added
like image 287
ss2006 Avatar asked Oct 01 '14 23:10

ss2006


3 Answers

As Ken says, the interface uses clause will be modified by the IDE and the processes by which this is achieved are somewhat less than sophisticated (as you have discovered). The same problem affects the project uses clause. Unfortunately this is much harder to avoid in the case of Form/DataModule uses clauses.

You could use a Unit Alias (see David Heffernan's answer) but need to be aware that if you create an alias for a unit that the IDE wishes to add, then the IDE will still add a reference to the required unit since it does not recognise the alias as identifying that required unit. Aliasing to the System unit will avoid this since it is already (implicitly) used by every unit.

Another alternative is to remove all such conditionals from your uses list and instead create place-holder units as required so that the different compilers you wish to use on the project can each be satisfied by the single uses list combined from the list that each IDE insists is required (the IDE won't remove unused units from the uses list, something that is often a complaint but in this case actually helps solve your problem).

In this case, in your Delphi 2010 project create an empty Actions unit:

 unit Actions;
 interface
 implementation
 end.

You will of course need to ensure that this unit is not in the project path for your XE7 version of the project.

One way to achieve that would be ensure that the empty Actions.pas unit is not explicitly listed in the DPR uses list, but is placed in a subfolder of your project source (e.g. 'placeholders'). You can then add this subfolder to the project search path for the Delphi 2010 version but not the XE7 version:

 \Project Folder

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

     \placeholders
          Actions.pas

If you find that you need placeholders for each of the different versions then you will need separate placeholder folders. You might create further version specific subfolders, for example:

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

This sort of structure might be advisable simply from the point of view of creating an auto/self documenting organisation.

Note that this is only required for dealing with unit references in the uses clause of the interface section of Forms (or Frames etc). In non-visual units or in the implementation section, the IDE does not interfere so conditional compilation directives should present no issues there.

like image 104
Deltics Avatar answered Oct 20 '22 22:10

Deltics


The easiest way to fix this is to add a unit alias to your Delphi 2010 project. You'll need to use different .dproj files for your different Delphi versions, but you need to do that anyway.

In the unit aliases settings for the Delphi 2010 project add this:

Actions=System

I'm using System as the alias target because the System unit is automatically included in every Delphi unit and so aliased inclusions are benign. It's the simplest way that I can think of to make the compiler effectively ignore an entry in a uses clause.

Then you can declare your uses clause like this:

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

This will compile fine in Delphi 2010, because the alias processing will map Actions onto System. In XE7 you are also fine because there is no alias, and the IDE is satisfied by the presence of the Actions unit and so feels no compulsion to modify the uses clause.

like image 36
David Heffernan Avatar answered Oct 20 '22 23:10

David Heffernan


Would there be something wrong with

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

or is this an academic argument?

Addendum...

Then add a dummy System.Actions.dcu containing nothing into your 2010 compile-path.

I'd theorise that the IDE would then insist on inserting uses ... System.Actions, 2010 has what it wants, XE7 has what it wants.

But I don't have XE7 so I can't test it out.

like image 45
Magoo Avatar answered Oct 20 '22 22:10

Magoo