How can one read the AssemblyFileVersion, or its components AssemblyFileMajorVersion, AssemblyFileMinorVersion, AssemblyFileBuildNumber, AssemblyFileRevision, within the .csproj, following compilation?
I have tried the following which pulls the information from the built assembly:
<Target Name="AfterCompile">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output
TaskParameter="Assemblies"
ItemName="MyAssemblyIdentities"/>
</GetAssemblyIdentity>
<Message Text="AssemblyVersion = %(MyAssemblyIdentities.Version)" />
</Target>
But that retrieves the AssemblyVersion and not the AssemblyFileVersion. There does not seem to be a documented metadata entry for the latter. I also tried:
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks" />
<Target Name="AfterCompile">
<MSBuild.ExtensionPack.Framework.Assembly TaskAction="GetInfo" NetAssembly="$(TargetPath)">
<Output TaskParameter="OutputItems" ItemName="Info" />
</MSBuild.ExtensionPack.Framework.Assembly>
<Message Text="AssemblyFileVersion = %(Info.FileVersion)" />
</Target>
Unfortunately, while this retrieves the correct value, it also file locks the assembly until VS2008 is closed.
Frankly, neither is what I want as I would rather read the information from the AssemblyInfo.cs directly. However, I cannot figure out how to do that. I assumed AssemblyInfo in the MSBuild Extensions was one way, but it seems focused on writing to the AssemblyInfo and not retrieving values from it.
How can I best accomplish this?
I've managed to solve this using a custom task. The class library DLL is as so (some code adjusted/eliminated for brevity):
using System;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
namespace GetAssemblyFileVersion
{
public class GetAssemblyFileVersion : ITask
{
[Required]
public string strFilePathAssemblyInfo { get; set; }
[Output]
public string strAssemblyFileVersion { get; set; }
public bool Execute()
{
StreamReader streamreaderAssemblyInfo = null;
Match matchVersion;
Group groupVersion;
string strLine;
strAssemblyFileVersion = String.Empty;
try
{
streamreaderAssemblyInfo = new StreamReader(strFilePathAssemblyInfo);
while ((strLine = streamreaderAssemblyInfo.ReadLine()) != null)
{
matchVersion = Regex.Match(strLine, @"(?:AssemblyFileVersion\("")(?<ver>(\d*)\.(\d*)(\.(\d*)(\.(\d*))?)?)(?:""\))", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
if (matchVersion.Success)
{
groupVersion = matchVersion.Groups["ver"];
if ((groupVersion.Success) && (!String.IsNullOrEmpty(groupVersion.Value)))
{
strAssemblyFileVersion = groupVersion.Value;
break;
}
}
}
}
catch (Exception e)
{
BuildMessageEventArgs args = new BuildMessageEventArgs(e.Message, string.Empty, "GetAssemblyFileVersion", MessageImportance.High);
BuildEngine.LogMessageEvent(args);
}
finally { if (streamreaderAssemblyInfo != null) streamreaderAssemblyInfo.Close(); }
return (true);
}
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
}
}
And in the project file:
<UsingTask AssemblyFile="GetAssemblyFileVersion.dll" TaskName="GetAssemblyFileVersion.GetAssemblyFileVersion" />
<Target Name="AfterCompile">
<GetAssemblyFileVersion strFilePathAssemblyInfo="$(SolutionDir)\AssemblyInfo.cs">
<Output TaskParameter="strAssemblyFileVersion" PropertyName="strAssemblyFileVersion" />
</GetAssemblyFileVersion>
<Message Text="AssemblyFileVersion = $(strAssemblyFileVersion)" />
</Target>
I've tested this and it will read the updated version if you use MSBuild.ExtensionPack.VersionNumber.targets for auto-versioning.
Obviously, this could be easily extended so that a regex is passed from the project file to a more general-purpose custom task in order to obtain any match in any file.
Update 2009/09/03:
One additional change has to be made to make the ApplicationVersion
update on each build. InitialTargets="AfterCompile"
must be added to the <Project...
. This was solved by Chao Kuo.
If you make the task inherit from AppDomainIsolatedTask, you don't need to the assembly loading from streams. You can just use AppDomain.LoadFrom(file).
What about copying the assmelby and then running the Assembly task on the copy?
Sayed Ibrahim Hashimi