Ever since the Delphi build engine got changed to MS Build in Delphi 2007, many people use Delphi build events. Their order is prebuild, prelink and postbuild (or maybe better spelled pre-build, pre-link and post-build).

Before Delphi 2007, you had to fiddler with project groups and dependencies to fake pre-build and post-build events. For an example see Pre and Post-Build Automation in Delphi.

One of the really good things about these events is that build events appear in the output tab of the messages window.

One of the really bad things is that there is hardly any documentation about the build events.

At least two important things are missing:

How the lines of a build event are actually executed How parameter expansion works inside build events

Let’s explain these.

Executing the source lines of a build event.

I can cut this short very easily: build events are not batch files.

What happens is that all lines in your build event are concatenated together using ampersand (&) signs which are used to execute multiple commands on one command line.

This means that all the fancy control structures (if statements, setlocal, for loops) are not possible inside build events.

The alternative is to call a batch file from a build event, and have your control structures there.

But for that, you have to pass parameters, which leads to the expansion of $() parameters:

Expansion of the $() parameters inside build events

I could not find any official documentation of the $() parameters on the Embarcadero sites.

Ken White took the effort to type the parameters from Delphi 2010 into a StackOverflow answer (that list is gone in Delphi XE, but the bug reports on that got fixed in Delphi XE2 and up), and since then they have not changed:

BDS The environment variable $(BDS) DEFINES The project's conditional defines DIR The environment variable $(DIR) INCLUDEPATH The project's include path INPUTDIR The input file's directory, which always ends in a back-slash INPUTEXT The input file's extension INPUTFILENAME The input file's name, with extension INPUTPATH The input file's full path LOCALCOMMAND Local command entered by user in project manager OUTPUTDIR The output file's directory, which always ends in a back-slash OUTPUTEXT The output file's extension OUTPUTFILENAME The output file's name, with extension OUTPUTNAME The output file's name, without extension OUTPUTPATH The output file's full path Path The environment variable $(PATH) PROJECTDIR The project's directory, which NEVER ends in a back-slash PROJECTEXT The project's extension PROJECTFILENAME The project file's name, with extension PROJECTNAME The project's name PROJECTPATH The project file's full path SAVE Save the input file to disk before it's compiled SystemRoot The environment variable $(SYSTEMROOT), which never ends in a back-slash WINDIR The environment variable $(WINDIR), which never ends in a back-slash

A nice list, but it doesn’t tell you what the values are (apart from the environment variables). So I compared the output of build-events like this:

>> %temp%\pre-build.txt echo BDS: $(BDS) >> %temp%\pre-build.txt echo Config: $(Config) >> %temp%\pre-build.txt echo DEFINES: $(DEFINES) >> %temp%\pre-build.txt echo DIR: $(DIR) >> %temp%\pre-build.txt echo INCLUDEPATH: $(INCLUDEPATH) >> %temp%\pre-build.txt echo INPUTDIR: $(INPUTDIR) >> %temp%\pre-build.txt echo INPUTEXT: $(INPUTEXT) >> %temp%\pre-build.txt echo INPUTFILENAME: $(INPUTFILENAME) >> %temp%\pre-build.txt echo INPUTNAME: $(INPUTNAME) >> %temp%\pre-build.txt echo INPUTPATH: $(INPUTPATH) >> %temp%\pre-build.txt echo LOCALCOMMAND: $(LOCALCOMMAND) >> %temp%\pre-build.txt echo OUTPUTDIR: $(OUTPUTDIR) >> %temp%\pre-build.txt echo OUTPUTEXT: $(OUTPUTEXT) >> %temp%\pre-build.txt echo OUTPUTFILENAME: $(OUTPUTFILENAME) >> %temp%\pre-build.txt echo OUTPUTNAME: $(OUTPUTNAME) >> %temp%\pre-build.txt echo OUTPUTPATH: $(OUTPUTPATH) >> %temp%\pre-build.txt echo Path: $(Path) >> %temp%\pre-build.txt echo Platform: $(Platform) >> %temp%\pre-build.txt echo PROJECTDIR: $(PROJECTDIR) >> %temp%\pre-build.txt echo PROJECTEXT: $(PROJECTEXT) >> %temp%\pre-build.txt echo PROJECTFILENAME: $(PROJECTFILENAME) >> %temp%\pre-build.txt echo PROJECTNAME: $(PROJECTNAME) >> %temp%\pre-build.txt echo PROJECTPATH: $(PROJECTPATH) >> %temp%\pre-build.txt echo SAVE: $(SAVE) >> %temp%\pre-build.txt echo SystemRoot: $(SystemRoot) >> %temp%\pre-build.txt echo WINDIR: $(WINDIR) "$(INPUTDIR)Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.bat" "$(Platform)" "$(INPUTDIR)..\..\..\..\..\fastmm.sourceforge.net\FullDebugMode DLL\Precompiled\" "$(OUTPUTDIR)"

I ran it using the project C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.dproj

The objective was to copy the correct FastMM FullDebugMode DLL into the directory of the .EXE (more on that later).

Observations of the events:

pre-link is not executed

pre-build and post-build get executed

pre-build and post-build give the same output

Output of pre-build.txt converted to a html table (too bad you cannot have code or pre tags around a table tag):

BDS c:\program files (x86)\embarcadero\rad studio\11.0 Config Debug DEFINES DEBUG;FullDebugMode; DIR INCLUDEPATH c:\program files (x86)\embarcadero\rad studio\11.0\lib\Win64\release;C:\Users\Developer\Documents\RAD Studio\11.0\Imports;c:\program files (x86)\embarcadero\rad studio\11.0\Imports;C:\Users\Public\Documents\RAD Studio\11.0\Dcp\Win64;c:\program files (x86)\embarcadero\rad studio\11.0\include;C:\Program Files (x86)\FastReports\LibD18x64;C:\Program Files (x86)\Raize\CS5\Lib\RS-XE4\Win64 INPUTDIR C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\ INPUTEXT .dproj INPUTFILENAME Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.dproj INPUTNAME Copy_FastMM_FullDebugMode_Dll_PostBuildEvent INPUTPATH C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.dproj LOCALCOMMAND OUTPUTDIR C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\Win64\Debug\ OUTPUTEXT .exe OUTPUTFILENAME Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.exe OUTPUTNAME Copy_FastMM_FullDebugMode_Dll_PostBuildEvent OUTPUTPATH C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\Win64\Debug\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.exe Path C:\Users\Public\Documents\InterBase\redist\InterBaseXE3\win32_togo;C:\Users\Public\Documents\InterBase\redist\InterBaseXE3\win64_togo;C:\Program Files (x86)\Embarcadero\RAD Studio\11.0\Redist\boost\win64;C:\Program Files (x86)\Embarcadero\RAD Studio\11.0\Redist\boost\win32;C:\Program Files (x86)\CollabNet;C:\Program Files (x86)\Embarcadero\RAD Studio\11.0\bin;C:\Users\Public\Documents\RAD Studio\11.0\Bpl;C:\Program Files (x86)\Embarcadero\RAD Studio\11.0\bin64;C:\Users\Public\Documents\RAD Studio\11.0\Bpl\Win64;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft\Web Platform Installer\;C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\;C:\Program Files (x86)\Windows Kits\8.0\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\Program Files\TortoiseSVN\bin;C:\Program Files\TortoiseHg\;C:\BIN Platform Win64 PROJECTDIR C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent PROJECTEXT .dproj PROJECTFILENAME Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.dproj PROJECTNAME Copy_FastMM_FullDebugMode_Dll_PostBuildEvent PROJECTPATH C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent.dproj SAVE SystemRoot C:\Windows WINDIR C:\Windows

Observations of the values

Partially lowercase:

INCLUDEPATH: c:\program files (x86)\embarcadero\rad studio\11.0\lib\Win64\release;C:\Users\Developer\Documents\RAD Studio\11.0\Imports;c:\program files (x86)\embarcadero\rad studio\11.0\Imports;C:\Users\Public\Documents\RAD Studio\11.0\Dcp\Win64;c:\program files (x86)\embarcadero\rad studio\11.0\include;C:\Program Files (x86)\FastReports\LibD18x64;C:\Program Files (x86)\Raize\CS5\Lib\RS-XE4\Win64

Lowercase:

BDS: c:\program files (x86)\embarcadero\rad studio\11.0

INPUTEXT: .dproj

OUTPUTEXT: .exe

PROJECTEXT: .dproj

Empty:

DIR:

LOCALCOMMAND:

SAVE:

Ends with backslash:

INPUTDIR: C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\

OUTPUTDIR: C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent\Win64\Debug\

No backslash:

PROJECTDIR: C:\Users\Developer\SVN\BeSharp.codeplex.com\Native\Delphi\Apps\Copy_FastMM_FullDebugMode_Dll_PostBuildEvent

SystemRoot: C:\Windows

WINDIR: C:\Windows

I tried these Intel based platforms in Delphi XE4:

Platform: OSX32

Platform: Win32

Platform: Win64

So that’s where the Copy_FastMM_FullDebugMode_Dll_PostBuildEvent came in (:

Note it’s now at https://bitbucket.org/jeroenp/besharp.net/src/tip/Native/Delphi/Apps/Copy_FastMM_FullDebugMode_Dll_PostBuildEvent/

For copying stuff, always use xcopy /y , use a target directory relative to $(OUTPUTDIR) . Example:

xcopy /y ..\..\..\Shared\OpenSSL\i386-win32\*eay32.dll $(OUTPUTDIR)

xcopy /y ..\..\..\Shared\gRPC

gHttp2

ghttp2.dll $(OUTPUTDIR)

Future research

I need to put some more research into where these values are filled, and how you can pass your own to the MSBUILD process.

According to name, with extension INPUTPATH The input file’s full path LOCALCOMMAND Local command entered by user in project manager OUTPUTDIR The output file’s directory OUTPUTEXT – CodeInPro

and What are the MSBuild project level properties for Delphi? – Stack Overflow,

many values are set in

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Borland.Delphi.Targets

but that has since then moved to files named like this:

C:\Program Files (x86)\Embarcadero|CodeGear\RAD Studio\*.0\bin\CodeGear.*.Targets

Nobody seems to know about $(LOCALCOMMAND) : Article 10854 Subject D2010 Build Events – Use of $(LOCALCOMMAND)? Where does it come from? on embarcadero.public.delphi.ide.

–jeroen