Delphi 2009/2010 Post-Build events

By | June 30, 2010

Have you ever wondered if it is possible to execute the “Post-Build events” only when the project was actually updated/compiled? With Delphi 2009/2010 you can do that easily by changing the “Execute when” combobox in the “Post-Build events” edit dialog to “Target is out-of-date”. At least you thought that would do the trick. Instead it disables the Post-Build event what means that it is never executed.
I did some research on this because I needed this functionality. Without it the “Execute when” = “Always” setting tells msbuild to execute the post-build script even if the compiler didn’t do anything. And that causes msbuild to build targets that don’t need to be built.

But what is the problem with “Target is out-of-date”. Let’s have a look into the msbuild script that executes the Post-Build events. The “PostBuildEvent” target in $(BDS)\bin\CodeGear.Common.Targets is declared as:

 

<Target Name="PostBuildEvent"
        Condition=" '$(PostBuildEvent)'!=''  And '$(KibitzCompile)'==''
        and ( '$(RunPostBuildEvent)'=='Always' or '$(RunPostBuildEvent)'=='' or '$(PreOutputTimeStamp)'!='$(PostOutputTimeStamp)' )"
        DependsOnTargets="$(PostBuildEventDependsOn)">
    <Exec Command="$(PostBuildEvent)"  IgnoreExitCode="$(PostBuildEventIgnoreExitCode)" WorkingDirectory="$(OutDir)"/>
</Target>

Now let’s see what msbuild with “/v:diag” tells us about this target.

'$(RunPostBuildEvent)'=='Always' or '$(RunPostBuildEvent)'=='' or
  '$(PreOutputTimeStamp)'!='$(PostOutputTimeStamp)'

is evaluated to

'OutOfDate'=='Always' or 'OutOfDate'=='' or ''!=''

The first two conditions are ok and mean that we have to look at the third condition for our out-of-date check. But what is with the third condition? Why is it an empty string comparison that is always false? And where do PreOutputTimeStamp and PostOutputTimeStamp come from? They are declared some lines about the “PostBuildEvent” target.

<Target Name="_PreOutputTimeStamp">
    <CreateItem Include="%(OutputFile.ModifiedTime)">
        <Output TaskParameter="Include" PropertyName="PreOutputTimeStamp"/>
    </CreateItem>
</Target>
 
<Target Name="_PostOutputTimeStamp">
    <CreateItem Include="%(OutputFile.ModifiedTime)">
        <Output TaskParameter="Include" PropertyName="PostOutputTimeStamp"/>
    </CreateItem>
</Target>

So the PreOutputTimeStamp is the modified time of the output file (EXE/DLL/BPL) before it was compiled and the PostOutputTimeStamp is the modified time of the same file after msbuild has done its work. But why are they empty?
The item “OutputFile” is not declared or set anywhere. Neither in CodeGear.Common.Targets nor in CodeGear.Delphi.Targets nor in MyProject.dproj. And that is why both msbuild properties are evaluated to empty strings.

With this knowledge I was now able to fix the this bug by adding the missing “OutputFile” item to the CodeGear.Delphi.Targets file.

 <ItemGroup>
    <_MSBuildProjectFullPath Include="$(MSBuildProjectFullPath)"/>
    <_DependencyCheckOutputName Include="$(DCC_DependencyCheckOutputName)"/>
+   <OutputFile Include="$(DCC_DependencyCheckOutputName)"/>
 </ItemGroup>

After this change the “Target is out-of-date” combobox item in the “Post-Build events” dialog started to work as expected.

One issue solved. But that wasn’t all that I needed for my build script. In order to reduce the file size of our massive EXE files we strip the PE format’s relocation section from the EXEs and we create and attach *.jdbg files to our EXEs. But both post-build events aren’t necessary when we debug our projects in the IDE, especially not the relocation section removal. In the IDE we want a fast edit-compile-debug turn-around. So I had to find a way to only execute those post-build events when the Release-Configuration was active.
Unfortunately Delphi doesn’t support this feature. But after browsing through the CodeGear.*.Targets files and reading msbuild /v:diag log files, I had the solution right there sitting in front of me. The $(OUTPUTPATH) from the post-build events are replaced by msbuild properties when msbuild processes the build script. So I had the idea of just trying to use other properties from msbuild in addition to the macros that are listed in the Post-Build events edit dialog. And with $(Config) I had the active configuration right at hand.

if "$(Config)" == "Release" (
  stripreloc.exe ...
  linkmapfile.exe ...
)

And with the active configuration being set to “Debug”, msbuild executes this as:

if "Debug" == "Release" (
  stripreloc.exe ...
  linkmapfile.exe ...
)

Now that I have solved the build script issues, I can turn to all the other “things” that I have to solve, think about and develop before all my co-workers can start working with the new Delphi IDE.

Those “things” are things like keeping the components as part of the project to make it possible to update to a new component version without breaking older project versions. And I’m not speaking of using the “-r” IDE command line option. That’s what we used in the older Delphi version. I’m speaking of opening a project from the 1.0 branch, closing it and opening the same project from the 7.0 branch that requires newer versions of the components. Furthermore the components are in the project’s svn folder, so they have the same revision number. (Our old svn “layout” was to have a separate svn repository for the components) And if you svn-update and somebody has changed the components, the IDE automatically compiles them and loads them when you open the project. And today this IDE plugin passed it’s final tests.

3 thoughts on “Delphi 2009/2010 Post-Build events

  1. Lachlan Gemmell

    Looking forward to hearing more about this component manager plugin.

  2. Giel

    Complicated stuff!

    Why don’t you put “{$SetPEFlags 1}” in the .dpr files to keep the compiler from adding relocation data?

  3. Andreas Hausladen Post author

    @Giel: I haven’t thought about that. In Delphi 7 this didn’t work. Thanks for the hint. (One task less to execute)

Comments are closed.