Automated Patch Building with WiX and Visual Studio

by bryanpjohnston

I’ve said previously that I generally prefer to implement major upgrades with each release and try not to worry about patching. I think it makes for a cleaner installation and is easier to manage. I mentioned in this post a way to update your installation by simply chaining an additional msi to your WiX bootstrapper. While that certainly is possible it feels a bit of a hack, and if you are fortunate enough to have authored your msi correctly from the beginning, you could use more traditional patch creation techniques. Until recently I haven’t had to create patches. While software companies are becoming more agile, there is a greater need to release software more often and deploy those updates online. I feel this creates a bigger need for patches. Furthermore, if you are developing a patch, you need an automated way of incorporating it into your build process so that it can be tested with each build.   After working through this process recently, I thought I would share a method of automatically generating a patch with each build of your installer project.

For the purposes of this example, I will make the following assumptions:

  • We have released version 1 of our installer called myProduct.msi
  • The released version of myProduct.msi is stored internally on a server at the path //myServer/released/myProduct.msi
  • The path to the WiX binaries is in our Path, so that we can run tools like Candle, Light, Torch, and Pyro on the command line without specifying the entire path to the executables.

Step 1: Create the Patch Authoring

Below is my Patch.wxs file which will specify which components I want to include in my patch. A few things to note.

  • The PatchBaseline Id can be whatever you want to call it. You will just need to reference it later in Step4.
  • You will add ComponentRefs to your PatchFamily if you want them to be included in your patch. Otherwise they will be ignored.
  • You’ll see in my example below that I’ve included the PropertyRef for product version, so that the version information gets updated when I install the new patch.
<?xml version=1.0 encoding=UTF-8?>
<Wix xmlns=http://schemas.microsoft.com/wix/2006/wi xmlns:util=http://schemas.microsoft.com/wix/UtilExtension>
  <?include “..\Config.wxi”?>
  <Patch  AllowRemoval=no Manufacturer=$(var.Company) DisplayName=$(var.InstallerProductName) Patch $(var.Build) Description=Small Update Patch Classification=Update>
    <Media Id=5000 Cabinet=patch.cab EmbedCab=yes>
      <PatchBaseline Id=RTM/>
    </Media>
    <PatchFamily Id=MyPatchFamily Version=$(var.Major).$(var.Minor).$(var.Build) Supersede=yes>
      <PropertyRef Id=ProductVersion/>
      <!–<ComponentRef Id=”MyComponent”/>–>
    </PatchFamily>
  </Patch>
</Wix>

Also, you will notice I use a lot of variables referenced in Config.wxi file. That file typically gets updated each build with the latest version number and new product codes, and will look something like this:

<?xml version=1.0 encoding=utf-8?>
<Include>
  <?define Company = “MyCompany” ?>
  <?define Major = “1”?>
  <?define Minor = “0”?>
  <?define Build = “0”?>
  <?define Revision = “0”?>
  <?define InstallerVersion = “$(var.Major).$(var.Minor).$(var.Build)”?>
  <?define InstallerProductName = “My Product Name”?>
  <?define InstallerProductCode = “PUT-GUID-HERE”?>
  <?define InstallerUpgradeCode = “PUT-GUID-HERE”?>
</Include>

Step 2: Perform Admin Installations of Your Released and Update Msis

In order to create a patch, we have to compare two msis to get their differences. We assume we already have the released msi sitting somewhere on a server. Therefore when we build the new version of our intaller, we can add PostBuild events (to the installer project itself) to copy our released msi to our temp directory, and perform administrative installations of each msi in the temp directory. This is necessary because in the next step we will use Torch to compare these two installations.

  1.  Remove any previous installation from %TEMP%\MyProductInstall
  2. Copy the released version of the msi to your temp directory
  3. Perform admin install of released msi
  4. Perform admin install of recently built msi
    
    FOR /D /R "%TEMP%\MyProductInstall" %%X IN (*.*) DO RD /S /Q "%%X"
    xcopy "//myServer/released/myProduct.msi" "%TEMP%\MyProductInstall\RELEASEDMSI\" /Y /I
    msiexec.exe /a "%TEMP%\MyProductInstall\RELEASEDMSI\myProduct.msi" /qb TARGETDIR="%TEMP%\MyProductInstall\RELEASEDMSI\Install"
    msiexec.exe /a "$(TargetDir)myProduct.msi" /qb TARGETDIR="%TEMP%\MyProductInstall\UPDATEMSI\Install"

Step 3: Generate the Differences Between the Msis

To generate the differences between the msis we will use the WiX tool Torch. In the example below, we compare the released msi with the update msi to generate the differences file diff.wixmst. Also, pay particular attention to the following command line switches:

  • -a    Indicates that these are administrative installs
  • -xo    Indicates the output file will be in wixout format rather than MST format.

We just add the following line to at the end of the PostBuild events for our installer project.

    torch.exe -p -xo -a "%TEMP%\MyProductInstall\RELEASEDMSI\Install\myProduct.msi" -a "%TEMP%\MyProductInstall\UPDATEMSI\Install\myProduct.msi" -out "$(TargetDir)diff.wixmst"

Step 4: Building the Patch

Now we are ready to build the final patch. We use Candle and Light to compile and link our patch authoring from Step 1. Then we use Pyro to build the final Patch.msp.

  • In my example, Patch.wxs is included in a “Patch” subdirectory in the solution, and not in the installer project itself. It’s up to you how you organize your solution.
  • Notice the -t switch in Pyro. We pass it the PatchBaseline from Step 1 (in our case RTM), and the diff transform from the previous step.

Again the following commands can be added to the list of PostBuild events for your intaller project.

    candle.exe "$(SolutionDir)Patch\Patch.wxs"
    light.exe "$(TargetDir)Patch.wixobj" -out "$(TargetDir)Patch.wixmsp"
    pyro.exe "$(TargetDir)Patch.wixmsp" -out "$(TargetDir)Patch.msp" -t RTM "$(TargetDir)diff.wixmst"

Step 5: Test

Once you add all the commands from Steps 2, 3 and 4 to your PostBuild events, your patch will get built each time you build your installer project. To install the patch, run

    msiexec /p Patch.msp

or better yet, add it to the chain of your WiX bootstrapper.

Advertisements