Conditional Compilation
Conditional compilation provides a way to have one set of source code that can be compiled slightly differently depending on certain conditions, such as compiling for different platforms, compiling different versions/editions of your project, or, if you must, compiling the same code in Elements and other compilers, for example:
- Oxygene vs. legacy Pascal compilers, such as Delphi or Free Pascal
- RemObjects C# vs. Microsoft Visual C# or Mono's C# Compiler
- RemObjects Silver vs. Apple's Swift compiler
- .NET vs. Cocoa vs. Java
For example:
- The
ECHOES
,TOFFEE
,COOPER
andISLAND
symbols can be used to distinguish between platforms, .NET, Cocoa, Java and Island, respectively. - The
ELEMENTS
symbol can be used to conditionally check for Elements vs. other compilers (e.g. when sharing code with Delphi, Visual C# or Apple's Swift Compiler).
Providing Conditional Defines
Conditional defines can originate from four sources:
-
The compiler will provide a set of predefined symbols based on compiler version and platform, as outlined in the Conditional Defines topics. Examples include
ELEMENTS
(always defined) orCOCOA
(defined for Apple's platforms). -
Referenced libraries or frameworks can provide additional symbols that are active if the respective namespace is used. For example, the iOS SDK provides
TARGET_OS_IPHONE
to distinguish between Mac and iOS, in the implicitly usedrtl
namespace. -
A list of Project-wide defines can be specified in the Project Settings and will be available to check for throughout the project. These defines can be configured separately for each configuration.
-
Finally, defines can be set (and removed) right inside the source code using
{$DEFINE}
(Oxygene) or#define
(C#, Swift and Java) Compiler Directives. Defines set (or removed) with these directives will apply only to the code in the same file and below the directive.
Conditional Compilation w/ defined()
New in Elements 10, the defined()
system function can be used to specify conditional compilation using regular if
statements that integrate naturally with the flow of the code.
Conditional Compilation w/ Directives
Wherever possible, i.e. inside method bodies, using defined()
is the preferred way for conditional compilation. That said, support for conditional directives such as $IF
(Oxygene) and #if
(C#, Swift and Java) is still provided, for use outside of method bodies (e.g. to conditionally omit whole methods or classes).
Conditional compilation is controlled by surrounding blocks of code with $IF
/#if
$ENDIF
/#endif
directives that check for specific conditions. These blocks can be nested within each other.
The $IF
/if
directive checks for the availability of one or more symbols, and begins a block that will conditionally be compiled if (and only if) the expression passed to the directive is found to be true (i.e. defined). Any block started with an "if
" directive must be terminated with a matching $ENDIF
/#endif
directive to close it off (and can optionally also include additional $ELSE
/#else
sections to provide alternate blocks of code that will be considered if the original condition was not met).
New since Elements 5.2 release, the if
(and elseif
) directives accept not only a single symbol name to check for, but can also handle two or more symbols combined with the boolean logical operators (AND
, OR
, XOR
and NOT
in Oxygene, and &&
, ||
, ^
and !
in C#, Swift and Java). This allows for more complex checks against several symbols, without the need to awkwardly nest symbols.
In Oxygene, the $IF
directive replaces the older $IFDEF
and $IFNDEF
directives, which are still supported, but considered legacy.
Examples:
{$IF COOPER} // Compile the following for Java only.
{$IF TOFFEE AND TARGET_OS_IPHONE} // Compile the following for Cocoa / iOS only.
{$IF ECHOES OR COOPER} // Compile the following for .NET and Java (but not Cocoa).
{$IF NOT TOFFEE} // Don't compile the following for Cocoa (but do for .NET and Java).
#if COOPER // Compile the following for Java only.
#if TOFFEE && TARGET_OS_IPHONE // Compile the following for Cocoa / iOS only.
#if ECHOES !! COOPER // Compile the following for .NET and Java (but not Cocoa).
#if !TOFFEE // Don't compile the following for Cocoa (but do for .NET and Java).
#if COOPER // Compile the following for Java only.
#if TOFFEE && TARGET_OS_IPHONE // Compile the following for Cocoa / iOS only.
#if ECHOES || COOPER // Compile the following for .NET and Java (but not Cocoa).
#if !TOFFEE // Don't compile the following for Cocoa (but do for .NET and Java).
#if COOPER // Compile the following for Java only.
#if TOFFEE && TARGET_OS_IPHONE // Compile the following for Cocoa / iOS only.
#if ECHOES || COOPER // Compile the following for .NET and Java (but not Cocoa).
#if !TOFFEE // Don't compile the following for Cocoa (but do for .NET and Java).
The "elseif
" (or "elif
" in C#) directive follows a previous "if
" directive (and optional "elseif
" directives). It closes the previous blocks and starts a new block of code that will be compiled if none of the previous conditions have been met and the condition provided in the directive itself is met.
"elseif
"/"elif
" allow the cascading of multiple cases, comparable to a case statement in regular code, without requiring a convoluted nesting of multiple "if
"/"endif
" directives.
Examples:
{$IF COOPER} // Compile the following for Java only.
{$ELSEIF ECHOES} // Compile the following for .NET only.
{$ELSEIF TOFFEE} // Compile the following for Cocoa only.
{$ELSE} // Compile if neither of the previous three were defined.
{$ENDIF} // Done.
#if COOPER // Compile the following for Java only.
#elif ECHOES // Compile the following for .NET only.
#elif TOFFEE // Compile the following for Cocoa only.
#else // Compile if neither of the previous three were defined.
#endif // Done.
#if COOPER // Compile the following for Java only.
#elseif ECHOES // Compile the following for .NET only.
#elseif TOFFEE // Compile the following for Cocoa only.
#else // Compile if neither of the previous three were defined.
#endif // Done.
#if COOPER // Compile the following for Java only.
#elif ECHOES // Compile the following for .NET only.
#elif TOFFEE // Compile the following for Cocoa only.
#else // Compile if neither of the previous three were defined.
#endif // Done.
The "else
" directive follows a previous "if
" directive (and optional "elseif
"/"elif
" directives). It closes the previous blocks and starts a new block of code that will be compiled if none of the previous conditions have been met. The block needs to be closed with a final "endif
" directive.
Finally, the "endif
" directive, as discussed in the previous sections, is used to close off a conditional section started with if
. Afterwards, compilation will continue unconditionally (or based on any conditions set forth by a nested "if
" directive) once again.
Within an ignored block (i.e. an if
, elseif
or else
block that is not being compiled) all code and all compiler directives except if
, else*
and endif
are ignored.
Oxygene Legacy Directives
{$IFDEF}
— Legacy, use{$IF}
instead.{$IFNDEF}
— Legacy, use{$IF NOT}
instead.{$IFOPT}
— For Delphi compatibility, will always resolve as false.
Examples
begin
{$IFDEF TRIAL}
writeLn('This a trial version!');
{$ELSE}
writeLn('This is the full version');
{$ENDIF}
{
#if TRIAL
Console.WriteLine("This a trial version!");
#else
Console.WriteLine("This is the full version");
#endif
{
#if TRIAL
println("This a trial version!");
#else
println("This is the full version");
#endif
{
#if TRIAL
Console.WriteLine("This a trial version!");
#else
Console.WriteLine("This is the full version");
#endif
begin
{$IFDEF ECHOES}
writeLn('.NET');
{$ELSEIF COOPER}
writeLn('Java');
{$ELSEIF TOFFEE AND TARGET_OS_IPHONE}
writeLn('Cocoa on iOS');
{$ELSEIF TOFFEE}
writeLn('Cocoa and not iOS (i.e. OS X, tvOS or watchOS)');
{$ELSE}
writeLn("Some platform that hasn't been invented yet");
{$ENDIF}
{
#if ECHOES
writeLn(".NET");
#elif COOPER
writeLn("Java");
#elif TOFFEE && TARGET_OS_IPHONE}
writeLn("Cocoa on iOS");
#elif TOFFEE
writeLn("Cocoa and not iOS (i.e. OS X, tvOS or watchOS)");
#else
writeLn("Some platform that hasn't been invented yet");
#endif
{
#if ECHOES
writeLn(".NET");
#elif COOPER
writeLn("Java");
#elif TOFFEE && TARGET_OS_IPHONE}
writeLn("Cocoa on iOS");
#elif TOFFEE
writeLn("Cocoa and not iOS (i.e. OS X, tvOS or watchOS)");
#else
writeLn("Some platform that hasn't been invented yet");
#endif
{
#if ECHOES
writeLn(".NET");
#elif COOPER
writeLn("Java");
#elif TOFFEE && TARGET_OS_IPHONE}
writeLn("Cocoa on iOS");
#elif TOFFEE
writeLn("Cocoa and not iOS (i.e. OS X, tvOS or watchOS)");
#else
writeLn("Some platform that hasn't been invented yet");
#endif
Defining or Undefining Conditionals
The $DEFINE
/#define
directive defines a new symbol for the pre-processor; the defines are position dependent, so the symbol will only be defined for everything after the directive, in the same source file.
{$DEFINE TRIAL}
#define TRIAL
#define TRIAL
#define TRIAL
The $UNDEF
/#undef
directive removes a symbol for the pre-processor, if previously
defined. Like "define
", the directive is position dependent, so it will only affect code after the directive. Undefining can remove any pre-processor symbol, even ones defined by the compiler itself or the project options. When a symbol doesn't exist, the undefine will be ignored.
{$UNDEF TRIAL}
#undef TRIAL
#undef TRIAL
#undef TRIAL
See Also
- Compiler Directives
defined()
System Functionexists()
System Functions