WiX 技巧: 使用 MSBuild 和 Windows Installer XML 执行自动发布

 本文讨论:

  Windows Installer XML 概述

  创建 WiX 打包说明

  集成 WiX 和 MSBuild

  自动执行生成和打包

  本文使用了以下技术:Visual Studio, Windows Installer XML (WiX), MSBuild

目录

  WiX 简介

  创建 WiX 文件WiX 和 MSBuildMSBuild 批处理自动执行生成和打包分析 MSBuild 脚本综述自定义过程总结

  在开发过程中,重要的是有自动的生成过程。同样重要的是要有自动创建发布的手段。遗憾的是,在很多组织中(尤其是较小的组织),并没有这些。通常,您会发现发布内容只是在最后一分钟才被拼合在一起。但是,如果花时间建立自动生成和发布计划,就会节省无数的时间,您就可以将这些时间更好地用在完成任务上,而不是用来生成和发布项目。

  在本文中,我将介绍如何使用 Microsoft® Build Engine (MSBuild) 和 Windows® Installer XML (WiX) 工具集在组织中实现自动和可重复的生成和发布过程。本文讨论了 WiX v2(注意,当 WiX v3 发布时,不会直接转换某些语法示例)。虽然 WiX 的确简化了创建发布的过程,但无论您是否使用 WiX 创建发布,本文描述的技术都可供您参考。也可以对这些技术进行一些修改后,将它们应用于不使用 Microsoft .NET Framework 2.0 所开发的应用程序(本文还包含指向英文网页的链接)。

  我将假定您熟悉 MSBuild(如果需要重新了解它,请参阅我在 2006 年 6 月的《MSDN®杂志》上的文章 深入了解 MSBuild:通过 Microsoft Build Engine 的自定义任务以自己的方式编译应用程序)。我将为那些不熟悉该工具的人提供 WiX 工具集的概述。请参见侧栏“MSBuild 和 WiX 资源”中关于更多相关文章和工具的参考。在本文中,为了进行演示,我将使用我的 Sedodream MSBuild 项目。您可以从 www.codeplex.com/Sedodream 获得最新源代码。

  WiX 简介

  创建应用程序时,通常最终结果是要在生产计算机上安装并运行它。WiX 工具集可以帮助您完成此任务。在这一节中,我将描述 WiX,并介绍如何使用 WiX 创建安装程序。

  WiX 描述了在目标计算机上的安装是什么样子的。从 Visual Studio® 到 Microsoft Office,在很多 Microsoft 应用程序中都使用了 Windows Installer。您可能会吃惊地发现:WiX 实际上是个开放源代码的项目,并且承载在 sourceforge.net 上。您可以从 wix.sourceforge.net 下载最新的二进制文件和源文件。下载并安装 WiX 后,您会发现计算机上安装了如图 1 中总结的大量可执行文件。我将重点介绍 Candle.exe 和 Light.exe 工具的使用。

Figure1WiX 组件

 

名称 说明
Candle.exe 将 WiX 源文件转换为中间表示形式。这实际上是另一个 XML 文件,但永远不应手动更改这些生成的文件。
Dark.exe 将 MSI 文件转换为适当的 WiX 源文件(可以视为“反编译”安装程序)。
Light.exe 从 WiX 源文件的中间表示形式生成 Windows Installer。
Lit.exe 生成 WiX 库,该库可用于生成其他安装程序包。
Tallow.exe 用于创建 WiX 源 XML,以复制它在安装目录或文件中的文件和文件夹。
WixCop.exe 检查 WiX 源文件中存在潜在问题的区域,类似于 FxCop。

  WiX 使用声明性语言,而不是过程性语言,这意味着您要描述您的安装将是什么样子的,而不用描述为了实现它需要执行哪些步骤。这可能与您的习惯不同,但它非常容易掌握。通常,要安装在目标计算机上的描述文件将填充 WiX 源文件。在这里,我将重点介绍这些组件。

  在 WiX 源文件中,有三个与您希望安装的文件有关的主要元素:文件、组件和功能。文件元素是对单个文件的引用。文件必须包含在组件元素中,组件元素是最小的安装单位。就是说,如果您有一个包含 100 个文件的组件,并且您要安装该组件,则会安装它包含的所有文件。相反,如果不安装该组件,则不安装任何文件。建议不要创建包含大量文件的组件。

  组件始终包含在功能元素中,并且可以包含在多个功能中。功能是一组组件,也可能是一组子功能。如果安装程序具有允许用户选择要安装哪些项的图形界面,则用户实际是在选择功能。

  创建 WiX 文件

  可以在您选择的任何文本或 XML 编辑器中创作 WiX 源文件。还可以使用 Visual Studio,利用 IntelliSense® 创作 WiX 源文件。如果在使用 Visual Studio 编辑 WiX 源文件时未启用 IntelliSense,则您唯一要做的是将 wix.xsd 文件复制到 Visual Studio 架构目录中。通常,此目录位于 %Program Files%Microsoft Visual Studio 8XmlSchemas。创建 WiX 源文件时,还需要生成很多 GUID。Visual Studio 有一个可用于此目的工具,还有一些 Visual Studio 宏也可用于分配快捷方式,以便直接插入新的 GUID。

  现在,让我们开始创建新的 WiX 源文件。第一个元素始终是 Wix 元素。Wix 元素的子元素包括 Product、Fragment、Module 和 PatchCreation。预期输出的类型将决定您将使用其中的哪个子元素。在这里,我希望最终结果是我的项目的安装程序数据库 (MSI) ,因此我将使用 Product 元素。下面是示例项目的 WiX v2 源文件 Sedodream.wxs 的开头部分:

<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'> 
  <Product Name='Sedodream MSBuild Project'
    Id='C9D25926-FCE0-4EB6-8FF5-4686EE5AB089'
    Language='1033' Codepage='1252' Version='1.0.0'
    Manufacturer='SedoTech'
    UpgradeCode='9C5E4073-EFDE-419B-935D-CE2632BC560E'>
   <Package Id='????????-????-????-????-????????????'
     Keywords='Installer'
     Description='Sedodream MSBuild Project Installer'
     InstallerVersion='100' Languages='1031'
     Compressed='yes' SummaryCodepage='1252' />
...

  可以看到,Product 元素有很多属性,其中最重要的是 Id 和 Name。Id 用于唯一标识产品。对于新的主要发布必须更改 Id。还应当记录下它的值,以便随后引用。Name 属性的值是将显示在“添加/删除程序”面板中的值,如果安装程序有 UI,它将显示在简介页上。

  Product 元素的第一个子项 Package 元素也显示在这里。注意,Id 属性包含一系列 GUID 格式的 ? 字符。通过使用此语法,代码指定应当在生成时生成 Id。创建安装程序时,可以让所创建的每个安装程序的 Package Id 各不相同,甚至让每次生成的安装程序不同。这是您可以让系统自动生成的唯一 GUID;其他所有 GUID 都需要保持相同,并应记录下来,供将来引用。

  几乎每个安装程序都会将至少一个文件放在目标计算机上,而且此安装程序没有差异。由于 WiX 使用声明性方式,所以我将声明目录结构是什么,并且将在目标计算机上重新创建它。我使用一系列 Directory 元素完成该操作:

<Media Id='1' Cabinet='Sedodream.cab' EmbedCab='yes'/>
<Directory Id='TARGETDIR' Name='SourceDir'>
  <Directory Id='ProgramFilesFolder' Name='PFiles'>
   <Directory Id='MSBuildDir' Name='MSBuild'>
     <Directory Id='INSTALLDIR' Name='Sedodrea'
     LongName='Sedodream'>

  此示例中的 Media 元素用于指定将生成一个压缩文件,并且要将它放在安装程序数据库中。这是必需元素,如果安装文件跨越多个介质,则可以有多个该元素。例如,如果在 CD 上分发应用程序,但它需要跨越多张盘。大多数情况下,不必担心这个问题。

  下面的 Media 是 Directory 元素系列。第一个 Directory 元素将是仅封装其他条目的虚拟元素。TARGETDIR 目录元素下的条目的 Id 值是 ProgramFilesFolder。您可能已猜到,这是众所周知的位置,并且 Windows Installer 将在启动时设置它的值。还有其他可以使用的系统文件夹;若要查看完整列表,请访问 msdn.microsoft.com/library/en-us/msi/setup/system_folder_properties.asp。这些系统属性始终解析为其完整路径。

  接下来的元素提供自定义目录的名称。如果可能,该目录将使用 LongName 属性进行命名;否则使用 Name 属性。如果这些目录都不存在,Windows Installer 将在安装时创建合适的目录。在前面的代码段中声明的内容的完整树以 C:Program FilesMSBuildSedodream 结束。

  声明目录结构之后,即可开始创建 Component 声明。前面提到过,可以安装的最小单位是组件,组件可以由很多不同项组成,这些项包括文件、快捷方式、注册表项和证书。请设计您的组件,使它们相互独立。就是说,安装或卸载组件时,不应当对其他组件有任何负面影响。实际上,这意味着这些项应当仅包含在单个组件中。如果不是这种情况,则可能要重新考虑如何组织组件。组件只包含单个文件是很平常的。

  每个组件声明必须有一个 Id 和一个 GUID 属性。Id 是用于引用组件的名称,GUID 是 Windows Installer 要使用的唯一标识符。非常重要的一点是,要确保所有组件的 GUID 都不重复。

  图 2 显示了示例 WiX 源文件中的组件。可以看到它包含四个 File 元素和两个 XmlFile 元素。File 元素只是对需要放在目标设备中的文件的引用。File 元素可以有很多不同属性,但您使用的最多的是 Id、Name、LongName、Source 和 DiskId,如图 3 所示。

Figure3文件属性

 

属性名称 说明
Id 可用于引用文件的标识符。
Name 8.3 格式的文件短名称。
LongName 文件的长名称;这是将用在目标计算机上的名称(如果该计算机支持长文件名)。否则使用 Name 值。
Source 在要创建安装程序数据库的计算机上的文件的相对路径。
DiskId 将包含此文件的介质项的标识符。

Figure2定义组件

<Component Id='TaskBinFiles' Guid='38736E2E-BEB7-48A9-A2B3-138A57A69D45'>
  <File Id='MSBCommonDLL' Name='CommoDLL'
   LongName='Sedodream.MSBuild.Common.dll'
   Source='Sedodream.MSBuild.Common.dll' Vital='yes' DiskId='1'/>
  <File Id='SedodreamLoggersDLL' Name='Logger'
   LongName='Sedodream.MSBuild.Loggers.dll'
   Source='Sedodream.MSBuild.Loggers.dll' Vital='yes' DiskId='1'/>
  <File Id='SedodreamTasksDLL' Name='Tasks'
   LongName='Sedodream.MSBuild.Tasks.dll'
   Source='Sedodream.MSBuild.Tasks.dll' Vital='yes' DiskId='1'/>
 
  <File Id='SedodreamTASKS' Name='SeTasks' LongName='Sedodream.tasks'
   Source='Sedodream.tasks' Vital='yes' DiskId='1'/>
  <!-- Merge in the Xsd for the custom tasks we have created to have
   IntelliSense -->
  <XmlFile Id='SedoSche'
   File='C:Program FilesMicrosoft Visual Studio 8
        XmlSchemas1033Microsoft.Build.xsd'
   Action='createElement' ElementPath='//xs:schema'
   Name='xs:include' Permanent='no' Sequence='0'/>
  <XmlFile Id='SedoSch2'
   File='C:Program FilesMicrosoft Visual Studio 8
        XmlSchemas1033Microsoft.Build.xsd'
   Action='setValue'
   ElementPath='//xs:schema/xs:include[[]not(@schemaLocation)[]]'
   Name='schemaLocation' Value='MSBuildSedodream.MSBuild.xsd'
   Permanent='no' Sequence='1' />
</Component>

  XmlFile 元素实际上用于修改目标计算机上的现有 XML 文件。它将新元素插入 Microsoft.Build.xsd 文件中,该文件允许对包含在示例项目中的自定义 MSBuild 任务启用 IntelliSense。

  Feature 是在安装程序显示 UI 的情况下用户要选择安装的项。下面是示例项目中的 Feature 声明:

<Feature Id='Complete'
  Title='Sedodrem Core' Description='MSBuild libraries'
  Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR'>
  <ComponentRef Id='TaskBinFiles'/>
  <ComponentRef Id='NUnitFiles'/>
  <Feature Id='Samples' Title='Samples'
   Description='Contains samples of Sedodream usage'
   Display='expand' Level='100' ConfigurableDirectory='INSTALLDIR'>
   <ComponentRef Id='Samples'/>
  </Feature>
</Feature>

  像其他很多 WiX 元素一样,Feature 元素有很多可能的属性,图 4 描述了一些较常用的属性。

Figure4功能属性

 

属性名称 说明
Id 标识符,用于引用功能。
ConfigurableDirectory 允许您从属性设置安装位置,可以由用户通过 UI 确定该位置。
Description 对功能的说明,它将通过 UI 向用户显示。
Level 指定功能的安装级别。值为零意味着将不安装该功能。小于或等于 INSTALLLEVEL 的非零值意味着安装该功能。
Title 功能的标题,如果使用 UI,其值将显示在功能树中。

  在此 WiX 片段中,主要的功能“Complete”包含对两个组件和一个子功能的引用。子功能引用通过子 Feature 元素实现。WiX 源文件可以包含任何数目的这些所需项。组件引用使用 ComponentRef 元素。使用 ComponentRef 元素时,所使用的 ID 值必须与在尝试引用的 Component 元素中所使用的该值相同。

  WiX 和 MSBuild

  在最新发布的 WiX v2 中,您将发现 wix.targets 文件,该文件包含的定义将帮助您从 MSBuild 创建 WiX 发布。WiX 团队已创建了一组被 wix.targets 文件引用的 MSBuild 任务。这些任务全部包含在 WiXTasks.dll 程序集中,如图 5 所示。

Figure5WiX 的 MSBuild 任务

 

名称 说明
Candle 通过调用 WiX 编译器创建 WiX 源文件的中间表示形式。
Lit 从中间表示形式创建 WiX 库。
Light 通过调用 WiX 连接器,从中间表示形式创建最终的安装程序。

  您生成安装程序的方式非常类似于现在生成托管项目的方式。例如,创建 C# 项目时,项目文件将包含生成项目所需的所有属性和项,但生成项目的所有步骤则包含在 Microsoft.CSharp.targets 文件中。此文件将通过 MSBuild Import 元素包含到项目文件中。对于 WiX,项目文件将定义所有需要的属性和项,然后包含 wix.targets 文件以定义如何生成最终包的逻辑。图 6 提供了使用 wix.targets 成功生成安装程序所需的最低声明的列表。

Figure6生成安装程序所需的声明

 

名称 说明
ToolPath 包含 WiX 安装目录的路径。这是 wix.targets 文件的驻留位置。
OutputName 将创建的输出的路径和名称。该名称不应包含任何扩展名,因为中间表示形式和最后的包文件都将使用它。此属性类似于 Candle.exe 和 Light.exe 的 OutputFile 参数。
OutputType 表示最后的文件应当是什么。可能的值是包、模块、库和对象。它将确定要调用 Light 还是 Lit 任务,以及所创建的最终文件的扩展名。由于我们要创建安装程序,因此我们将为此属性提供值 package。
Compile 包含要编译的 WiX 源文件的 MSBuild 项。
BaseInputPath 作为源文件的输入路径。尽管该技术不是必需的,但在大多数情况下应使用它。否则,您必须确保从正确的目录调用 msbuild.exe。

  若要创建安装程序,需要创建 MSBuild 文件,以定义必需的属性和项。在这里,我将该文件命名为 SedodreamMSI.wproj。图 7 中显示了此文件。

Figure7MSBuild 文件 SedodreamsMSI.wproj

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
 <!--===============================================================
  These must be declared BEFORE the statement
  that imports the wix.targets file
  ==================================================================-->
 <PropertyGroup>
  <!-- The location pointing where WiX is installed -->
  <ToolPath>C:DataDevelopmentWiX</ToolPath>
  <!-- Required Property by WiX -->
  <OutputName Condition="$(OutputName)==''" >
   $(Configuration)Sedodream</OutputName>
  <!-- Required property by WiX -->
  <OutputType
   Condition="$(OutputType)==''" >package</OutputType>
  <!-- Input path to source files -->
  <BaseInputPath Condition="$(BaseInputPath)==''">
   $(PackageRoot)$(Configuration)</BaseInputPath>
 </PropertyGroup>
 <ItemGroup>
  <!-- Required WiX item.
   Files in this item are sent to the Candle tool.
  -->
  <Compile Include="$(BaseInputPath)Sedodream.wxs"/>
 </ItemGroup>
 <Import Project="$(ToolPath)wix.targets"/>
</Project>

  可以看到,此 MSBuild 文件中的内容不是很多,只定义了少量属性和一个单项。该文件假定 WiX 源文件 Sedodream.wxs 已创建,并且驻留在 BaseInputPath 目录中。WiX 文件还对文件的存放位置进行了一些假定。现在,我们假定所有文件都在期望的位置。

  为了生成安装程序,我需要对项目文件调用 MSBuild。我打开 Visual Studio 2005 命令提示符,导航到文件所在位置,并执行以下命令:

msbuild Sedodream.wproj

  输出显示在图 8 中。

  由于我没有定义要执行的目标,因此会执行 DefaultTargets,并且它被定义为 Sedodream.wproj 文件中的 Build。从输出,可以看到在 binRelease 文件夹中创建了名为 ReleaseSedodream.msi 的文件。在这里,可以执行安装程序,以确保它按期望工作。在继续实现我们的主要目标之前,我将在下一节讨论高级 MSBuild 主题:批处理。之所以需要这样做,是因为在生成产品及其发布的整个过程中要用到批处理。

  图 8从 MSBuild 实现的 WiX 安装程序创建输出

  MSBuild 批处理

  使用 MSBuild 时,您将发现找不到循环构造。您可以使用批处理来代替循环,批处理使用项的元数据将项划分成不同的类别,这些类别称为批或桶,它们是由一个或多个项组成的。一旦划分了项,就可以迭代遍历每个批。这种方式在托管项目的整个生成过程中使用。批处理有两个大的类别:任务批处理和目标批处理。在任务批处理中,当您执行任务时,MSBuild 引擎将确定需要创建什么存储桶,并通过这些存储桶执行任务。若要使用任务批处理,则要向任务提供项的元数据。

  若要演示批处理,请想象您面对以下情形。您必须将一组文件从一个位置复制到一个或多个其他位置,并且您有两个选项:可以使用单个 Copy 任务将文件复制到每个位置,也可以使用批处理在一个复制元素中实现该操作。第一个方案尽管简单,但不是个好的解决方案,因为它难以维护。为了说明第二个方案,请参考下面这个非常简单的 MSBuild 文件 batching01.proj:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
     DefaultTargets="CopyFiles">
 <ItemGroup>
  <SourceFiles Include="*.txt"/>
  <Dest Include="One;Two;Three;Four;Five"/>
 </ItemGroup>
 <Target Name="CopyFiles">
  <Copy SourceFiles ="@(SourceFiles)"
     DestinationFolder="%(Dest.FullPath)"/>
  <Message Text="Fullpath: %(Dest.FullPath)"/>
 </Target>
</Project>

  在此文件中,我声明了一个 SourceFiles 项,包括以 .txt 结尾的任何文件,然后我还声明了一个 Dest 项,包括五个位置。在目标 CopyFiles 中,我使用 Copy 任务将 SourceFiles 复制到每个目标位置。由于我将 FullPath 元数据传递到 Copy 任务中,因此 MSBuild 引擎将创建一个由所有具有不同 FullPath 值的 Dest 项组成的批,然后对每个批调用 Copy 任务。这就是任务批处理。为了执行它,我可以从包含 batching01.proj 文件的目录调用以下命令:

msbuild.exe batching01.proj

  现在,我将用目标批处理演示相同结果。对于目标批处理,批是基于目标的 Inputs 和 Outputs 创建的,因此我必须为这些值传递项的元数据:

<Project xmlns="http://schemas.
 microsoft.com/developer/msbuild/2003"
     DefaultTargets="CopyFiles">
 <ItemGroup>
  <SourceFiles Include="*.txt"/>
  <Dest Include="One;Two;Three;Four;
          Five"/>
 </ItemGroup>
 <!-- These targets demonstrate target
    batching -->
 <Target Name="CopyFiles"
     Inputs="@(SourceFiles)"
     Outputs="%(Dest.FullPath)">
  <Copy SourceFiles="@(SourceFiles)"
     DestinationFolder=
     "%(Dest.FullPath)"/>
 </Target>
</Project>

  目标批处理是以目标 Inputs 和 Outputs 驱动的。由于 Outputs 包含 Dest.FullPath 声明,因此每存储桶将调用目标一次。这类似于任务批处理,不同的是整个目标的内容将每批重复一次,如图 9 所示。

  图 9目标批处理的 MSBuild 输出

  从此输出中,可以看到对于 Dest 项,每批调用一次目标 CopyFiles。在自动执行生成/打包过程的示例中,我将使用批处理为配置平台与风格的每个组合创建安装程序。

  自动执行生成和打包

  对于每个组织来说,有时由于所使用的技术,有时由于组织要求,使得生成和打包所涉及的步骤略微不同。我将一组我认为可以很好地适合很多组织的步骤放在一起,虽然可能需要根据特定情形对它进行一些微调。自动的生成和打包过程的目标是双重的:创建可重复的公用生成过程,以及创建自动的可重复打包过程。有三个核心步骤:更新、生成和打包,但这又细分成很多更小的步骤,如图 10 所示。

  图 10生成过程

  一个非常重要的步骤是标记源文件。之所以要这样做,是因为如果没有此步骤就不能重复该过程。创建一个将发送用于生产的生成版本时,需要能够重新创建完全相同的生成版本,以便维护所部署的代码。根据用作源代码管理提供程序的工具,此步骤将有所不同。针对各种不同的源代码管理提供程序,可以下载很多不同的 MSBuild 任务来实现此目的。有关详细信息,请参见侧栏“MSBuild 和 WiX 资源”。

  分析 MSBuild 脚本

  在这一节中,我将介绍可以用于生成和打包示例应用程序的 MSBuild 脚本。总体结构非常类似于托管项目的生成方式。就是说,您将定义一个项目文件,在其中包含应当处理的各项,然后导入另一个用于定义过程流的项目文件 Sedodream.Package.targets。请将这些过程视为句子。打包项目文件用于定义名词,导入的文件用于定义谓词。我将通过对 Solution 文件使用 MSBuild 任务来生成项目。若要生成安装程序,将使用前面的项目文件 SedodreamMSI.wproj。

  如果您有一个过程具有多个分离元素,那么需要将它们以某种方式聚合在一起。可以通过定义一组公用的属性、项和目标来完成该操作(参见图 11)。

Figure11用于连接元素的属性和项

 

名称 说明
SolutionFilePath 用于定义将用来生成产品的解决方案文件的位置的属性。
PackageRoot 用于定义在哪里生成项目并作为 WiX 的临时生成位置的属性。
WiXSourceFiles 包含要发送到 Candle 工具进行编译的所有 WiX 文件的项。
AllConfigurations 包含生成此过程要针对的所有配置的项。每个配置都应当包含 FlavorToBuild 和 PlatformToBuild 的元数据值。示例声明是:<AllConfigurations Include="Release|Any CPU"> <FlavorToBuild>Release</FlavorToBuild> <PlatformToBuild>Any CPU</PlatformToBuild> </AllConfigurations>
OtherFiles 可选项,其中包含应当复制到包目录中的其他文件。这些项应当包含 Destination 元数据。此元数据定义了目标相对于包的根目录的相对路径。这是示例声明:<OtherFiles Include= "..Sedodream.MSBuild.TasksSampleTargets***"> <Destination>Samples</Destination> </OtherFiles>

  在此过程中,将生成解决方案,并且会将 WiX 源文件复制到相同目录中。您应当定义您的 WiX 源文件,并记住此路径。不管 WiX 文件包含于源代码管理中的什么位置,都将从生成产品的相同目录中生成这些文件。其他必需文件也将由 CopyFilesForPackaging 目标复制到此目录。

  在此过程中,PackageRoot 属性还将定义在哪里生成项目以及 WiX 将在哪里生成安装程序。在示例中,目录处于与解决方案文件相同的级别,但它可以是生成计算机上的任何位置(虽然我建议此位置不要使用网络共享)。

  图 12 显示了 Sedodream.Package.targets 文件中声明的主要目标。所有这些目标(以及目标文件中的其他很多目标)都有其在属性中定义的依赖项,因此您可以在此过程中完全更改事件的顺序。例如,如果需要在 Build 目标执行项目文件之前注入步骤,则可以在 Sedodream.Package.targets 文件的 Import 语句之后直接插入以下片段:

<BuildDependsOn>
  CustomBeforeBuild;
  $(BuildDependsOn);
</BuildDependsOn>

Figure12在示例项目中生成目标

 

名称 说明
Package 只调用 DefaultTargets 列表中的目标以执行整个过程。
Build 生成解决方案文件。在调用 MSBuild 任务时,通过重写 OutputPath 属性,将把此生成过程的输出发送到正确的目录。
CopyFilesForPackaging 将所有未生成但需要用到的文件复制到生成目录。
DeployPackage 接近过程结束时调用。在示例中此目标为空,但在实现时可以决定重写该目标,以便将包发送到 QA 环节或部署团队。
Clean 清理其他目标造成的混乱。

  由于已重新定义了 BuildDependsOn 属性,因此可以有效地将 CustomBeforeBuild 步骤注入到当前过程中,而不用修改现有目标文件。若要了解对这个问题的更详细讨论,应参阅我在 2006 年 6 月的文章(前面已引用)。

  在创建安装程序之前,让我们快速看一下生成解决方案的 CoreBuild 目标(参见图 13)。它将演示如何针对每个已定义的配置,使用目标批处理来生成解决方案。

Figure13CoreBuild 目标

<Target Name="CoreBuild"
  Inputs="%(AllConfigurations.PlatformToBuild);
      %(AllConfigurations.FlavorToBuild)"
  Outputs="%(AllConfigurations.PlatformToBuild);
      %(AllConfigurations.FlavorToBuild)" >
  <Message Text="Building for Flavor/Platform:
    %(AllConfigurations.FlavorToBuild)/
    %(AllConfigurations.PlatformToBuild)"/>
  <MSBuild Projects="@(SolutionFile)"
   Targets="Build"
   Properties="OutputPath=$(PackageRoot)
     %(AllConfigurations.FlavorToBuild);
   Configuration=%(AllConfigurations.FlavorToBuild);
   Platform=%(AllConfigurations.PlatformToBuild)">
   <Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
  </MSBuild>
</Target>

  具体来说,首先要注意的是 Inputs 和 Outputs;它们是 AllConfigurations 项的元数据值,该项将使目标按每个平台/风格配对分别执行一次。然后,调用 MSBuild 任务以生成具有所有相应属性的解决方案文件。之所以提供 OutputPath,是因为我要将输出重定向到另一个目录。

  综述

  至此,我已讨论了如何使用 WiX 创建安装程序、某些高级 MSBuild 概念以及 MSBuild 打包脚本的结构。对于为示例项目创建安装程序,剩下的唯一问题是创建 MSBuild 项目文件,以定义必需属性。这非常的简单。最后,我将介绍如何根据具体需要自定义该过程。

  图 14 显示了 MSBuild 文件 Sedodream.Package.dproj,它将驱动示例项目的该过程。前面讨论过,此项目文件的主要目的是描述需要生成什么。导入的文件 Sedodream.Package.targets 知道如何创建最后产品的所有细节,还可以通过向生成过程注入步骤来自定义该过程。

Figure14Sedodream.Package.dproj MSBuild 项目

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
     DefaultTargets="Package">
  <!-- Required properties by the deployment.targets file -->
  <PropertyGroup>
   <SolutionFilePath>..Sedodream.MSBuild.sln</SolutionFilePath>
   <PackageRoot>..Package</PackageRoot>
  </PropertyGroup>
  <ItemGroup>
   <WiXSourceFiles Include="Sedodream.wxs"/>
   <OtherFiles Include="..Sedodream.MSBuild.Tasks
     SampleTargets***">
     <Destination>Samples</Destination>
   </OtherFiles>
   <!-- Copy the license to the BaseSearchPath so it can
     be included into the installer -->
   <OtherFiles Include="License.rtf">
     <Destination></Destination>
   </OtherFiles>
   <!-- Copy the custom installer bitmaps -->
   <OtherFiles Include="bitmapsdlgbmp.bmp">
     <Destination>bitmaps</Destination>
   </OtherFiles>
  </ItemGroup>
  <!-- Define all the configurations that you want to build here -->
  <ItemGroup>
   <AllConfigurations Include="Debug|x86">
     <FlavorToBuild>Debug</FlavorToBuild>
     <PlatformToBuild>Any CPU</PlatformToBuild>
   </AllConfigurations>
   <AllConfigurations Include="Release|Any CPU">
     <FlavorToBuild>Release</FlavorToBuild>
     <PlatformToBuild>Any CPU</PlatformToBuild>
   </AllConfigurations>
  </ItemGroup>
  <PropertyGroup>
   <DropLocation>C:DataDropsMSBuild-Wix</DropLocation>
  </PropertyGroup>
 
  <!-- Import the deployment target to do all the work for us -->
  <Import Project="Sedodream.Package.targets"/>
</Project>

  在此项目文件中,可以看到已定义的必需的 SolutionFilePath 属性。这是将用于生成产品的解决方案文件的路径。而且,可以看到 WixSourceFiles 项的声明,其中包括将用于编译安装程序的文件集合。它们是将发送给 Candle.exe 工具进行编译的文件。

  同时还提供了 OtherFiles 项的声明,其中包含那些应当复制到将用于创建安装程序的位置的文件。这些文件将复制到用于创建安装程序的 BaseInputPath 中。通过使用 Destination 元数据值,可以将它们复制到 BaseInputPath 下面的任何位置。在此示例中,我会将用于生成安装程序的自定义位图复制到位图文件夹中。当调用 Light.exe 工具来创建 MSI 文件时,它将在选取位于本地目录中的任何默认文件之前,先选取在此目录中的文件。

  注意 DropLocation 属性的声明。通过声明此属性,在 PackageRoot 中的所有文件都将复制到 DropLocation 中的目录内。当需要快速进行某些修改时,将二进制文件和安装程序文件放在同一个位置将大有帮助。

  若要看到为示例项目执行的生成文件和包,请打开 Visual Studio 2005 命令提示符,并导航到项目部署文件夹。然后执行以下命令:

msbuild SedodreamPackage.dproj

  命令提示符下会生成很多输出,您将在 Package 目录下发现两个目录:Debug 和 Release。这两个目录都将包含所生成的安装程序及其关联文件。

  自定义过程

  为产品设置此过程时,将需要创建类似于 SedodreamPackage.dproj 文件的文件。可以将自定义内容直接放在该文件内。应当不需要更改 Sedodream.Package.targets 文件本身。如果要注入步骤,可以执行先前描述的过程。如果要重新定义目标,可以通过在 import 语句之后重新声明目标,直接重写它们。例如,Sedodream.Package.targets 文件定义了目标 GetLatest,但它为空。在此目标中,应当放入必要的任务,以便从存储库获得最新的源文件。下面是它在项目中的大致模样:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Package">
  <!-- Your properties and items defined here -->
  <!-- Import the deployment target to do all the work for us -->
  <Import Project="Sedodream.Package.targets"/>
  <!-- Place overriding customizations after this point -->
 
  <Target Name="GetLatest">
   <Message Text="Getting latest sources"/>
   <!-- Insert tasks to get latest from source control -->
  </Target>
</Project>

  图 15 提供了在 Sedodream.Package.targets 文件中定义的重要目标的列表。可以自定义这些目标,以适合具体需要。

  剩下的主要问题是如何自动执行此过程,现在这一问题非常简单。您唯一要做的是对部署文件自动执行 MSBuild.exe,在这里,该文件是 SedodreamPackage.dproj。如何完成该操作将取决于当前如何生成公用生成文件。如果正在使用 Visual Studio Team Foundation Server,则可以使用 Team Build 来执行此操作。可以使用 TFSBuild.proj 文件中的 MSBuild 任务。如果使用其他某些技术,则很可能它们对 MSBuild 有本机支持。如果不借用任何一种上述技术,可以使用 Windows Scheduler 定时执行此操作。

  总结

  在本文中,我介绍了如何基于 WiX 工具集创建自动的生成文件和打包过程。一旦完成了此集成,就可以按可靠和可重复的方式生成和打包产品。这对即将发送到部署位置的应用程序是非常重要的。

  如果 WiX 不是您的安装程序技术,仍然可以在进行某些修改后使用其理念和文件。所有我知道的安装程序技术都支持某种形式的命令行执行方式。您可以借用此方式,以使用这些其他技术来创建您的安装程序。另外随 MSBuild 任务和目标一同提供了某些第三方安装程序技术,它们可帮助您重新创建此过程。

你可能感兴趣的:(windows)