Reputation: 4684
Currently, whenever I build my package, I have to manually increment the Version attribute inside Product.wxs file like this:
<Product
Id = "*"
Version="4.1.3"
I'd like to automate that, to streamline the build process. We use the following versioning scheme for exe/dll files:
major.minor.special.build
The special is almost never used, and set to 0, and the convention was to version the packaged MSI's as following, since you can only use three numbers:
major.minor.build
The only solutions I've seen let you grab the 4 digit version of the other project, and then truncate the build version, so you end up with this:
major.minor.special
Clearly that won't work with our scheme, since we lose the build number. How can I grab major.minor.build
, ignoring special?
Upvotes: 5
Views: 8106
Reputation: 560
Based on Tom Blodget answer i did the following:
<projectName>.wixproj
file i added the following under <target Name="BeforeBuild">
:<GenerateProductVersion AssemblyPath='path\to\file.dll' />
<Project>
tag (for example, just before the closing), i added something like this: <UsingTask TaskName="GenerateProductVersion" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<AssemblyPath ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Using Namespace="System" />
<Using Namespace="System.Xml.Linq" />
<Using Namespace="System.Reflection" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var assemblyVersion = AssemblyName.GetAssemblyName(AssemblyPath).Version;
//var productVersion = String.Format("{0}.{1}.{2}", assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Revision);
//Log.LogMessage(MessageImportance.High, "ProductVersion=" + productVersion + " extracted from assembly version of " + AssemblyPath);
new XDocument(
new XElement("Include",
new XProcessingInstruction("define", "ProductVersion=" + assemblyVersion)))
.Save("ProductVersion.wxi");
]]>
</Code>
</Task>
</UsingTask>
When compiling the wix project, this will generate the ProductVersion.wxi as Tom explains.
<Include>
<?define ProductVersion=1.0.38549?>
</Include>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include ProductVersion.wxi?>
<Product Version="$(var.ProductVersion)" …>
…
</Wix>
Hope this can clarify you a bit more!
Upvotes: 2
Reputation: 20812
I use a WiX variable from an include file that I regenerate with every build.
Since my project is a .wixproj (MSBuild/Visual Studio), I just code the version extraction and formatting right in there as a custom, inline MSBuild task and call it in the BeforeBuild target.
In the example below, I get the assembly version of the main assembly of the product. You can code it up for any version you want.
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include ProductVersion.wxi?>
<Product Version="$(var.ProductVersion)" …>
…
</Wix>
<Include>
<?define ProductVersion=1.0.38549?>
</Include>
I recommend including the .wxi file in the project so it's visible in the Solution View. And, since it's generated, I recommend excluding it from source control.
A .wixproj is both a Visual Studio project file and an MSBuild project file. To edit a Visual Studio project file in Visual Studio, pick a tutorial or extension.
MSBuild systems, including WiX's, offer BeforeBuild and AfterBuild targets, as explained in the .wixproj comments.
Just pull the target out of the comments and add a task call.
<Target Name="BeforeBuild">
<GenerateProductVersion AssemblyPath='../wherever/whatever.exe' />
</Target>
Task code can be in its own MSBuild file or even DLL for reuse. Or, for a scripting approach, it can be inline.
There are 3 parts to this task:
.
<UsingTask TaskName="GenerateProductVersion" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<AssemblyPath ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Using Namespace="System" />
<Using Namespace="System.Xml.Linq" />
<Using Namespace="System.Reflection" />
<Code Type="Fragment" Language="cs"><![CDATA[
var assemblyVersion = AssemblyName.GetAssemblyName(AssemblyPath).Version;
var productVersion = String.Format("{0}.{1}.{2}", assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Revision);
Log.LogMessage(MessageImportance.High, "ProductVersion=" + productVersion + " extracted from assembly version of " + AssemblyPath);
new XDocument(
new XElement("Include",
new XProcessingInstruction("define", "ProductVersion=" + productVersion)))
.Save("ProductVersion.wxi");
]]></Code>
</Task>
</UsingTask>
All of this is hidden too well in the project file. Many project designers have a Build tab that allows entering name-value pairs into the build. That provides a mechanism to raise the path up out of the XML.
<Target Name="BeforeBuild">
<GenerateProductVersion AssemblyPath='$([System.Text.RegularExpressions.Regex]::Match(";$(DefineConstants);", ";VersionExtractionPath=(?<path>.*?);").Groups["path"].Value)' />
</Target>
Upvotes: 9