Search code examples
c#.netcommanifest

Version number in manifest file's dependentAssembly/assemblyIdentity element


I'm working with an application that includes an un-managed client DLL and a managed COM server DLL (which was a challenge in itself: Managed Reg-Free COM Server Won't Activate), and now I'm wondering what is the best way to keep the version numbers in sync. Since we are building both the client and the server, and we try to keep the version numbers of all our files in sync for every build, it looks like I need to have a process that edits all my manifest files on both the client and server ends of all my isolated COM references before a full build happens. Is there an easier way?

Example (client manifest):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity type="win32" name="globals" version="1.0.0.0" />
   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32" name="SoftBrands.FourthShift.FSCulture" version="8.0.0.999" publicKeyToken="541b4aff0f04b60a" />
      </dependentAssembly>
   </dependency>
</assembly>

Server Manifest:

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity type="win32" name="SoftBrands.FourthShift.FSCulture" version="8.0.0.999" publicKeyToken="541b4aff0f04b60a" />
   <clrClass   clsid="{23D4FF3D-EEDF-4F68-AD65-749958EE3B2A}"
               name="SoftBrands.FourthShift.FSCulture.FSCulture"
               tlbid="{8D480B22-D603-309F-9A26-EA9E9B020207}">
   </clrClass>
</asmv1:assembly>

I could just do a global search and replace on version="8.0.0.999" with the current version for every build, but I suspect there might be a better way.


Solution

  • Your best bet is to leverage a custom MSBuild task to manipulate the project artifacts prior to compilation.

    The task should accept three properties. One property for the client manifest, another property for the server manifest and the third property for the version.

    Here is what the targets file might look like in MSBuild.

    Manifests.targets

    <?xml version="1.0" encoding="utf-8"?>
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
      <!-- Import Tasks -->
      <!-- Be sure to sue the full namespace of the task to import -->
      <UsingTask TaskName="Full.Namespace.To.UpdateManifestsTask" AssemblyFile="$(MSBuildThisFileDirectory)\MyCustomTasks.dll" />
      <!-- Define the location of the client and server manifest files -->
      <PropertyGroup>
        <ClientManifest><!-- Location of the client manifest --></ClientManifest>
        <ServerManifest><!-- Location of the server manifest --></ServerManifest>
      </PropertyGroup>
      <!-- Define Generate App Config Target -->
      <Target Name="UpdateManifests">
        <UpdateManifests ClientManifest="$(ClientManifest)" ServerManifest="$(ServerManifest)" Version="$(Version)" />
      </Target>
      <!-- Define Before Build Target -->
      <!-- This will ensure the above target gets executed prior to compilation -->
      <Target Name="BeforeBuild">
        <CallTarget Targets="UpdateManifests;" />
      </Target>
    </Project>
    

    Here is what the custom task might look like -

    UpdateManifestsTask.cs

    public class UpdateManifestsTask : Task
    {
        public ITaskItem ClientManifest { get; set; }
        public ITaskItem ServerManifest { get; set; }
        public ITaskItem Version { get; set; }
    
        public override bool Execute()
        {
            var newVersion = string.Format("name=\"SoftBrands.FourthShift.FSCulture\" version=\"{0}\"", this.Version.ItemSpec);
            var clientFile = File.ReadAllText(this.ClientManifest.ItemSpec);
            clientFile = Regex.Replace(clientFile, "name=\"SoftBrands.FourthShift.FSCulture\" version=\"\\d*\\.\\d*\\.\\d*\\.\\d*\"", newVersion);
            File.WriteAllText(this.ClientManifest.ItemSpec, clientFile);
            var serverFile = File.ReadAllText(this.ClientManifest.ItemSpec);
            serverFile = Regex.Replace(clientFile, "name=\"SoftBrands.FourthShift.FSCulture\" version=\"\\d*\\.\\d*\\.\\d*\\.\\d*\"", newVersion);
            File.WriteAllText(this.ServerManifest.ItemSpec, serverFile);
    
            return true;
        }
    }
    

    The RegEx is a little sloppy as this was a quick and dirty example but this is effectively how you would go about doing this kind of thing.

    Be sure to add references to the MSBuild libraries.

    http://blogs.msdn.com/b/msbuild/archive/2006/01/21/515834.aspx

    If you don't want to build a custom task you can write a small console app that does the same thing but in my opinion a custom task is cleaner.

    Should you decide to go the console app route, you can leverage the BeforeBuild and AfterBuild events in your project file.

      <Target Name="BeforeBuild">
          <!-- Invoke console app -->
      </Target>
      <Target Name="AfterBuild">
      </Target>