从WiX库项目中引用WixVariable到WiX安装程序项目

27

我正在尝试配置WiX安装程序和库,使得库中一个文件的版本号被用作安装程序的Product/@Version。

背景

如果在一个本地定义文件的安装程序中进行设置,则相对比较简单,只需要假设组件项目被WiX项目引用并进行配置即可:

  <Component Id="Company.Assembly" Guid="[GUID]">
    <File Id="Company.AssemblyFile"
          Name="Company.Assembly.dll" KeyPath="yes"
          DiskId="1"
          Source="$(var.Company.Assembly.TargetPath)" />
  </Component>

然后可以将产品版本设置为

  <Product Id="[GUID]"
           Name="Product Name"
           Language="1033"
           Version="!(bind.FileVersion.$(var.Company.AssemblyFile
                    .TargetFileName))"
           Manufacturer="Company Name"
           UpgradeCode="[GUID]">

问题

将所有组件移动到 WiX 库项目后,不再可以直接引用 !(bind.FileVersion.$(var.Company.AssemblyFile.TargetFileName)) 变量。

我尝试在库中配置了一个 WixVariable。

WixVariable Id="BuildVersion" Value="!(bind.FileVersion.Company.AssemblyFile)"/>

然后从设置中引用它。

  <Product Id="[GUID]"
           Name="Product Name"
           Language="1033"
           Version="!(wix.BuildVersion)"
           Manufacturer="Company Name"
           UpgradeCode="[GUID]">

没有成功。

在库或设置中是否需要一些额外的步骤或语法才能使WixVariable(或其某个派生)可从安装程序中访问?

2个回答

54

我经常听到这个问题,而且我认为WiX文档没有很好地解释这种情况,因此在这里我来解释一下。简短的答案是你的语法是正确的;使用WixVariable 元素声明的变量用!(wix.VariableName) 的语法引用,你可以使用在引用库中已定义的变量,因此!(wix.BuildVersion) 对于上面给出的示例是正确的。它不起作用的原因是它需要在编译阶段进行验证,但它直到链接阶段才生成。以下是详细解释:

在*.wxs文件中,有两种不同类型的变量可以引用:预处理器变量和绑定器(或链接器)变量。前者使用$语法引用,例如$(var.VariableName),后者使用!语法引用,例如!(bind.FileVersion.FileId)。主要区别很简单:预处理器变量在编译阶段(由candle.exe)解析,绑定变量在链接阶段(由light.exe)解析。编译器负责将源*.wxs文件编译为*.wixobj文件;它不处理实际有效载荷,因此不能从链接文件读取版本信息。然后将*.wixobj文件传递给链接器,该链接器处理有效载荷并创建MSI数据库。链接器负责从有效载荷中收集元数据,这就是它可以为变量提供值,例如!(bind.FileVersion.FileId)

请注意,使用WixVariable元素声明的变量使用!语法引用,因此它是绑定器变量;它将对light.exe可用,但对于candle.exe不可用。这是一个问题,因为candle.exe会对某些字段(如Product/@Version)应用验证。它不知道!(wix.BuildVersion)将评估为什么,因此无法验证它是否会生成有效版本。相比之下,您可以使用!(bind.FileVersion.FileId) ,因为在编译时,candle已经满足它将在链接时解析为有效版本(FileId是产品中文件的直接引用,因此candle信任它将存在以在链接时产生版本号)。

因此,你可以在*.wxs中的任何其他地方使用!(wix.BuildVersion) ,但你不能将其用作Product/@Version的值。据我所知,你只能在这里使用唯一的绑定器变量是!(bind.FileVersion.FileId),但如果你想从引用库中获取值,则显然这是不行的。否则,你只能从其他地方获取版本信息并在编译时将其传递到WiX,以便它在编译时可用。如果你使用MSBuild,则可以通过GetAssemblyIdentity任务查询版本信息,并通过DefineConstants属性将其传递给WiX。你的*.wixproj文件中应包含以下目标:

<Target Name="BeforeBuild">
  <GetAssemblyIdentity AssemblyFiles="[Path.To.Target.File]">
    <Output TaskParameter="Assemblies" ItemName="AsmInfo" />
  </GetAssemblyIdentity>
  <CreateProperty Value="%(AsmInfo.Version)">
    <Output TaskParameter="Value" PropertyName="BuildVersion" />
  </CreateProperty>
  <CreateProperty Value="$(DefineConstants)">
    <Output TaskParameter="Value" PropertyName="DefineConstantsOriginal" />
  </CreateProperty>
  <CreateProperty Value="$(DefineConstants);BuildVersion=$(BuildVersion)">
    <Output TaskParameter="Value" PropertyName="DefineConstants" />
  </CreateProperty>
</Target>
<Target Name="AfterBuild">
  <CreateProperty Value="$(DefineConstantsOriginal)">
    <Output TaskParameter="Value" PropertyName="DefineConstants" />
  </CreateProperty>
</Target>

BuildVersion属性将传递给candle.exe,因此您可以使用预处理器变量$(var.BuildVersion)进行引用。这肯定不如将所有内容都保留在*.wxs文件中那样干净,但这是将版本信息传递到烛光程序中以便在Product/@Version中用作变量的一种方法。我肯定会很乐意听到更好的做法。


1
谢谢,非常详细的回答。有趣的是,您还回答了我的后续问题,关于如何将变量写入DefineConstants。 - Michael Phillips

1

当我使用Heat.exe时,遇到了类似的问题,这使我能够覆盖源变量,而不会干扰系统环境变量或其他变量:

"%wix%bin\heat.exe" dir "$(SolutionDir)Web\obj\$(Configuration)\Package" -cg PACKAGEFILES -gg -g1 -sreg -srd -dr DEPLOYFOLDER -var wix.PackageSource="$(SolutionDir)Web\obj\$(Configuration)\Package" -out "$(SolutionDir)WebInstaller\PackageFragment.wxs"

我认为这种语法可以用于任何变量声明,在链接时不会出现问题:

<wix.YOURVAR="VALUE">-in command line/<!(wix.YOURVAR="VALUE")>-in .WXS files

也许在其他地方也有帮助。

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