使用打包布局创建包

随着资产包的引入,开发人员现在除了拥有更多的包类型外,还拥有生成更多包的工具。 随着应用变得越来越大、越来越复杂,它通常由更多包组成,管理这些包的难度也将增加(尤其是在 Visual Studio 外部生成并使用映射文件时)。 要简化应用打包结构的管理,可以使用 MakeAppx.exe 支持的打包布局。

打包布局是描述应用打包结构的单个 XML 文档。 它指定应用(主要和可选)的捆绑包、捆绑包中的包,以及包中的文件。 可以从不同的文件夹、驱动器和网络位置选择文件。 可以使用通配符来选择或排除文件。

设置应用的打包布局后,它与 MakeAppx.exe 一起使用,以在单个命令行调用中创建应用的所有包。 可以编辑打包布局以更改包结构来满足部署需求。

简单打包布局示例

下面是简单打包布局的示例:

<PackagingLayout xmlns="http://schemas.microsoft.com/appx/makeappx/2017">
  <PackageFamily ID="MyGame" FlatBundle="true" ManifestPath="C:\mygame\appxmanifest.xml" ResourceManager="false">
    
    <!-- x64 code package-->
    <Package ID="x64" ProcessorArchitecture="x64">
      <Files>
        <File DestinationPath="*" SourcePath="C:\mygame\*"/>
        <File ExcludePath="*C:\mygame\*.txt"/>
      </Files>
    </Package>
    
    <!-- Media asset package -->
    <AssetPackage ID="Media" AllowExecution="false">
      <Files>
        <File DestinationPath="Media\**" SourcePath="C:\mygame\media\**"/>
      </Files>
    </AssetPackage>

  </PackageFamily>
</PackagingLayout>

让我们分解此示例以了解其工作原理。

PackageFamily

此打包布局将创建一个包含 x64 体系结构包和“媒体”资产包的平面应用捆绑包文件。

PackageFamily 元素用于定义应用捆绑包。 必须使用 ManifestPath 属性为捆绑包提供 AppxManifestAppxManifest 应与捆绑包体系结构包的 AppxManifest 相对应。 还必须提供 ID 属性。 此项在包创建过程中与 MakeAppx.exe 一起使用,因此,可以根据需要仅创建此包,这将成为生成的包的文件名。 FlatBundle 属性用于描述要创建的捆绑包类型,true 表示平面捆绑包(可以在此处阅读更多信息),false 表示经典捆绑包。 ResourceManager 属性用于指定此捆绑包中的资源包是否将使用 MRT 来访问文件。 此项默认为 true,但截至 Windows 10 版本 1803,此功能尚未准备就绪,因此必须将此属性设置为 false

包和 AssetPackage

PackageFamily 中,定义了应用捆绑包包含或引用的包。 在这里,体系结构包(也称为主包)使用 Package 元素定义,而资产包使用 AssetPackage 元素定义。 体系结构包必须指定包针对的体系结构:"x64"、"x86"、"arm" 或“中立”。 (可选)还可以再次使用 ManifestPath 属性直接提供特定于此包的 AppxManifest。 如果未提供 AppxManifest,则会通过为 PackageFamily 提供的 AppxManifest 自动生成一个 AppxManifest。

默认情况下,将为捆绑包中的每个包生成 AppxManifest。 对于资产包,还可以设置 AllowExecution 属性。 将此项设置为 false(默认值),将有助于缩短应用的发布时间,因为不需要执行的包不会让其病毒扫描阻止发布过程(可以在资产包简介中了解此内容)。

文件

在每个包定义中,可以使用 File 元素选择要包含在此包中的文件。 SourcePath 属性是文件在本地的位置。 你可以从不同的文件夹(通过提供相对路径)、不同的驱动器(通过提供绝对路径),甚至网络共享(通过提供类似 \\myshare\myapp\* 的内容)选择文件。 DestinationPath 是文件最终在包中相对于包根目录的位置。 可以使用 ExcludePath(而不是其他两个属性)来选择要从同一包中其他 File 元素的 SourcePath 属性所选文件中排除的文件。

使用通配符,每个 File 元素都可用于选择多个文件。 通常,单个通配符 (*) 可以在路径内的任何位置使用任意次数。 但是,单个通配符仅匹配文件夹中的文件,而不匹配任何子文件夹。 例如,C:\MyGame\*\* 可以在 SourcePath 中用来选择文件 C:\MyGame\Audios\UI.mp3C:\MyGame\Videos\intro.mp4,但不能选择 C:\MyGame\Audios\Level1\warp.mp3。 双通配符 (**) 也可以用来代替文件夹或文件名,以递归地匹配任何内容(但不能位于部分名称旁边)。 例如,C:\MyGame\**\Level1\** 可以选择 C:\MyGame\Audios\Level1\warp.mp3C:\MyGame\Videos\Bonus\Level1\DLC1\intro.mp4。 如果在源和目标之间的不同位置使用通配符,则作为打包过程的一部分,通配符还可用于直接更改文件名。 例如,对 SourcePath 使用 C:\MyGame\Audios\* 且对 DestinationPath 使用 Sound\copy_*,便可选择 C:\MyGame\Audios\UI.mp3 并使其在包中显示为 Sound\copy_UI.mp3。 通常,对于单个 File 元素的 SourcePathDestinationPath,单通配符和双通配符的数量必须相同。

高级打包布局示例

下面是更复杂的打包布局的示例:

<PackagingLayout xmlns="http://schemas.microsoft.com/appx/makeappx/2017">
  <!-- Main game -->
  <PackageFamily ID="MyGame" FlatBundle="true" ManifestPath="C:\mygame\appxmanifest.xml" ResourceManager="false">
    
    <!-- x64 code package-->
    <Package ID="x64" ProcessorArchitecture="x64">
      <Files>
        <File DestinationPath="*" SourcePath="C:\mygame\*"/>
        <File ExcludePath="*C:\mygame\*.txt"/>
      </Files>
    </Package>

    <!-- Media asset package -->
    <AssetPackage ID="Media" AllowExecution="false">
      <Files>
        <File DestinationPath="Media\**" SourcePath="C:\mygame\media\**"/>
      </Files>
    </AssetPackage>
    
    <!-- English resource package -->
    <ResourcePackage ID="en">
      <Files>
        <File DestinationPath="english\**" SourcePath="C:\mygame\english\**"/>
      </Files>
      <Resources Default="true">
        <Resource Language="en"/>
      </Resources>
    </ResourcePackage>

    <!-- French resource package -->
    <ResourcePackage ID="fr">
      <Files>
        <File DestinationPath="french\**" SourcePath="C:\mygame\french\**"/>
      </Files>
      <Resources>
        <Resource Language="fr"/>
      </Resources>
    </ResourcePackage>
  </PackageFamily>

  <!-- DLC in the related set -->
  <PackageFamily ID="DLC" Optional="true" ManifestPath="C:\DLC\appxmanifest.xml">
    <Package ID="DLC.x86" Architecture="x86">
      <Files>
        <File DestinationPath="**" SourcePath="C:\DLC\**"/>
      </Files>
    </Package>
  </PackageFamily>

  <!-- DLC not part of the related set -->
  <PackageFamily ID="Themes" Optional="true" RelatedSet="false" ManifestPath="C:\themes\appxmanifest.xml">
    <Package ID="Themes.main" Architecture="neutral">
      <Files>
        <File DestinationPath="**" SourcePath="C:\themes\**"/>
      </Files>
    </Package>
  </PackageFamily>

  <!-- Existing packages that need to be included/referenced in the bundle -->
  <PrebuiltPackage Path="C:\prebuilt\DLC2.appxbundle" />

</PackagingLayout>

此示例与添加 ResourcePackageOptional 元素的简单示例不同。

可以使用 ResourcePackage 元素指定资源包。 在 ResourcePackage 中,必须使用 Resources 元素来指定资源包的资源限定符。 资源限定符是资源包支持的资源,此处可以看到定义了两个资源包,每个资源包都包含英语和法语特定文件。 一个资源包可以有多个限定符,这可以通过在 Resources 中添加另一个 Resource 元素来完成。 如果存在资源维度,还必须指定该维度的默认资源(维度包括语言、规模、dxfl)。 在这里,我们可以看到英语是默认语言,这意味着对于没有设置法语系统语言的用户,将回退下载英语资源包并用英语显示。

可选包各自具有其不同的包系列名称,并且必须使用 PackageFamily 元素定义,同时将 Optional 属性指定为 trueRelatedSet 属性用于指定可选包是否在相关集中(默认情况下此项为 true)- 可选包是否应随主包一起更新。

PrebuiltPackage 元素用于添加未在打包布局中定义的包,以供在要生成的应用捆绑包文件中包含或引用。 在这种情况下,此处将包含另一个 DLC 可选包,以使主应用捆绑包文件可以对其进行引用,并将其作为相关集的一部分。

使用打包布局和 MakeAppx.exe 生成应用包

获得应用的打包布局后,即可开始使用 MakeAppx.exe 生成应用的包。 要生成打包布局中定义的所有包,请使用以下命令:

MakeAppx.exe build /f PackagingLayout.xml /op OutputPackages\

但是,如果要更新应用,并且某些包未包含任何更改的文件,则只能生成已更改的包。 使用此页面上的简单打包布局示例并生成 x64 体系结构包,我们的命令如下所示:

MakeAppx.exe build /f PackagingLayout.xml /id "x64" /ip PreviousVersion\ /op OutputPackages\ /iv

/id 标志可用于从打包布局中选择要生成的包,这与布局中的 ID 属性相对应。 在这种情况下,/ip 用于指示以前版本包的位置。 必须提供以前的版本,因为应用捆绑包文件仍需要引用以前版本的 Media 包。 /iv 标志用于自动递增所生成包的版本(而不是更改 AppxManifest 中的版本)。 或者,可以分别使用开关 /pv/bv 直接提供包版本(适用于要创建的所有包)和捆绑包版本(适用于要创建的所有捆绑包)。 使用本页上的高级打包布局示例时,如果只想生成 Themes 可选包及其引用的 Themes.main 应用包,将使用以下命令:

MakeAppx.exe build /f PackagingLayout.xml /id "Themes" /op OutputPackages\ /bc /nbp

/bc 标志用于表示还应生成 Themes 捆绑包的子项(在这种情况下,将生成 Themes.main)。 /nbp 标志用于表示不应生成 Themes 捆绑包的父项。 Themes 的父项是一个可选的应用捆绑包,即主应用捆绑包:MyGame。 通常,对于相关集中的可选包,还必须为要安装的可选包生成主应用捆绑包,因为当可选包位于相关集中时,也会在主应用捆绑包中引用该包(以保证主包和可选包之间的版本控制)。 下图演示了包之间的父子关系:

Packaging Layout Diagram