Where do I define symbols tested with {$IFDEF}? - delphi

When I use Delphi directives in code, like:
{$IFDEF something}
.
.
.
{$ENDIF}
Where do I assign the word 'something' in the project? I tried in some places in project options but it didn't work. Guess I didn't find the correct one.

It's in the Conditional Defines slot under Project | Options, which looks like this on D2010:

Other answers have pointed you at the places to define symbols and the scope implications of the different approaches.
However, what no-one has yet mentioned is that if you change the DEFINE symbols you MUST do a FULL BUILD of your project for them to have any effect on your code.
When you "Compile" the Delphi compiler will only compile units which have themselves changed since the previous compile. If you change DEFINE symbols this doesn't change any project units, so if the units are not re-compiled then the change in DEFINE symbols will not have ANY effect in those units.
To FORCE changes in DEFINE symbols to be applied in ALL units, you MUST "build", not compile.
This may explain why your attempt to set defines did not appear to work previously

You can also define them in {$DEFINE <symbol>} directives. What changes is the scope. When you define a <symbol> under conditional defines in the project options, the scope is global to the whole project. $DEFINE directives are valid only from the point they are declared to the end of the current module, or until an $UNDEF directive using the same <symbol> is encountered. What to use depends on your needs, and what the IFDEF does.

There are two places where you can put conditional defines that are used in all units of a project:
in the project options (as David Heffernan already said)
in an include file that is included in all of these units
Why do I mention the second option? Because it allows specialized processing based on the VERxxx conditional define and other conditional defines given in 1. See jedi.inc (from the Jedi JCL) for an example.
Also, as Deltics said: When it determines which units to recompile, the compiler only checks whether the unit itself has changed, not whether the conditional defines or any include files have changed. So if you change conditional defines, you must do a rebuild, not just a recompile. Since the Delphi compiler is very fast, this fortunately does not make much of a difference for compile times.

You can define global symbols in external file with .inc extension.
Create a new text file, put in it all you defines and save it as for instance Predefines.inc:
--- Content of the file Predefines.inc ---
{$DEFINE Symbol}
{$IFDEF Symbol}
{$DEFINE AnotherSymbol}
{$ENDIF}
In you Delphi modules, where you need to check are symbols defined, put this code in interface section:
interface
{$I Predefines.inc}
uses ...
// Check you defines
{$IFDEF Symbol}
...
{$ENDIF}

Related

Compiler Directive IF and IFEND

I have a project group containing three projects (two exe and one dll). Now, in one unit which is shared by the two exe-projects, I want to compile a specific region ONLY when in a specific project. How can I achieve this? Should I do something like this?
{$IF PROJECT1}
// Compile this code
{$IFEND}
I have never used Delphi compiler directives before.
Compiler directives are defined at the project level (Project > Options > Delphi Compiler > Compiling), so as long as you do define the directive, it should work as you describe.

Is it possible to find out position of a unit in project's uses clause?

I have two units (SuperPuper.pas and SuperPuper777.pas) in a project (.exe or .dll)
Is it possible to find out at runtime from my code in SuperPuper777.pas that
SuperPuper.pas is listed in project's uses clause;
SuperPuper.pas is first unit in project's uses clause.
The question was heavily edited. I guess that it's practical purpose is to find out if ShareMem.pas unit was declared in right position in project's uses clause.
If you want to enforce the correct declaration of a unit in a project's uses clause I would add a pre-build event to run a regex based Perl/Python/Ruby script. The script would do a simple regex based check of the .dpr file and return an error if it was not as intended. Not fool-proof, but probably the best balance of utility for a small amount of effort.
I realise that your question asks for runtime detection but this is a compile time property and so best attacked at compile time.
You can get a list of all units linked to the executable (i.e. at runtime) from the resources. There is a resource named PACKAGEINFO which contains a list of all units. You can find some reverse information from here. Perhaps you can get this information from enhanced RTTI (available since Delphi 2010).
About how to detect that an unit is first in the .dpr uses clause, I do not see any way of doing it at runtime easily. The list in PACKAGEINFO is not in this order. You can do that at compile time, by parsing the .dpr content and checking its uses clause.
The only way I see to guess which unit was first set is to use a global variable in a common unit:
var LatestUnitSet: (oneUnit, anotherUnit);
Then in the initialization section of each unit:
initialization
LatestUnitSet := OneUnit;
...
initialization
LatestUnitSet := anotherUnit;
...
Then check for LatestUnitSet to see which one was initialized the latest.

Can I undefine a conditional in the command line?

In the help of Delphi 7 command line compiler, I just see an option to define a conditional compiling directive:
-D<syms> = Define conditionals
Is it possible to undefine a conditional?
I have defined DEVELOPMENT in the IDE, and want to have define just PRODUCTION in the command line compiler. The problem is that the command line compiler keeps the IDE definitions. I'd like just to undef it as is possible in the C world.
If you don't come up with a command line option, you can always add additional conditionals in your units or include file like this:
{$DEFINE FOO}
// Allow us to undefine foo at the command line by defining UNDEFFOO
{$IFDEF UNDEFFOO}
{$UNDEF FOO}
{$ENDIF}
{$IFDEF FOO}
...
{$ENDIF}
Then use -D to set UNDEFFOO.
You can use {$UNDEF NAME} to undefine a symbol, equivalent to #undef in C and C++. The facility to undefine a conditional is only applicable at a unit level and cannot be applied project wide. In other words you cannot unset conditionals at the command line. This is no different from the facilities offered by C or C++ toolsets.
A very common approach is to locate all your conditional definitions in a shared .inc file which is then included at the head of every source file that relies on those definitions. If you arrange things this way then you have all the flexibility that you need.
I know it will be of little consolation, but more recent Delphi versions have much stronger support for configuration management. Modern Delphi versions make use of the msbuild system. They allow the same configuration options to be used in the IDE and on the command line. There is flexibility to define, for example, debug and release build options and switch between them easily. I know I find it a great reassurance to know, for sure, that I am using the same build in the IDE as on the command line. I did not feel anywhere near so secure in legacy Delphi versions.

Including client files for shared file

Okay, so I have 2 projects for a game. One is the server and one is the client. I keep the shared units into a shared folder that I use to include in my client/server project. There is a problem however: I have a shared file that needs a different file for client / server. Example: mySharedLib needs to print to the console, however the client/server console is different. What are my options? Thanks
In your shared file you could use define compiler directive
For example
{$IFDEF MYSERVER}
Writeln('Server'); // this code executes
{$ELSE}
Writeln('Client'); // this code does not execute
{$ENDIF}
Then in your server project define a MYSERVER define and in your client define a MYCLIENT one, then when the shared code seperates use an {$IFDEF) statement.
From the Delphi help on conditional definitions:
The conditional directives $IFDEF, $IFNDEF, $IF, $ELSEIF, $ELSE, $ENDIF, and $IFEND allow you to compile or suppress code based on the status of a conditional symbol. $IF and $ELSEIF allow you to base conditional compilation on declared Delphi identifiers. $IFOPT compiles or suppresses code depending on whether a specified compiler switch is enabled.
This will not however work if the shared code is in a DLL or any other sort of complied shared resource such as a package.
From the Delphi help on conditional definitions:
Conditional definitions are evaluated only when source code is recompiled. If you change a conditional symbol's status and then rebuild a project, source code in unchanged units may not be recompiled. Use Project|Build All Projects to ensure everything in your project reflects the current status of conditional symbols.
If they're different, they're not really a shared file anymore.

How to patch a method in Classes.pas

I need to patch a method in Classes.pas
(TReader.ReadString - I want to force it to use a specified codepage, not the system default).
If I copy Classes.pas into my project,I will end up having to rebuild the entire VCL. Is there any (easy) way to patch a method at runtime?
Modifying the implementation side of Classes.pas will not require recompiling everything. Delphi figures out if a unit needs to be recompiled by an algorithm that looks roughly like this:
If DCU found:
Is DCU format out of date (old version of compiler)? If so, need source to recompile or compile-time error.
Is the source on the path? If so, if it's newer than the DCU, recompile
For each used unit:
Repeat analysis when loading
For each used symbol ("import": type, variable, routine, initialized constant etc.) from that unit:
Is symbol version of import different to symbol found in used unit? If so, recompile needed.
If DCU is not found, source will need to be found and compiled, otherwise compile-time error
The important concept is that of symbol version. When saving a DCU, Delphi calculates a hash based on the interface declaration of the symbol and associates it with the symbol. Other units that use the symbol also store the symbol version. In this way, link-time conflicts caused by stale symbols are avoided, unlike most C linkers.
The upshot of this is that you should be able to add Classes.pas to your project and modify its implementation section almost to your heart's content, and still be able to statically link with the rest of the RTL and VCL and third-party libraries, even those provided in object format only.
Things to be careful of:
Inlined routines; the body of inlined routines are part of the symbol version
Generics; the implementation side of generic types and methods are part of the respective symbol versions
I found VCLFixPack:
https://www.idefixpack.de/blog/bugfix-units/vclfixpack-10/
I used the techniques from this to replace the method I wanted to patch at runtime.

Resources