Skip to main content

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;
}

}
}

Comments

Dmitry Pavlov said…
Wow! Great job! Thanks!

Popular posts from this blog

dotNET - Debugging

Debugging with .NET MSIL assemblies Visual Studio and debugging the CLR are different, I'll talk about both. MSIL Assemblies Assemblies compiled with .NET tools such as the CLR compiler are compiled into a file which contains MSIL (Microsoft Intermediate Language). At runtime the contents of the assembly are loaded into the CLR and ran as machine code. When you compile an assembly in debug a PDB file is generated alongside the DLL or EXE you've just created. The link between these 2 files is that the PDB contains the line numbers of the methods and classes as well as the file names of the original source code that created the assembly. When you launch the debugger in Visual Studio the assembly is loaded into the Debugger (similar to the CLR) along with the PDB file. The debugger now uses your PDB file contents to match the running code found in the assembly to locations in source files (hopefully in your present project). CLR CLR Inside Out (msdn magazine) .NET Framework Tools:...

Installer CustomAction, Debugging the CustomAction, InstallState

Custom Action The Custom Action is added to the Setup Project, select the Project node and hit the Custom Action button. This allows you add an Action to a particular phase in the Installation. But first you must create the Custom Action. To Add a Custom Action you must first have a Custom Action created, this is usually in the form of a Installer Class, this should be created in a seperate project, the Installer Class is actually one of the File Templates in the C# Projects. So it's File->New Project and select Visual C# Projects. Then add a Class Library, this will prompt you for the Class Library Types , select "Installer Class". Walkthrough - Creating Custom Action (msdn). Also here's a more comprehensive document on Setup/Installer implementations, it delves into the Registry etc Getting Started with Setup Projects (SimpleTalk). Visual Studio Setup Projects and Custom Actions (Simple Talk). Create your Installer Class and then add it as a Custom Action to the ...

dotNET - Use app.config ApplicationSettings and UserSettings

When using Settings in an Assembly or .exe you can use the Settings Designer to generate a config file using Settings. The Settings Designer provides a wrapper class which allows you to provide defaults and access the config data using Properties. But what if you're not working inside that Assembly or .exe? this presents a problem. If your loading the Assembly externally and want to access that Assembly's .config file you'll probably wish to use something in the System.Configuration namespace... unfortunately it's not of much use if you've created the .config file from the Settings Designer in Visual Studio!! This is because the Designer creates Sections and ApplicationSettings and UserSettings, the System.Configuration namespace does not provide a method to access these (it has a method to access AppSettings which are a different thing. Below I've written a workaround which locates the app.config and accesses the ApplicationSettings and UserSettings using XML i...