April 17, 2008

Automated update of AssemblyVersion and AssemblyFileVersion

Like so many others I wanted to update the Version of all of my assemblies with one version value but I could not find the solution I wanted, hence I've written my own, see the source code below.

This implementation fits my own problem, a build script which gets all our codebase sourcecode from SourceSafe, builds all the projects and then labels all the sourecode with a single label passed in by the builder at command-line.

This implementation takes either 1 or 2 arguments.
If only 1 is supplied it will do a check for a valid Version value i.e. the version must be of the format 1.0.0.0 and the third value must be less than 65535 (This is a .NET requirement, the assembly would not build if the number is above 65535, see here for more).
If 2 arguments are supplied it will do all that step 1 does and also will set the version to AssemblyVersion and AssemblyFileVersion in all AssemblyInfo.cs files under the directory specified (and it's subdirectories).

e.g. here's how I call it from my build.bat, NOTE AssemblyUpdateVersion.exe is the name of the Console app which I built the sourcecode shown later into.
@echo off
setlocal
c:
pushd ..\
echo "%cd%"
set CDIR=%cd%

if "%1" neq "" goto update_assembly_version
if "%1"=="" goto end

:update_assembly_version
REM Validate the label using the Custom tool AssemblyUpdateVersion.exe
call AssemblyUpdateVersion.exe %1 || goto validate_version_error

echo -- Update the Versions of the Assemblies before Building
REM First checkout all of the AssemblyInfo.cs files
set SSDIR=\\SourceSafe\sscodebase || goto error
call sscodebase C:\src checkout -R AssemblyInfo*.cs

REM now apply the new label to the AssemblyInfo.cs files using our custom tool AssemblyUpdateVersion.exe
call AssemblyUpdateVersion\bin\Debug\AssemblyUpdateVersion.exe %1 %CDIR% || goto update_version_error

REM now checking the file with the new version
call sscodebase . checkin -R AssemblyInfo*.cs || goto checkin_error

:end_update_assembly_version

goto great_success

:validate_version_error
echo --- Error with Version Validation
goto end
:end_validate_version_error

:checkout_error
echo --- Error with checkout
goto end
:end_checkout_error

:update_version_error
echo --- Error with Update Version
goto end
:end_update_version_error

:checkin_error
echo --- Error with checkin
goto end
:end_checkin_error


:great_success
echo --- Great Success!!!
goto end
:end_great_success
:end
Here's the code:
First File:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace AssemblyUpdateVersion
{
///
/// Apply Version information to Assemblies before they are built by editing the AssemblyInfo.cs
/// Or just Validate the Version number against a valid format by supplying a single argument e.g. 1.0.0.1
/// Or Validate and Update the Version number by supplying 2 arguments e.g. 1.0.0.1 and C:\Galaxy\src
///
public class Program
{
static int Main(string[] args)
{

string versionValueEntered;
string directory;

if (args.Length == 1)
{
versionValueEntered = args[0];
if (!ValidateVersion(versionValueEntered))
return 1;
}
else if (args.Length == 2)
{
versionValueEntered = args[0];
directory = args[1];
if (!RunUpdateVersion(versionValueEntered, directory))
return 1;
}
else
{
Console.WriteLine("The new Version must be provided e.g. 1.2.3.4 ");
Console.WriteLine("The root directory must also be provided.");
return 1;

}
return 0;//Success
}

public static bool RunUpdateVersion(string version, string directory)
{
if (!ValidateVersion(version))
return false;

AssemblyUpdateVersion assemblyUpdateVersion = new AssemblyUpdateVersion(version, directory);
return true;
}
public static bool ValidateVersion(string version)
{
Console.WriteLine("ValidateVersion with {0}", version);
//Validate the format of the value entered. It must be of the format ...
//all ints and the cannot be more than 65535.
Regex assemblyVersionPattern = new Regex("[0-9]*[.][0-9]*[.][0-9]*[.][0-9]*");
if (!assemblyVersionPattern.IsMatch(version))
{
Console.WriteLine("The AssemblyVersion must be in the format 1.0.0.0.");
return false;
}
string[] values = version.Split('.');
if (values.Length != 4)//must be 4 values
{
Console.WriteLine("The AssemblyVersion must be in the format 1.0.0.0.");
return false;
}
else
{

int result;
Int32.TryParse(values[2], out result);
if (result > 65535)
{
Console.WriteLine("The AssemblyVersion must be in the format 1.0.0.0 and the 3rd value cannot be more than 65535.");
return false;
}
}
return true;

}
}
}

2nd File:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace AssemblyUpdateVersion
{
public class AssemblyUpdateVersion
{
private string _valueEntered;
private string _directory;

public AssemblyUpdateVersion(string valueEntered, string directory)
{
_valueEntered = valueEntered;
_directory = directory;

ChangeVersion();
}

///

/// Change the version number of all AssemblyInfo.cs files found in the current directory.
///
private void ChangeVersion()
{
string newAssemblyVersion;
string newAssemblyFileVersion;

string inputFile = "AssemblyInfo.cs";
string tempFile = "AssemblyInfoTemp.cs";

DirectoryInfo dirInfo = new DirectoryInfo(_directory);


foreach (FileInfo assemblyInfoFileInfo in GetFilesRecursive(dirInfo, inputFile))
{
string tempFileFullName = Path.Combine(assemblyInfoFileInfo.Directory.FullName, tempFile);


//[assembly: AssemblyVersion("1.2.3.9")]
newAssemblyVersion = @"[assembly: AssemblyVersion(""" + _valueEntered + @""")]";
//[assembly: AssemblyFileVersion("1.0.0.0")]
newAssemblyFileVersion = @"[assembly: AssemblyFileVersion(""" + _valueEntered + @""")]";

try
{
using (StreamReader sr = new StreamReader(assemblyInfoFileInfo.OpenRead()))
{
using (StreamWriter sw = new StreamWriter(tempFileFullName, false, sr.CurrentEncoding))
{
String line;
while ((line = sr.ReadLine()) != null)
{
if (line.Contains("AssemblyVersion"))
{
string result = line.Replace(line, newAssemblyVersion);
sw.WriteLine(result);
}
else if (line.Contains("AssemblyFileVersion"))
{
string result = line.Replace(line, newAssemblyFileVersion);
sw.WriteLine(result);
}
else
{
sw.WriteLine(line);
}
}
}
}

File.Copy(tempFileFullName, assemblyInfoFileInfo.FullName, true);
File.Delete(tempFileFullName);
Console.WriteLine("Successfully set Version to {0} on File {1} ", newAssemblyVersion, assemblyInfoFileInfo.FullName);
}
catch (UnauthorizedAccessException uae)
{
Console.WriteLine("Access Denied: ");
Console.WriteLine(uae.Message);
}
catch (Exception e)
{
// Let the user know what went wrong.
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}
public IEnumerable GetFilesRecursive(DirectoryInfo dirInfo, string searchPattern)
{
foreach (DirectoryInfo di in dirInfo.GetDirectories())
foreach (FileInfo fi in GetFilesRecursive(di, searchPattern))
yield return fi;

foreach (FileInfo fi in dirInfo.GetFiles(searchPattern))
yield return fi;
}

}
}

1 comment:

VSX Insider said...

Wow! Great job! Thanks!