Delphi and conditional compilation
Posted by jpluimers on 2020/10/15
There are various ways for Delphi code to verify what features are available.
Historically, testing for the existence of VER###
defines with $IFDEF
or $IFNDEF
is the oldest means, and as of Delphi 6, you can also test for the existence and values of identifiers is $IF defined
, $IF not defined
and, especially for CompilerVersion
and RTLVersion
.
My versioned PowerShell script List-Delphi-Installed-Packages.ps1 tries to keep an up to date list of versions and features starting with BDS 1 (which was C# Builder) and BDS 2 (which was Delphi 8 with
VER160
). One day I will make it Pascal based in stead of BDS based.The JEDI Code Library has kept a versioned JEDI.INC up to date since Delphi 1.0.
Binding those to specific features can be a tough thing when you depend on version numbers, but less hard when you rely on feature names.
Every couple of years, people start proposing units to replace include files, usually with an argument like this:
If you forget to include it into your source code, all your IFDEFS will fail and in the worst case your workaround won’t be active (the best case is that the compiler runs into an error so you will notice the missing include).
The problem is that for such a unit to work:
- you have to always use it (debunking the above argument)
- you will need to have the very latest version, even if you use old compilers (so you can use code written for any Delphi version)
- See the quoted below “Do you expect that the old versions will get an update to know the new constants RTLVersion_Atlantis = 99.0; too?” below
- you need a central place where that version is available (like
JEDI.INC
) - it will only work with booleans
- See the quote below “Which is also backwards compatible because the compiler (at least in XE and up, haven’t checked any older versions) just evaluates a non existing value as False.”
- it requires $IF, which is a pain in the ass
- See the quote below “Using
$IF
is a major pita because of$IFEND
or$ENDIF
depending on compiler version and$LEGACYIFEND
setting.”
- See the quote below “Using
The only good thing is what Rudy Velthuis commented:
checking for a
$DEFINE
likeDELPHI_RIO_UP
can go wrong. If you have a typo, it will simply not be recognized as defined and compile the wrong code. Checking for{$IF CompilerVersion >= some_constant}
will fail to compile ifsome_constant
is not defined
A few people tried:
- 2018: [WayBack] Conditional compilation for various Delphi versions – twm’s blog
- 2017: [WayBack] from old school to new frontier: FDK – RELEASE – DAY via [WayBack] from old school to new frontier: {$IFDEF Version Problem
- 2016: [WayBack] On Conditional Compilation – The Art of Delphi Programming
JEDI.INC
You might think that JEDI.INC was only introduced in 2003, but it is in fact much older as the JEDI Code Library had its own version control system (initially called FreeVCS) before first switching to SVN and later to GIT.
So these are only part of the history:
- while located in jcl/source (from 2003 until 2008)
- while located in jcl/source/include (from 2008 until 2010)
- while located in jedi (as of 2008)
VER### got introduced as VER40 in Turbo Pascal 4
The product naming mess now completely has disconnected people from binding it to their Delphi version.
It helps knowing that VER### is the compiler version starting with Turbo Pascal 1, and remembering that Delphi 1 had VER80
, and the three digits only started with Delphi 3 which introduced VER100
.
Turbo Pascal 1 through 3 did not have any VER##
defined, despite some sites
The first ever VER##
conditional define was VER40
was introduced in Turbo Pascal 4, as you can see in Full text of “borland :: turbo pascal :: Turbo Pascal Version 4.0 Owners Manual 1987” (or PDF via borland :: turbo pascal :: Turbo Pascal Version 4.0 Owners Manual 1987 : Free Download, Borrow, and Streaming : Internet Archive)
Related:
- [WayBack] Compiler Versions – RAD Studio
- [WayBack] Delphi version info table; need help with these” ProjectVersion for C# Builder, Delphi 2005 and 2006. DllSuffix for C# Builder. ProductSummaries · GitHub
- [WayBack1/WayBack2] If an up-to-date version of that unit would indeed have been distributed with every new Delphi version and your code makes use of f.i. the CompilerVersi… – Uwe Raabe – Google+
- [WayBack] If you are maintaining a library, component or plugin for various Delphi versions you will sooner or later hit a point where it becomes necessary to hav… – Thomas Mueller (dummzeuch) – Google+
JEDI History:
- [WayBack] Report to the Delphi Community on Project JEDI: Alan C. Moore, the Project JEDI director, provides an update on the many projects under development with the Project JEDI team
- [WayBack] www.matthias-thoma.com
–jeroen
[WayBack] http://www.uweraabe.de/Blog/2016/01/22/on-conditional-compilation/
How will this be helpful baking these constants into the System.pas?
Do you expect that the old versions will get an update to know the new constants RTLVersion_Atlantis = 99.0; too?
{$IF RTLVersion < RTLVersion_Atlantis}
Result := nil;
{$ELSE}
Result := TBrainReader.Create( User );
{$ENDIF}
This will not compile unless you define the constant.
There should be an official VersionConst.inc/VersionConst.pas (simply including the inc) managed by EMBA and published through a VCS you can link to.
That would be helpful.
What’s wrong with using jedi.inc?
There is nothing wrong …
{$IF CompilerVersion >= CompilerVersion_XE2}
is just more readable to me than
{$IFDEF DELPHI16_UP}
I know DELPHI16_UP includes also we are for sure dealing with Delphi
+Uwe Raabe https://github.com/project-jedi/jedi?files=1 has files exposing both version and feature based defines. The main file jedi.inc is for Delphi and FPC. A smaller file is for Kylix only. It’s maintained well.
Actually, the feature based conditionals are much more usable that the version based ones. The programmer shouldn’t have to worry about which language feature or library was introduced with which Delphi version. That can be solved once.
+Jeroen Wiert Pluimers And a feature like `record helper` doesn’t tell about the existence of TStringHelper or TIntegerHelper, let alone the level of implementation (like string.ToHexString f.i). There may be cases when such a feature define is sufficient, but I doubt it will cover the majority of needs.
+Uwe Raabe can you do `if defined(TStringHelper)` ?
+Jeroen Wiert Pluimers Probably not. Seems to return `false` in Seattle while not Defined succeeds.
+Nicholas Ring Both indeed – it should be added as well as it would not solve that issue. Actually I don’t see that much benefit in these feature defines, which might just be caused by my workflow. When I implement a new feature or extend or refactor existing code I usually code for the latest version. Then I step down to each previous version and react when the compiler complains. Therefore I can easily spot the affected version to introduce a condition. Because I mostly go for RTLVersion a simple Ctrl-Click reveals the number to check for. It’s plain easy and I rarely (never?) have the need for an include file.
Using
$IF
is a major pita because of$IFEND
or$ENDIF
depending on compiler version and$LEGACYIFEND
setting.+Uwe Raabe The compiler versions are wrong. They are being counted since TP and thus Seattle is 30. This means unless EMBT decides to progress the RTL independently from the compiler and change the way they are increasing these numbers both are exactly the same.
+Oliver Münzberg How is
{$IF CompilerVersion >= CompilerVersion_XE2}
any more readable than
{$IFDEF DelphiXE2_UP}
So if you want to do the $IF approach then get rid with the version checking yourself and declare these consts as following:
const
DelphiXE = CompilerVersion = 22;
DelphiXE2 = CompilerVersion = 23;
DelphiXE3 = CompilerVersion = 24;
DelphiXE4 = CompilerVersion = 25;
DelphiXE5 = CompilerVersion = 26;
DelphiXE6 = CompilerVersion = 27;
DelphiXE7 = CompilerVersion = 28;
DelphiXE8 = CompilerVersion = 29;
Delphi10Seattle = CompilerVersion = 30;DelphiXEOrGreater = CompilerVersion >= 22;
DelphiXE2OrGreater = CompilerVersion >= 23;
DelphiXE3OrGreater = CompilerVersion >= 24;
DelphiXE4OrGreater = CompilerVersion >= 25;
DelphiXE5OrGreater = CompilerVersion >= 26;
DelphiXE6OrGreater = CompilerVersion >= 27;
DelphiXE7OrGreater = CompilerVersion >= 28;
DelphiXE8OrGreater = CompilerVersion >= 29;
Delphi10SeattleOrGreater = CompilerVersion >= 30;Now you can just write:
{$IF DelphiXE3OrGreater}
result := S.ToUpper;
{$ELSE}
result := Uppercase(S);
{$IFEND}Which is also backwards compatible because the compiler (at least in XE and up, haven’t checked any older versions) just evaluates a non existing value as False.
Leave a comment