Monday, April 1, 2013

InstallShield - Building multiple releases with MSBuild

A while ago I encountered this problem: I had an InstallShield project in Visual Studio IDE. The project had 3 releases and I wanted Visual Studio to build all 3 releases at every build.

InstallShield's Visual Studio project doesn't inherently support this. It allows you to select one of the releases in the solution's Configuration Manager dialog, and this release only gets built when you build the solution. So in order to build 3 releases I needed to build the solution 3 times - each with a different release selection in Configuration Manager.

To overcome this I added an empty project to the solution where I called InstallShield's command-line builder IsCmdBld 3 times - one for each release.

This involves some manual changes to the MSBuild file:

First we create a project configuration for each release. It will allow us to select 'Release', 'Release_1', 'Release_2' or 'All_Releases' in VS Configuration Manager dialog.

<PropertyGroup Condition=" '$(Configuration)' == 'Release' " />
<
PropertyGroup Condition=" '$(Configuration)' == 'Release_1' " />
<
PropertyGroup Condition=" '$(Configuration)' == 'Release_2' " />
<
PropertyGroup Condition=" '$(Configuration)' == 'All_Releases' " />

And create an ItemGroup for target batching. Target batching directs MSBuild to execute the target once for each release that we want to build - resembling a 'for-each' loop in programming. Notice the condition attribute which creates each item only if its own configuration or All_Releases was selected.

<ItemGroup>
 <IS_Release Include="Release" Condition="'$(Configuration)' == 'Release' OR '$(Configuration)' == 'All_Releases'">
  <Name>Release</Name>
 </IS_Release>
 <IS_Release Include="Release_1" Condition="'$(Configuration)' == 'Release_1' OR '$(Configuration)' == 'All_Releases'">
  <Name>Release_1</Name>
 </IS_Release>
 <IS_Release Include="Release_2" Condition="'$(Configuration)' == 'Release_2' OR '$(Configuration)' == 'All_Releases'">
  <Name>Release_2</Name>
 </IS_Release>

</ItemGroup>


Now we'll create the 'Build' target. To tell MSBuild that this target should be called once for each release we set the 'Outputs' attribute to an item metadata.

<Target Name="Build" Outputs="%(IS_Release.Name).NeverToExist">
 <Exec Command="IsCmdBld.exe -p &quot;$(IS_ProjectFile)
&quot; -a &quot;Default Configuration&quot; -r &quot;%(IS_Release.Name)&quot; -b &quot;$(IS_BuildDir)&quot;" />
</Target>


I've passed these command line arguments to the command-line  builder:
-p "project file path"
-a "configuration name"
-r "release name" (This is the batching ItemGroup)
-b "build folder"
There are other parameters which you can explore in InstallShield documentation.
I had to wrap each parameter with &quot; which denotes the " character in XML

The above code contains some properties the I've omitted for readability - Configuration, IS_ProjectFile, and  IS_BuildDir.

Now I can build one of the releases, or all of them at once - which is what I want to happen in the nightly build.