How to insert an assembly version number into a WiX script at build time

Things have been quiet on this blog regarding Team Foundation Server… this isn’t from lack of interest, merely that the server is doing a great job taking care of itself and thereby leaving me to get on with other pleasures… such as documentation of project process.

Well, there was one thing, but I was somewhat ashamed to show it. It seems like when anyone creates a cool Powershell script in 5 lines of code, there will be some RegEx ninja who will jump in and splice it into 1.5 lines.

Well, first the scenario. We are using AssemblyInfoTask in the build project. Each time a build is kicked off, this task will go check out an AssemblyInfo.cs file of your choosing, and increment the assembly version number inside it. If the build is ultimately successful, it will then check this file back into source control with the new version.

The problem was that I had a WiX project that built the MSI, and there was no automatic way to pick up this assembly value and put it in as a variable.

Well, there is a way to get it in there.. you run a Powershell script during the build process, and update the WiX file whilst it is being built.

1 : you need to get a MSBuild task that can invoke Powershell.. I used this one.

2 : Place the following Include in your MSbuild .Proj file

  <UsingTask TaskName=”PowerShell” AssemblyFile=”C:\Build\Tasks\PowershellMSBuildTask.dll” />

3: Define location of script file in project

  <PropertyGroup>
    ………..

    <ReplaceScript>C:\Build\Tasks\ReplaceVersion.ps1</ReplaceScript>
  </PropertyGroup>

4 : Add (or merge) the following target into your .Proj file. For completeness, I will include the full target definition:

  <Target Name=”AfterGet” Condition=”‘$(IsDesktopBuild)’!=’true'”>
    <CreateItem Include=”$(SolutionRoot)\Client\$(AssemblyInfoSpec)”>
      <Output ItemName=”AssemblyInfoFiles” TaskParameter=”Include” />
    </CreateItem>

    <Exec WorkingDirectory=”$(SolutionRoot)”
        Command=”$(TF) checkout /recursive $(AssemblyInfoSpec)”/>
   
    <PowerShell Script=”$(ReplaceScript)” />

  </Target>

5 : In your WiX files, wherever you want a version number, insert this string: x.x.x.x. Here is an example Environment.wxi

<?xml version=”1.0″ encoding=”utf-8″?><Include><?define UpgradeCode = “AAAA-AAAA-AAAA-AAAA-AAAAA” ?><?define CompanyName = “Best Cookie Company” ?><?define ProductName = “Cookie Client vx.x.x.x” ?>

<?define ProductVersion = “x.x.x.x” ?>

</Include>

6 : Add the following Powerscript file to the file system on your build machine. I called it ReplaceVersion.ps1, but you can name and place it where you want

#######################
# This function will extract assembly version from an AssemblyInfo.cs file
# and replace the value x.x.x.x in the destination file with this value
#######################
function replaceVersions([string]$assemblyFile, [string]$fileToFix)
{
 # Get the whole line where ‘AssemblyFileInfo exists 
 $version = (select-string -pattern “AssemblyFileVersion” $assemblyFile) -replace ‘\”‘,”;

 # Find the beginning of the version number
 $beginIndex = ($version.IndexOf(“(“) + 1);

 # Extract version number
 $version = $version.SubString($beginIndex, ($version.IndexOf(“)”) – $beginIndex));

 # Create a temp file to write to
 $tempFile = $fileToFix + “.temp”;

 # Copy target file to temp file
 get-content $fileToFix | set-content $tempFile;

 # Set target file to writable
 (Get-ChildItem $fileToFix).set_IsReadOnly( 0 );

 # Increment version
 $version = incrementVersion $version;

 # Replace x.x.x.x in temp file to version number. Copy it back to target.
 get-content $tempFile | % { $_.Replace(“x.x.x.x”, $version) } | set-content $fileToFix;

 # Output
 Write-Host $version + “written to WiX files”;
}

#######################
# This function will increment the version assembly string by one minor number
#######################

function incrementVersion([string]$version)
{
 # Increment version number
 $minorVersionStartIndex = $version.LastIndexOf(“.”);
 $majorVersion = $version.SubString(0, ($minorVersionStartIndex+1));
 $minorVersion = $version.SubString(($minorVersionStartIndex + 1), ($version.length – $minorVersionStartIndex -1));
 $minorVersionInt = [Int32]$minorVersion;
 $minorVersionInt++;
 $minorVersion = [String]$minorVersionInt;

 return $majorVersion + $minorVersion;
}

$wixInclude = “C:\Build\Build\WiX\Environment.wxi”;
$wixProject = “C:\Build\Build\WiX\Build.wixproj”;
$assemblyInfo = “C:\Build\Build\Client\AssemblyInfo.cs”;

replaceVersions $assemblyInfo $wixInclude;

replaceVersions $assemblyInfo $wixProject;

(Question: I thought to source control this.. but how much of a risk is that?? A developer could put any code they wanted inside it, and it would get called as part of the build process. That said, your build process should be running as a standard user anyhow)

I’m not really happy with this script, so if anyone wants to make it shorter, then feel free to let me know for the good of the community 🙂

_______________________________________________________

UPDATE: 27/09/2007

Already a couple of comments saying that this is a long hard way to do it… and you are right!

What I am looking for is:

  1. Build my assemblies with TFS build
  2. Update the minor version number of the assemblies
  3. Get this assembly number used as a variable inside WiX 3.0

It doesn’t have to be Powershell.. if there is a simple setting in WiX that will allow me to do this then let me know!

_______________________________________________________

UPDATE: 31/10/2007

Morten Lyhr has an alternative solution, which I’m happy to advertise as an alternative.

He describes my solution as ‘clumsy’, and I guess everyone is entitled to their opinion 🙂

http://morten.lyhr.dk/PermaLink,guid,26be3b8d-8c89-4b1a-8528-46426f256c04.aspx

However I strongly disagree: once you have my powershell script on the system then there is not a great deal of configuration to do inside of the .proj file. His solution requires A LOT of hacking the project file, which I’m sure will cause major headaches down the road.

Because of this, I suggest mine is in fact more ‘elegant’.

What would be better, if I ever get the time, is just create an MSBuild task to handle this all in one stepI’m thinking just to create a new MSBuild task that can do all this in one step…

Advertisements

9 thoughts on “How to insert an assembly version number into a WiX script at build time

  1. Nice solution, too bad WiX can’t catch up to InstallShield so that you don’t have to do any of this in the first place.

    In InstallShield MSBuild support, the default targets file maps $(InstallShieldProductVersion) to the command line builder ( IsCmdBld or IsSaBld ) argument -y for setting ProductVersion.

    That’ll slash it down to 1 line of code. 🙂

  2. Hi Roger,

    I am actually setting a variable in the WXS file, but my problem was not doing that but getting the new version number out of my Team Build process.

    Have you done this?

  3. No, I’ve not done this. We’ve not moved to TFS; our build process is SVN/NAnt-based. We have a custom NAnt task that sets a property from the last-committed-revision for a particular branch, which we can then use in other tasks, including updating AssemblyInfo.cs, Version.rc or whatever.

    I wanted to refute Christopher’s assertion that “WiX can’t catch up to InstallShield”, since WiX’s -dPRODUCTVERSION is equivalent to InstallShield’s -y, unless there’s some complexity I’m missing.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s