This is post #37 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.
Last week I wrote a post on getting Hg revision information into assembly versions. It works okay, but it has a small drawback. Because I'm putting the version information into two files, CommonAssemblyInfo.cs and Version.cs, I have to manually maintain the major, minor and build numbers in two places. And because I'm using all three assembly versions, I have to maintain those numbers on multiple lines as well. This morning I'm going to remove all the needless duplication.
I want to consolidate the assembly version numbers into one place for easy maintenance. The best place to do that is probably in the build project itself. Here I'm going to set up properties for the major, minor and build numbers as well as create the three assembly versions as replacement values for template tokens.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<UsingTask TaskName="HgVersionFile" AssemblyFile="References/MSBuildVersioning/MSBuildVersioning.dll" />
<PropertyGroup>
<SolutionName>StackingCode.Moja</SolutionName>
<Major>1</Major>
<Minor>0</Minor>
<Build>1000</Build>
<BuildText>Alpha</BuildText>
</PropertyGroup>
<ItemGroup>
<Tokens Include="AssemblyVersion">
<ReplacementValue>$(Major).$(Minor).$(Build).0</ReplacementValue>
</Tokens>
<Tokens Include="AssemblyFileVersion">
<ReplacementValue>$(Major).$(Minor).$(Build).$REVNUM$</ReplacementValue>
</Tokens>
<Tokens Include="AssemblyInformationalVersion">
<ReplacementValue>$(Major).$(Minor) $(BuildText) ($REVID$${Dirty$DIRTY$})</ReplacementValue>
</Tokens>
<Tokens Include="Dirty0">
<ReplacementValue></ReplacementValue>
</Tokens>
<Tokens Include="Dirty1">
<ReplacementValue>+</ReplacementValue>
</Tokens>
</ItemGroup>
<!-- ... -->
</Project>
These are tokens that will be swapped in using the TemplateFile task from the MSBuild Community Tasks Project, before the HgVersionFile task from MSBuild Versioning gets a crack at doing its token replacement.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<!-- ... -->
<Target Name="Version">
<!-- Properties\CommonAssemblyInfo.cs -->
<TemplateFile Template="Properties\CommonAssemblyInfo.template.cs" OutputFileName="CommonAssemblyInfo.temp1.cs" Tokens="@(Tokens)" />
<HgVersionFile TemplateFile="Properties\CommonAssemblyInfo.temp1.cs" DestinationFile="Properties\CommonAssemblyInfo.temp2.cs" />
<TemplateFile Template="Properties\CommonAssemblyInfo.temp2.cs" OutputFileName="CommonAssemblyInfo.cs" Tokens="@(Tokens)" />
<Delete Files="Properties\CommonAssemblyInfo.temp1.cs;Properties\CommonAssemblyInfo.temp2.cs" />
<!-- Version.cs -->
<TemplateFile Template="Version.template.cs" OutputFileName="Version.temp1.cs" Tokens="@(Tokens)" />
<HgVersionFile TemplateFile="Version.temp1.cs" DestinationFile="Version.temp2.cs" />
<TemplateFile Template="Version.temp2.cs" OutputFileName="Version.cs" Tokens="@(Tokens)" />
<Delete Files="Version.temp1.cs;Version.temp2.cs" />
</Target>
<!-- ... -->
</Project>
The template file is run through a TemplateFile task, then a HgVersionFile task, then another TemplateFile task.
The first TemplateFile replaces ${} tokens with strings.
${AssemblyInformationalVersion} becomes
1.0 Alpha ($REVID$${Dirty$DIRTY$})
The HgVersionFile task replaces $$ tokens with values from Hg.
1.0 Alpha ($REVID$${Dirty$DIRTY$}) becomes
1.0 Alpha (875673902fee${Dirty1})
The final TemplateFile task replaces any remaining ${} tokens.
1.0 Alpha (875673902fee${Dirty1}) becomes
1.0 Alpha (875673902fee+)
Temporary files are used instead of using the same file as input and output -- doing that causes problems in the HgVersionFile task. The temporary files are cleaned up with a Delete task at the end.
The CommonAssemblyInfo.template.cs file now looks like this...
using System.Reflection;
[assembly: AssemblyCopyright("Copyright 2010 - 2011 Adam Boddington")]
[assembly: AssemblyVersion("${AssemblyVersion}")]
[assembly: AssemblyFileVersion("${AssemblyFileVersion}")]
[assembly: AssemblyInformationalVersion("${AssemblyInformationalVersion}")]
The Version.template.cs file now looks like this...
using System;
namespace StackingCode.Moja
{
public static class Version
{
static Version()
{
DateTime buildDate = DateTime.Parse("$UTCDATETIME$");
buildDate = DateTime.SpecifyKind(buildDate, DateTimeKind.Utc);
HgBuildDate = new DateTimeOffset(buildDate);
}
public static readonly string AssemblyVersion = "${AssemblyVersion}";
public static readonly string AssemblyFileVersion = "${AssemblyFileVersion}";
public static readonly string AssemblyInformationalVersion = "${AssemblyInformationalVersion}";
public static readonly DateTimeOffset HgBuildDate;
public static readonly int HgRevisionNumber = $REVNUM$;
public static readonly string HgChangesetId = "$REVID$";
public static readonly bool HgIsBuildDirty = $DIRTY$ == 1;
public static readonly string HgBranch = "$BRANCH$";
public static readonly string HgTags = "$TAGS$";
}
}
Overall a much cleaner and easier to maintain solution, without any need for preprocessor directives either.
The StackingCode.Moja.csproj and StackingCode.Neno.csproj files still have the BeforeBuild tasks to copy a default file over CommonAssemblyInfo.cs and Version.cs (see the earlier post). I want to keep those tasks for users that don't have Hg installed, and for the AppHarbor build process which doesn't currently support Mercurial.
Happy versioning.
There are 0 comments.
browse with Pivot
Readable Code with Extension Methods
Visual Studio 2010 and Vim
Zenburn PowerShell
Mercurial Prompt
PowerShell Prompt
Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Linq (5)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)
Comments
Leave a Comment
Please register or login to leave a comment.