Automating WiX Installer Builds with Maven: Windows Installer Xml

The next step in creating an installer after assembling the application is generating the actual msi.  There are a couple issues that need to be addressed when setting up the framework though.

  1. The installer may include jars that do not have version information or are snapshot releases.  This means that we have to be careful in how we create patches that upgrade files of the same name.
  2. We can’t leave unused or old jars in the lib folder.  If this was .Net, we wouldn’t have to worry as much about stray dlls but everything in the classpath is loaded.  That means the first jar wins if there’s a conflict.
  3. There could be a lot of dependencies and editing the installer source files on every change is a bit unrealistic.

First, let’s create the skeleton of our application.  Some cool things to note:

  • All of the important application information is pulled out of its pom.  This includes the Windows Installer upgrade code which should never change and a static product code to use when we build the application locally.  Jenkins injects a new product code every time so patches (as major upgrades) can be generated easily.
  • The maven build number and build helper plugins are used to generate a version number with every build.  They parse out the major and minor versions of our application as well as add a time-based build number.  Our versions end up being major.minor.yyDDD.HHmm.
  • The application is invoked with a batch file that is also run when anyone logs into the computer.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <Product Id="${installer.productCode}" Name="${installer.product}"
 Language="1033"
 Version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${buildNumber}"
 Manufacturer="${installer.manufacturer}" UpgradeCode="${installer.upgradeCode}">

<Package InstallerVersion="${installer.windows_installer_version}"
 Compressed="yes" InstallScope="perMachine" />

<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

<Directory Id="TARGETDIR" Name="SourceDir">
 <Directory Id="DesktopFolder" Name="Desktop" />
 <Directory Id="ProgramMenuFolder">
 <Directory Id="ApplicationProgramsFolder" Name="${installer.manufacturer}" />
 </Directory>
 <Directory Id="ProgramFilesFolder">
 <Directory Id="manufacturer_dir" Name="${installer.manufacturer}">
 <Directory Id="INSTALLLOCATION" Name="${installer.product}">
 <Directory Id="dir_logs" Name="logs">
 <Component Id="cmp_logs_dir" Guid="guid">
 <CreateFolder></CreateFolder>
 </Component>
 </Directory>
 </Directory>
 </Directory>
 <Component Id="cmp_version_txt_1"
 UninstallWhenSuperseded="yes" Guid="guid">
 <File Id="file_version_txt_1" Source="version.txt" KeyPath="yes" />
 </Component>
 <Component Id="cmp_start_bat_1" UninstallWhenSuperseded="yes"
 Guid="guid">
 <File Id="file_start_bat_1" Source="start.bat" KeyPath="yes">
 <Shortcut Advertise="yes" Id="shtct_start_bat_1"
 Directory="DesktopFolder" Name="${installer.product}"
 WorkingDirectory="INSTALLLOCATION" Description="Start ${installer.product}" />
 <Shortcut Advertise="yes" Id="shtct_start_bat_2"
 Directory="ApplicationProgramsFolder" Name="${installer.product}"
 WorkingDirectory="INSTALLLOCATION" Description="Start ${installer.product}" />
 </File>
 <RemoveFolder Id='ApplicationProgramsFolderRemove'
 Directory='ApplicationProgramsFolder' On='uninstall' />
 </Component>
 <Component Id="cmp_AutoStart" Guid="guid">
 <RegistryValue Action="write" Root="HKMU"
 Key="Software\Microsoft\Windows\CurrentVersion\Run" Name="VEView"
 Value="[INSTALLLOCATION]start.bat" Type="string" KeyPath="yes" />
 </Component>
 </Directory>
 </Directory>
 </Directory>
 </Directory>
</Product>
</Wix>

Now the most important directory, the lib folder. We found the best way to handle an ever-changing list of dependencies was to generate the lib folder wxs source file once and let paraffin update it automatically with every build. Some very important things to note:

  • UninstallWhenSuperseded should be set for every component.  This means that when paraffin removes a component since it doesn’t see it anymore in our assembled application’s lib folder, it will be completely removed fixing our stray jar file problem.
  • One file per component is the standard.
  • The directory paraffin looks for files is filtered in at build time.  Everything is automated!
</pre>
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <!--<CommandLineOptions>
 <Producer>Autogenerated by Paraffin - Wintellect - John Robbins - john@wintellect.com</Producer>
 <WARNING>Manual changes to this file may cause incorrect behavior.</WARNING>
 <CreatedOn>1/16/2012 11:42 AM</CreatedOn>
 <Directory>${project.build.directory}\${project.build.finalName}\${project.build.finalName}\lib</Directory>
 <Custom>lib-dir</Custom>
 <DirAlias>lib\</DirAlias>
 <Increment>1</Increment>
 <Guids>true</Guids>
 <Win64>false</Win64>
 <Multiple>false</Multiple>
 <Norecurse>false</Norecurse>
 <ExtensionExcludes>
 <Ext>.ZIP</Ext>
 </ExtensionExcludes>
 <DirExcludes />
<NextDirectoryNumber>1</NextDirectoryNumber>
 <NextComponentNumber>53</NextComponentNumber>
</CommandLineOptions>-->
 <Fragment>
 <ComponentGroup Id="group_lib_dir">
 <ComponentRef Id="comp_lib_dir_0"></ComponentRef>
 <ComponentRef Id="comp_lib_dir_1"></ComponentRef>
 <ComponentRef Id="comp_lib_dir_2"></ComponentRef>
 </ComponentGroup>
 <DirectoryRef Id="INSTALLLOCATION">
 <Directory Id="dir_lib_0" Name="lib">
 <Component Id="comp_lib_dir_0" Guid="guid" KeyPath="no" UninstallWhenSuperseded="yes" DiskId="1">
 <File Id="file_lib_dir_0" KeyPath="yes" Source="lib\\activeio-core-3.1.2.jar" />
 </Component>
 <Component Id="comp_lib_dir_1" Guid="guid" KeyPath="no" UninstallWhenSuperseded="yes" DiskId="1">
 <File Id="file_lib_dir_1" KeyPath="yes" Source="lib\\activemq-camel-5.5.1.jar" />
 </Component>
 <Component Id="comp_lib_dir_2" Guid="guid" KeyPath="no" UninstallWhenSuperseded="yes" DiskId="1">
 <File Id="file_lib_dir_2" KeyPath="yes" Source="lib\\activemq-core-5.5.1.jar" />
 </Component>
 </Directory>
 </DirectoryRef>
 </Fragment>
</Wix>

We generally have a automatically updated WiX source files for the conf and lib folders since they can change the most.  The build process also supports any number of wxs files so additional files with the application’s images, sounds, etc. is no problem.

The next post will detail how the process is actually automated.

Tagged with: , , ,
Posted in Java, Jenkins, Maven, WiX
2 comments on “Automating WiX Installer Builds with Maven: Windows Installer Xml
  1. […] hope to cover the cool WiX stuff we’ve done in the next post soon!  Here’s the next post! Share this:Like this:LikeBe the first to like this […]

Leave a reply to Automating WiX Installer Builds with Maven: Assembling Your Application « Matt McKnight's Blog Cancel reply