在构建MSI文件(wix项目)时,在文件名(OutputName)中包括MajorVersion等内容

26

在我的 Defines.wxi 文件中,我有:

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

在我的 MyProject.Setup.wixproj 文件中:

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

有没有可能在文件名中以某种方式包含版本变量,使我的文件可以命名为MyProject.1.08.11.msi?

这种方法不起作用(未定义任何此类变量):

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

这行代码没有生效(未定义此变量):

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

对我而言,很明显$(MajorVersion)不是从Defines.wxi文件中获取定义的正确方式。那正确的方式是什么呢?


更新

我尝试将以下代码放入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>

同时在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)" ?>

还是不行。出现了以下错误信息:

  • The Product/@Version 属性的值'..'不是一个有效的版本号。 合法的版本号应该看起来像'x.x.x.x',其中x是0到65534之间的整数。
  • 找不到 Product/@Version 属性;它是必须的。
7个回答

16

未来版本的WiX将简化这个常见任务!

此解决方案结合了@Wimmel和这篇帖子的内容。它从目标.exe中提取版本,否则不会在文件中存储版本号;它也不会在后期更改输出文件名。但是,必须更新ProjectDefineConstants属性,从中派生candle参数(在wix.targets中)。否则,仅更新TargetPath属性不会更改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>

非常好,这里是最佳解决方案。 - Zac Morris
@l33t 为什么?这个解决方案有什么问题吗?你的链接对我目前来说无法使用... - Nicolas
@Steve Mitchell 谢谢,这个对我有用。有没有比这更简单的方法来爬取项目可执行文件:$(ProjectDir)..\MyName\bin\$(Configuration)\MyExeName.exe?另外,如果使用调试二进制文件编译设置,是否有可能将“Beta”添加到msi名称中? - Nicolas
@Nicolas,试试这个链接:http://web.archive.org/web/20150416052138/http://wixtoolset.org/issues/4555/ - l33t
@l33t 谢谢链接!我检查了 candle.exe 对我打印 -d"TargetFileName=Correct Modified Name Including Version.msi",所以我认为这个问题已经解决了。 - Nicolas
GetAssemblyIdentity 仅适用于 .NET 程序集。它无法读取本机二进制文件中的 VS_VERSIONINFO 数据。T_T - Keith Russell

15

这就是我最终得到的,而且它能够工作!

Setup.Version.proj

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <InstallerMajorVersion>55</InstallerMajorVersion>
        <InstallerMinorVersion>66</InstallerMinorVersion>
    <InstallerBuildVersion>$(BuildNumber)</InstallerBuildVersion>
        <InstallerBuildVersion Condition="$(InstallerBuildVersion) == ''">0</InstallerBuildVersion>
  </PropertyGroup>
</Project>

MyProject.Setup.wixproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="Setup.Version.proj" />
  <PropertyGroup>
    <OutputName>MyProject_$(InstallerMajorVersion)_$(InstallerMinorVersion)_$(InstallerBuildVersion)</OutputName>
    <OutputType>Package</OutputType>
    <DefineConstants>InstallerMajorVersion=$(InstallerMajorVersion);InstallerMinorVersion=$(InstallerMinorVersion);InstallerBuildVersion=$(InstallerBuildVersion)</DefineConstants>
    ...

Defines.wxi

<?define MajorVersion="$(var.InstallerMajorVersion)" ?>
<?define MinorVersion="$(var.InstallerMinorVersion)" ?>
<?define BuildVersion="$(var.InstallerBuildVersion)" ?>

1
经过尝试,我发现它的工作原理与描述相同,而且 defines.wxi 不是必需的。然而,我卡了一段时间,因为我从网上复制粘贴了 <DefineConstants> 部分,但某种特殊字符或者其他东西 VS 没有接受 - 我总是得到 var.InstallerMajorVersion not found 错误。最终,它运行得非常好,我认为这是一个非常优雅的解决方案! - Damian Vogel
刚刚发现另一个可能的错误:你要确保<DefineConstants>标签没有在另一个PropertyGroup中定义,否则会出现var.InstallerMajorVersion not found错误。 - Damian Vogel
如果您更改了版本号,并且在装有WiX Visual Studio扩展程序的Visual Studio解决方案中加载了您的项目,则必须关闭并重新打开整个解决方案以使更改生效。不知何故,即使您取消加载并重新加载WiX项目,<Import>数据仍会保留。 - snips-n-snails
经过一些实验...如果直接将属性/属性放置在wixproj中,那么您只需要重新加载项目,而不是重新打开整个解决方案(VS2022 Community)...我喜欢这个想法,但显然“导入”并不支持此目标 :-) - dba

10

在你的 .wixproj 文件中,在 </Project> 标签之前添加以下部分。

<!-- 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>

这对我起作用。


5
一种方法是在MSBuild脚本中定义变量,并在构建时更新Defines.wxi,例如此示例所示。
在MSBuild脚本中,您可以按以下方式定义版本属性:
  <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>

运行UpdateWixVars目标将使用MSBuild项目中指定的版本号更新Defines.wxi中的版本号。

请注意,我无法找到实际编译的dll与此自定义构建任务一起使用,因此我必须通过以下方式创建它:

  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"/>
    

谢谢您的回答。我希望不使用Tranxition就能解决它。我更新了我的问题,并尝试了一些类似的方法。您对此有什么建议吗? - user479911
虽然这个答案可行,但它需要手动更新每个构建的文件,我更喜欢下面自动化处理的答案。 - nrjohnstone

5

无法从.wixproj文件中读取.wxi文件。因此,您必须使用另一种方式来指定版本。我可以举一个例子,在安装程序中包含的程序集中读取版本,并使用该版本重命名msi文件。

在编辑器中打开.wixproj文件并添加一个ReadVersion目标:

  <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>

这段代码会读取 bin\program.exe 的版本信息,用于调试目的,并且会更改 TargetName、TargetFileName 和 TargetPath。

在包含 <Import Project="$(WixTargetsPath)" /> 的行后,添加以下内容以将此目标注入到构建过程中:

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

我更新了我的问题,并附上了最新的尝试。现在可能更清晰了。 - user479911
然后您需要在wixproj的PropertyGroup中使用DefineConstants,请参见https://dev59.com/i3RB5IYBdhLWcg3wbGtB#627279 - wimh
是的,我也这样做了,现在已经粘贴了。看起来对我来说是正确的,但仍然出现了那个错误。 - user479911
这是一个很好的解决方案,因为它自动将主程序集的版本号包含在安装文件名中,而不需要手动更新文件。 - nrjohnstone
对我来说,这在隔离环境下可以运行,但与我的引导程序项目不兼容。 引导程序使用对msi的项目引用,仍在寻找安装程序的outputname.targetext,而不是targetfilename。 - JDennis
太好了!我只需要提供我的主程序集的相对路径(例如..\bin\MyAssembly.dll),安装程序就会带有版本号! - Sudhanshu Mishra

2
您可以通过实现以下两个答案来无缝完成此操作: 其他答案太过复杂!
PS:如果您想按照语义化版本规范删除第四位数字,可以像这样操作:
<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>

这将创建一个名为MyProject-1.2.3.x64.msi的MSI文件。欲了解更多信息,请查看此答案

1
我认为重命名MSI会引起问题:https://blogs.msdn.microsoft.com/robmen/2004/12/08/why-does-changing-the-name-of-your-msi-file-require-a-major-upgrade/ - Loring

0

这是一个完整的wixproj文件示例,您可以在UI中设置版本号并使用它来修改输出msi文件名。

在Visual Studio中(例如2015):

在项目属性窗口的“定义预处理器变量”中定义版本号。例如,我输入了VersionNumber=1.14;
在解决方案资源管理器中卸载您的项目;
右键单击解决方案资源管理器中的项目,选择编辑yourproject.wixproj文件;
在Target Name="BeforeBuild"元素中添加代码,如下所示。
```xml Debug x86 3.10 PROJECT-GUID 2.0 my project Package bin\$(Configuration)\ obj\$(Configuration)\ VersionNumber=1.14 bin\$(Configuration)\ obj\$(Configuration)\ VersionNumber=1.14 $(WixExtDir)\WixUIExtension.dll WixUIExtension $([System.String]::new('%(DefineConstantsKVPairs.Identity)').Split('=')[1]) ```

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接