Include MajorVersion etc in filename (OutputName) when building MSI file (wix project)

19,177

Solution 1

This common task should be simplified in future versions of WiX!

This solution combines @Wimmel's and this post. It draws the version from a target .exe, and otherwise does not store version numbers in files; it doesn't rename the output file in post-build. But, it is necessary to update the property ProjectDefineConstants, from which the candle arguments are derived (in wix.targets). Otherwise, updating only the TargetPath property does not change the inputs to candle.exe.

*.wixproj:

<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeBuild">
  <!-- Read the version from the to-be-installed .exe -->
  <GetAssemblyIdentity AssemblyFiles="path.to.primary.exe">
    <Output TaskParameter="Assemblies" ItemName="AsmInfo" />
  </GetAssemblyIdentity>

  <!-- Create the MSBuild property $(VersionNumber) -->
  <CreateProperty Value="%(AsmInfo.Version)">
    <Output TaskParameter="Value" PropertyName="VersionNumber" />
  </CreateProperty>

  <!-- Create the WiX preprocessor variable $(var.VersionNumber) -->
  <CreateProperty Value="$(DefineConstants);VersionNumber=$(VersionNumber)">
    <Output TaskParameter="Value" PropertyName="DefineConstants" />
  </CreateProperty>

  <!-- Update the MSBuild properties $(TargetName), etc. -->
  <CreateProperty Value="$(SolutionName)-$(Platform)-$(VersionNumber)">
    <Output TaskParameter="Value" PropertyName="TargetName" />
  </CreateProperty>
  <CreateProperty Value="$(TargetName)$(TargetExt)">
    <Output TaskParameter="Value" PropertyName="TargetFileName" />
  </CreateProperty>
  <CreateProperty Value="$(TargetName)$(TargetPdbExt)">
    <Output TaskParameter="Value" PropertyName="TargetPdbName" />
  </CreateProperty>
  <CreateProperty Value="$(TargetDir)$(TargetFileName)">
    <Output TaskParameter="Value" PropertyName="TargetPath" />
  </CreateProperty>
  <CreateProperty Value="$(TargetPdbDir)$(TargetPdbName)">
    <Output TaskParameter="Value" PropertyName="TargetPdbPath" />
  </CreateProperty>

  <!-- Update the MSBuild property from which candle.exe args are derived -->
  <CreateProperty Value="
    Configuration=$(ConfigurationName);
    OutDir=$(OutDir);
    Platform=$(PlatformName);
    ProjectDir=$(ProjectDir);
    ProjectExt=$(ProjectExt);
    ProjectFileName=$(ProjectFileName);
    ProjectName=$(ProjectName);
    ProjectPath=$(ProjectPath);
    TargetDir=$(TargetDir);
    TargetExt=$(TargetExt);
    TargetFileName=$(TargetFileName);
    TargetName=$(TargetName);
    TargetPath=$(TargetPath);
  ">
    <Output TaskParameter="Value" PropertyName="ProjectDefineConstants" />
  </CreateProperty>
</Target>

*.wxs

<Product Id="*" Version="$(var.VersionNumber)" ... >
  ...
</Product>

Solution 2

In your .wixproj file. Add the following section just before the </Project> tag.

<!-- rename the output msi with Version number -->
  <Target Name="AfterBuild">
    <GetAssemblyIdentity AssemblyFiles="[Path of the main assembly with the assembly version number you want to use]">
      <Output TaskParameter="Assemblies" ItemName="AssemblyVersion"/>
    </GetAssemblyIdentity>
    <Copy SourceFiles=".\bin\$(Configuration)\$(OutputName).msi" DestinationFiles=".\bin\$(Configuration)\$(OutputName)_%(AssemblyVersion.Version).msi" />
    <Delete Files=".\bin\$(Configuration)\$(OutputName).msi" />
  </Target>

This works for me.

Solution 3

It is not possible to read the .wxi file from the .wixproj file. So you have to use another way to specify the version. I can give an example where I read the version from a assembly included in the installer, and use that version to rename the msi;

Open the .wixproj file in an editor and add a ReadVersion target:

  <Target Name="ReadVersion">
    <GetAssemblyIdentity AssemblyFiles="bin\program.exe">
      <Output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities" />
    </GetAssemblyIdentity>
    <Message Text="AssemblyVersion = %(MyAssemblyIdentities.Version)" />
    <CreateProperty Value="$(TargetName) %(MyAssemblyIdentities.Version)">
      <Output TaskParameter="Value" PropertyName="TargetName" />
    </CreateProperty>
    <CreateProperty Value="$(TargetName)$(TargetExt)">
      <Output TaskParameter="Value" PropertyName="TargetFileName" />
    </CreateProperty>
    <CreateProperty Value="$(OutDir)$(TargetFileName)">
      <Output TaskParameter="Value" PropertyName="TargetPath" />
    </CreateProperty>
  </Target>

This reads the version from bin\program.exe, displays it for debugging purposes, and changes the TargetName, TargetFileName and TargetPath.

After the line containing <Import Project="$(WixTargetsPath)" />, add the following to inject this target into the build process:

  <PropertyGroup>
    <BuildDependsOn>ReadVersion;$(BuildDependsOn)</BuildDependsOn>
  </PropertyGroup>

Solution 4

One way would be to define the variables in your MSBuild script, and have it update Defines.wxi at build time, as in this example.

In your MSBuild script, you could define the version properties as follows:

  <PropertyGroup>
    <MajorVersion>1</MajorVersion>
    <MinorVersion>08</MinorVersion>
    <BuildVersion>11</BuildVersion>
    <WixConfigPath>.\Defines.wxi</WixConfigPath>

    <_VariableDefinitions>
      <Root>
        <VariableDefinition Name="MajorVersion" NewValue="$(MajorVersion)" />
        <VariableDefinition Name="MinorVersion" NewValue="$(MinorVersion)" />
        <VariableDefinition Name="BuildVersion" NewValue="$(BuildVersion)" />
      </Root>
    </_VariableDefinitions>
  </PropertyGroup>

  <Target Name="UpdateWixVars">
    <WixVarSubstitution SourceFile="$(WixConfigPath)" VariableDefinitions="$(_VariableDefinitions)"/>
  </Target>

Then running the UpdateWixVars target will update the version numbers in Defines.wxi with the version numbers specified in your MSBuild project.

Note that I could not find an actual compiled dll with this custom build task, so I had to create it by:

  1. Download the source from here. Build it and name the file Tranxition.BuildTasks.dll.
  2. Add the reference to the build task like so:

    <UsingTask TaskName="WixVarSubstitution"
         AssemblyFile="$(MSBuildExtensionsPath)\Tranxition\Tranxition.BuildTasks.dll"/>
    

Solution 5

You can accomplish this seamlessly by implementing these two answers:

The other answers are much too complex!

PS: If you want to drop the fourth digit, following semantic versioning, you can do it like this:

<Target Name="AfterBuild">
    <GetAssemblyIdentity AssemblyFiles="..\Path\To\MyProject\bin\$(Platform)\$(Configuration)\MyProject.dll">
        <Output TaskParameter="Assemblies" ItemName="AssemblyInfo" />
    </GetAssemblyIdentity>
    <PropertyGroup>
        <In>%(AssemblyInfo.Version)</In>
        <Pattern>^(\d+.\d+.\d+)</Pattern>
        <AssemblyVersion>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</AssemblyVersion>
    </PropertyGroup>
    <Move SourceFiles="bin\$(Platform)\$(Configuration)\MyProject.msi" DestinationFiles="bin\$(Platform)\$(Configuration)\CodeGenerator-$(AssemblyVersion).$(Platform).msi" />
</Target>

This will create an MSI named, for example, MyProject-1.2.3.x64.msi. See this answer for more.

Share:
19,177
Admin
Author by

Admin

Updated on July 02, 2022

Comments

  • Admin
    Admin almost 2 years

    In my Defines.wxi I have:

    <?define MajorVersion="1" ?>
    <?define MinorVersion="08" ?>
    <?define BuildVersion="11" ?>
    

    In my MyProject.Setup.wixproj I have:

    <OutputName>MyProject</OutputName>
    <OutputType>Package</OutputType>
    

    Is it possible to include the version variables in the filename somehow, so that my file can be named MyProject.1.08.11.msi?

    This didn't work (no such variable is defined):

    <OutputName>MyProject-$(MajorVersion)</OutputName>
    <OutputType>Package</OutputType>
    

    This didn't work (no such variable is defined):

    <Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
        <Copy SourceFiles="$(OutputPath)$(OutputName).msi" DestinationFiles="C:\$(OutputName)-$(MajorVersion).msi" />
    </Target>
    

    It seems very clear to me that $(MajorVersion) is not the correct way of fetching the definition from the Defines.wxi file. What is?


    Update

    I tried to put this in MyProject.Setup.wixproj:

    <InstallerMajorVersion>7</InstallerMajorVersion>
    <InstallerMinorVersion>7</InstallerMinorVersion>
    <InstallerBuildNumber>7</InstallerBuildNumber>
    

    ...

    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
        <OutputPath>bin\$(Configuration)\</OutputPath>
        <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
        <DefineConstants>PrebuildPath=..\..\obj\prebuild\web\;InstallerMajorVersion=$(InstallerMajorVersion);InstallerMinorVersion=$(InstallerMinorVersion);InstallerBuildNumber=$(InstallerBuildNumber)</DefineConstants>
    </PropertyGroup>
    

    And this in Defines.wxi:

    <?define MajorVersion="$(var.InstallerMajorVersion)" ?>
    <?define MinorVersion="$(var.InstallerMinorVersion)" ?>
    <?define BuildVersion="$(var.InstallerBuildNumber)" ?>
    <?define Revision="0" ?>
    <?define VersionNumber="$(var.InstallerMajorVersion).$(var.InstallerMinorVersion).$(var.InstallerBuildNumber)" ?>
    

    Didn't work either. Got these error messages:

    • The Product/@Version attribute's value, '..', is not a valid version. Legal version values should look like 'x.x.x.x' where x is an integer from 0 to 65534.
    • The Product/@Version attribute was not found; it is required.