The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

Delphi prebuild/prelink/postbuild events

Posted by jpluimers on 2014/11/20

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:

  1. How the lines of a build event are actually executed
  2. 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\ngHttp2\nghttp2.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

5 Responses to “Delphi prebuild/prelink/postbuild events”

  1. […] Delphi build-events (see my post Delphi prebuild/prelink/postbuild events), you can automate the process of signing your Delphi executables with a digital […]

  2. Peter said

    Do you have an idea of the difference between INPUTPATH and PROJECTPATH? Wouldn’t they always be the same?

    • jpluimers said

      I think they are always the same, but I’m not 100% sure. If I find a situation where they are different, then I will make a note here.

  3. David M said

    “Pre-link is not executed” – does that mean that the pre-link build event doesn’t function at all?

    • jpluimers said

      I probably did something wrong, but I could not get it to work in Delphi.
      Maybe it is just meant for C++ Builder projects, but it has been ages ago since I did C++.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.