Automating Build Versioning and Publishing of Smart Device Projects with Team Foundation Build

A few months ago I sent out an email describing the process of setting up the Team Foundation Build System to play nicely with Smart Device Projects. This mail included: setting up the build machine to work with Smart Device Projects, account permissions, publish location, versioning, and also a workaround to making Smart Device CAB projects buildable by TFS. I’m sure other developers have had to or will have to go through the same pains of figuring this out, so I decided to publish it a la blog.

I hit 3 snags while working on this:

  1. Build Service Setup and Permissions
  2. Automatic versioning in the standard Major.Minor.Build.Revision style
  3. Smart Device CAB files are not supported by MSBuild

Build Service Setup and Permissions

  1. Set up Team Build on the Build machine (BuildMachine)
    1. Install Team Build
    2. Install Visual Studio 2008 on the machine as well, otherwise Compact Framework projects will not compile.
    3. Install the Windows Mobile 6 Professional and Standard SDKs, Visual Studio 2008 only ships with Windows Mobile 5 SDKs.
  2. Make sure the Build Service account (TFSBuild) has full access and permissions to the share you are publishing (\\PublishMachine\Builds\...).
  3. Create a Build Agent (only needs to be done once per server, and then is accessible to everyone). In Visual Studio, click Build, and type the server name (BuildMachine).
  4. Create a Build Definition for your project
    1. In Team Explorer, under Builds, create a new Build definition. The wizard is very straightforward. Choose a location in the source tree, and choose a build configuration (Release). This will create a tfsbuild.proj file in the location you specified in the source tree. You will need to edit this file later on.
    2. Right click the build definition and perform a build. Verify it succeeded. You will notice that the build number is Ole Automation dated. Not very easy on the eyes.

Automatic Versioning

Microsoft has some generic MSBuild tasks available that can be used to handle this. They are a separate download from Visual Studio however. Here’s the relevant links:

  1. Setting up your Solution
    1. Add the Microsoft.Sdc.Common.tasks and Microsoft.Sdc.Tasks.dll to the root of your solution. Using the ones provided by Microsoft actually caused me build errors for whatever reason. Some of the tasks they included were the cause. I have commented out all the tasks but those necessary to accomplish versioning. I have attached the .dll and the edited .tasks file to this blog.
  2. Preparing the Version.xml File
    1. I have attached a sample Version.xml to this blog. Save this to a network share (preferably the same as the one being used when the Build definition was created). The task will reference this file to retrieve the current version, increment it, and save the new version back to the file.
    2. The build service account (TFSBuild) needs to have full permissions to this file. This file must not be read only.
  3. Modifying the TFSBuild.proj
    1. Check the tfsbuild.proj file out for edit
    2. Add the following XML blob before the ending </Project> tag:
    3. <!-- This will retrieve and increment a version number from a file on a network share -->
      <Import Project="$(MSBuildProjectDirectory)\Microsoft.Sdc.Common.tasks" />
      <Target Name="BuildNumberOverrideTarget">
        <!-- Now update the version number -->
        <VersionNumber.Update VersionNumberConfigFileLocation="\\emsea100\Products\Blackberry Home Screen\Version.xml"
                              SkipSourceControl="true"
                              OnlyIncrementRevision="true">
          <Output TaskParameter="VersionNumber" PropertyName="BuildNumber" />
        </VersionNumber.Update>
      </Target>
      
      <!-- This will replace all version numbers in the assemblyinfo.cs files with the one from the share -->
      <!-- This target is called after Team build gets all the source files from TFS. -->
      <Target Name="AfterGet" DependsOnTargets="VersionAssemblies" />
      
      <Target Name="VersionAssemblies">
        <!-- Get the Assembly Info files.-->
        <CreateItem Include="$(SolutionRoot)\**\AssemblyInfo.cs;">
          <Output TaskParameter="Include" ItemName="AssemblyInfos"/>
        </CreateItem>
      
        <!-- Update the version numbers -->
        <File.Replace Force="true" Path="%(AssemblyInfos.FullPath)" NewValue="AssemblyVersion(&quot;$(BuildNumber)&quot;)" regularExpression="AssemblyVersion\(\&quot;(\d+.\d+.\d+.\d+)\&quot;\)" ignoreCase="true" />
      </Target>
    4. Edit the VersionNumberConfigFileLocation value to be configured for your publish machine and directory. You can also change the OnlyIncrementRevision to false so that the build number also gets autoincremented.

Building a Smart Device CAB Project Using MSBuild

There is no “good” way to do this. I did this by creating a standard Smart Device CAB project, and then building it on my development machine. If your project is called SmartDeviceCab, in the SmartDeviceCab\Debug folder, there is a SmartDeviceCab.inf file. That file can be used as input to CabWiz.exe to create the file from a command prompt. I will be using the INF file of our recently released tool, Device IP Utility, as an example.

  1. Solution Setup
    1. Copy the EMIPUtil.inf file to the solution root folder.
    2. Right click the solution and add existing file: %SolutionRoot%\ EMIPUtil.inf
  2. Modifying the INF file
    1. The INF file will contain a section of absolute paths to the location of the binaries on the development machine. This needs to be changed to absolute paths on the build machine. Note that all the binaries on the build machine will be dumped into the same directory, making it relative easy to edit. The first INF file shows what the file may look on your development machine, and the second will show how it should look on your build server.
      1. Old Paths Local to Your Developement Machine

        1=,”Common1″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\EnterpriseMobile.WindowsMobile.IPUtil\bin\Release\”
        2=,”Common2″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\EnterpriseMobile.WindowsMobile.IPUtil\obj\Release\”
        3=,”Common3″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\EnterpriseMobile.WindowsMobile.Utilities\bin\Release\”
        4=,”Common4″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\EnterpriseMobile.WindowsMobile.Utilities\obj\Release\”
        5=,”Common5″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\WMIPUtil\obj\Release\”
        6=,”Common6″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\WindowlessControls\bin\Release\”
        7=,”Common7″,,”C:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\WindowlessControls\obj\Release\”
        8=,”Common8″,,”c:\Users\kdutta\Documents\Visual Studio 2008\Projects\Device IP Utility\Device IP Utility - Production\EnterpriseMobile.WindowsMobile.IPUtil.Interop\Windows Mobile 5.0 Smartphone SDK (ARMV4I)\Release\”

      2. New Paths on Build Machine

        1=,”Common2″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        2=,”Common3″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        3=,”Common4″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        4=,”Common5″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        5=,”Common6″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        6=,”Common7″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        7=,”Common8″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”
        8=,”Common9″,,”C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries\Release\”

  3. Making MSBuild call CabWiz.exe
    1. Check out and edit the tfsbuild.proj file
    2. Before the ending </Project> tag, insert the following:
        <!-- This will build a CAB file from an .inf file -->
        <Target Name="BeforeDropBuild" DependsOnTargets="MakeCAB"/>
        <Target Name="MakeCAB">
          <Message Text="Building a CAB file" Importance="normal"/>
          <Exec Command="&quot;C:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\cabwiz.exe&quot; &quot;C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Sources\EMIPUtil.inf&quot; /compress /dest &quot;C:\Documents and Settings\TFSBuild\Local Settings\Temp\Device IP Utility\Device IP Utility - Production\Binaries&quot;"/>
        </Target>
      
    3. Modify the following Command Attribute to be set up specifically for your build machine. This defines the path to CabWiz.exe, the path to your INF file, and the output directory of the CAB file.
    4. This XML insert calls the CabWiz.exe prior to publishing the binaries. CabWiz.exe adds the CAB file to the binaries folder, which in turn gets published with everything else.

Sadly there is somewhat poor support for Smart Device Projects in TFSBuild, so making it work isn’t exactly elegant, but it works!

1 comments:

Joshua Smith said...

Thanks for helpful tips. For business solutions it is the right way to get software development services.