Pre/post-build event

This is a tip for all developers, not for BuildMasters only.

The issue is those pre and post-build events that developers use to add any conceivable code… especially code that assume a developer’s workstation folder structure.

Such code may break in a TeamBuild for many reasons:

  • different folder structure — all binaries are produced in the common Binaries folder, no more in a per-project bin;
  • missing tools — on a typical build server only the bare minimum tools are present;
  • different context — e.g. a developer workstation is 32-bit, while the build server is targeting 64-bit binaries.

There are some different solutions applicable.

The first one, done by developers on their projects, wrap the code in the pre- or post-build event with a condition, like this:

IF NOT "$(TeamBuildConstants)"=="_TEAM_BUILD_" (
gacutil /i $(TargetFile) /f
)

This needs to be done on each project and event but has a very explicit intent; also this is portable to foreseeable versions of Visual Studio and Team Foundation Server.

 

A second solution is to redefine the CoreBuildDependsOn property via CustomPropertiesForBuild so that PreBuildEvent and PostBuildEvent are left out. Something like this

<PropertyGroup>
<CustomPropertiesForBuild>
CoreBuildDependsOn=
BuildOnlySettings;PrepareForBuild;
ResolveReferences;PrepareResources;ResolveKeySource;
Compile;
UnmanagedUnregistration;GenerateSerializationAssemblies;
CreateSatelliteAssemblies;GenerateManifests;
GetTargetPath;PrepareForRun;UnmanagedRegistration;IncrementalClean
</CustomPropertiesForBuild>
</PropertyGroup>

I think of two scenarios where this approach is especially convenient: when you inherit existing code — so called brown field — and need to start control its behavior; or when the build shows weird behavior and you want to troubleshoot its root cause.

Keep in mind that you are touching a reserved property: this may cause problems with Service Packs or new product releases. It is also dangerous if you already use CustomPropertiesForBuild — could be hard to track what happens in the build.

I suggest to use this approach as last resort and for a limited time.

 

A third solution, requiring a bit more effort, is to rewrite the action as an MSBuild task and leverage the BeforeBuild and AfterBuild targets with an apt condition, like:

<Target Name="AfterBuild" Condition="'$(TeamBuildConstants)'!='_TEAM_BUILD_'>
<GacUtil Command="Install" Assemblies="$(TargetFile)" Force="true" />
</Target>

using the MSBuild Community Tasks or, using the MSBuild Extension Pack:

<Target Name="AfterBuild" Condition="'$(TeamBuildConstants)'!='_TEAM_BUILD_'>
<Gac TaskAction="AddAssembly" AssemblyPath="$(TargetFile)" Force="true" />
</Target>

As there are lot of built-in and publicly available tasks, this could be an elegant and viable approach.

 

Happy Building!

 

UPDATE: the above suggestion works for Visual Studio + Team Foundation Server 2008.

In Visual Studio 2010 and later, the recommended method is to use the predefined property BuildingInsideVisualStudio

IF "$(BuildingInsideVisualStudio)"=="true" ( …your code here… )

Hope this helps.