This project has moved and is read-only. For the latest updates, please go here.

Update PortBindings with Dynamic Version Number from TFS Team Build

Topics: Bindings File, Settings Management and SSO, Tips and Tricks
Jun 23, 2014 at 11:56 AM
Hi.

I need to supply assembly version number of the BizTalk assemblies to the PortBindingsMaster.xml file. Is there a way of doing this? Can I refer to MSBuild properties within the PortBindingsMaster.xml file? Or do I need to create a new task?

Background:
I do this, because I want to completely control the version number from TFS. The version number will be the same for all the biztalk assemblies. I already use MSBuild task VersionNumber from the ExtensionPack. It works fine, but I need to be able to specify the assembly version build in the binding file, so that the bindings will import correctly.

Any hints? I've looked all over and cannot find anything.

/Bo
Jun 23, 2014 at 6:49 PM
Edited Nov 27 at 9:05 AM
What I've done in the past is change the AssemblyFileVersion attribute instead of AssemblyVersion to avoid the hassle of changing the main version number. You can easily view the AssemblyFileVersion in the DLL Properties page in Windows Explorer, but in the GAC and to .NET the AssemblyVersion remains unchanged.

To do what you're describing you'd probably run a second XmlPreprocess pass on the binding file. XmlPreprocess has a command line parameter /d to define additional properties, like /d:${RegEx=1.0.0.0}=1.1.294.0.
<Target Name="UpdateAssemblyVersion" AfterTargets="PreprocessBindings">
  <Exec
    Command="&quot;$(DeployTools)\xmlpreprocess.exe&quot; /i:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /o:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /d:${Regex=1.0.0.0}=$(AssemblyVersion)"
    ContinueOnError="false" Condition="'$(EnableXmlPreprocess)' == 'true' and '$(RequireXmlPreprocessDirectives)' == 'true'" />
  <Exec
    Command="&quot;$(DeployTools)\xmlpreprocess.exe&quot; /noDirectives /i:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /o:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /d:${Regex=1.0.0.0}=$(AssemblyVersion)"
    ContinueOnError="false" Condition="'$(EnableXmlPreprocess)' == 'true' and '$(RequireXmlPreprocessDirectives)' == 'false'" />
</Target>
With this in your .btdfproj file, you'd leave the version numbers as 1.0.0.0 in PortBindingsMaster.xml and pass a property from Team Build to the .btdfproj build named AssemblyVersion holding the new version number.

Thanks,
Tom
Jun 24, 2014 at 3:00 PM
Hi Tom.

Thanks for the update. I can't get it working with passing commandline arguments:

No matter what I do - when I pass commandline parameters to MSBuild (that is what I'm doing at TFS Buildserver) - the MSI installer later "forgets" and go to default values.

I tried it locally also with commands like:

msbuild /p:AssemblyMinorVersion=97 Deployment\Deployment.btdfproj
(I split the assemblyversion in Major and Minor in the above Target).

I also tried

msbuild /t:Installer /p:AssemblyMinorVersion=97 Deployment\Deployment.btdfproj

And the Installer does not pick up the AssemblyMinorVersion I supplied - but defaults to what i placed in the propertygroup:
    <AssemblyMajorVersion>0</AssemblyMajorVersion>
    <AssemblyMinorVersion>93</AssemblyMinorVersion>
  </PropertyGroup>
I tried doing a simple test of passing arguments
  <Target Name="TestVar">
    <Message text="AssemblyMinor $(AssemblyMinorVersion)"></Message>
  </Target>
and running only that target from the commandline - and that works.

I'm thinking that I need to store the commandline arguments somewhere "inside", because the actual preprocessing is happening at install stage.
Maybe that could be the problem?

Regards,
Bo.
Jun 24, 2014 at 5:14 PM
Edited Nov 27 at 9:06 AM
Hi Bo,

Sorry, I focused on your specific question and didn't consider the full picture of what you're trying to do.

You are building the solution and .btdfproj under TFS Build, which is passing a dynamic version number to the .btdfproj. The .btdfproj build is executing the Installer target to generate an MSI. You need the port bindings file to reflect the dynamic version number prior to loading into BizTalk.

The Installer target is simply going to package the MSI, so it will never use the extra parameters that you are passing in.

You have a couple of options:
  1. Use a custom MSBuild task or WF activity to locally (on the build server) do a search and replace on the version number in PortBindingsMaster.xml before the MSI is built (BeforeTargets="Installer" on a custom target).
  2. Persist the dynamic version number into a file that can be packaged into the MSI and referenced at deploy time.
I think both are good solutions. If you want to use #2, I'd suggest dynamically writing a .targets file to the source directory on the build server (into the parent of the Deployment project folder). It would look like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AssemblyMajorVersion>1</AssemblyMajorVersion>
    <AssemblyMinorVersion>2</AssemblyMinorVersion>
  </PropertyGroup>
</Project>
Then in your .btdfproj, you'd load the dynamically generated file and have it included into the MSI:
  <ItemGroup>
    <AdditionalFiles Include="AssemblyVersion.targets">
      <LocationPath>..</LocationPath>
    </AdditionalFiles>
  </ItemGroup>

  <Import Project="..\AssemblyVersion.targets" />
Essentially this is moving your existing PropertyGroup definition into a dynamically generated file and leaving the rest unchanged.

I hope that helps!
Tom
Jun 25, 2014 at 12:28 PM
Hi Tom.
Thanks a lot for your reply. I should have posed my initial question in a more precise way :-)
Your last reply helped me the rest of the way.
For others, I post the complete code below.
Again, thanks for the great BTDF package.
Regards,
Bo.
  <Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks"/>

  <!--Create file dynamically-->
  <Target Name="CreateAssemblyVersionFile" BeforeTargets="Redist">
    <Message text="Createing AssemblyVersion.targets with $(AssemblyMajorVersion).$(AssemblyMinorVersion)"/>
    <Exec Command="attrib -R ..\AssemblyVersion.targets"/>
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="Create" Files="..\AssemblyVersion.targets" />
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="WriteLines" Files="..\AssemblyVersion.targets" Lines="&lt;Project xmlns=&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&gt;"/>
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="WriteLines" Files="..\AssemblyVersion.targets" Lines="&lt;PropertyGroup&gt;"/>
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="WriteLines" Files="..\AssemblyVersion.targets" Lines="&lt;AssemblyMajorVersion&gt;$(AssemblyMajorVersion)&lt;/AssemblyMajorVersion&gt;"/>
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="WriteLines" Files="..\AssemblyVersion.targets" Lines="&lt;AssemblyMinorVersion&gt;$(AssemblyMinorVersion)&lt;/AssemblyMinorVersion&gt;"/>
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="WriteLines" Files="..\AssemblyVersion.targets" Lines="&lt;/PropertyGroup&gt;"/>
    <MSBuild.ExtensionPack.FileSystem.File TaskAction="WriteLines" Files="..\AssemblyVersion.targets" Lines="&lt;/Project&gt;"/>
  </Target>
  
  <!--include file in MSI-->
  <ItemGroup>
    <AdditionalFiles Include="AssemblyVersion.targets">
      <LocationPath>..</LocationPath>
    </AdditionalFiles>
  </ItemGroup>

  <Import Project="..\AssemblyVersion.targets" />

  <!-- update assembly version i portbindings.xml -->
  <Target Name="UpdateAssemblyVersion" AfterTargets="PreprocessBindings">
    <Message text="Updating assembly major and minor in bindings Major $(AssemblyMajorVersion) Minor $(AssemblyMinorVersion)"/>
    <Exec
      Command="&quot;$(DeployTools)\xmlpreprocess.exe&quot; /i:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /o:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /d:${Regex=1.0.0.0}=$(AssemblyMajorVersion).$(AssemblyMinorVersion).0.0"
      ContinueOnError="false" Condition="'$(EnableXmlPreprocess)' == 'true' and '$(RequireXmlPreprocessDirectives)' == 'true'" />
    <Exec
      Command="&quot;$(DeployTools)\xmlpreprocess.exe&quot; /noDirectives /i:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /o:&quot;$(MSBuildProjectDirectory)\$(PortBindings)&quot; /d:${Regex=1.0.0.0}=$(AssemblyMajorVersion).$(AssemblyMinorVersion).0.0"
      ContinueOnError="false" Condition="'$(EnableXmlPreprocess)' == 'true' and '$(RequireXmlPreprocessDirectives)' == 'false'" />
  </Target>

Marked as answer by tfabraham on 6/26/2014 at 9:24 PM
Jun 27, 2014 at 5:20 AM
Perfect, thanks for posting the code!

Tom