使用打包布局创建包Package creation with the packaging layout

随着资产包的引入,开发人员现在有了构建更多包和更多包类型的工具。With the introduction of asset packages, developers now have the tools to build more packages in addition to more package types. 当应用变得越来越大、越来越复杂时,通常会包含更多的包,管理这些包的难度也会增加(特别是在 Visual Studio 以外构建和使用映射文件的情况下)。As an app gets larger and more complex, it will often be comprised of more packages, and the difficulty of managing these packages will increase (especially if you are building outside of Visual Studio and using mapping files). 为了简化应用包结构的管理,你可以使用 MakeAppx.exe 支持的包布局。To simplify the management of an app’s packaging structure, you can use the packaging layout supported by MakeAppx.exe.

包布局是一个描述应用打包结构的 XML 文档。The packaging layout is a single XML document that describes packaging structure of the app. 它指定应用中的捆绑(主要和可选)、捆绑中的包以及包中的文件。It specifies the bundles of an app (primary and optional), the packages in the bundles, and the files in the packages. 可以从不同的文件夹、驱动器和网络位置选择文件。Files can be selected from different folders, drives, and network locations. 可以使用通配符选择或排除文件。Wildcards can be used to select or exclude files.

设置好应用的包布局后,其与 MakeAppx.exe 配合使用,在单个命令行调用中为应用创建所有包。After the packaging layout for an app has been set up, it's used with MakeAppx.exe to create all of the packages for an app in a single command line call. 可以编辑包布局以改变包结构,从而契合你的部署需求。The packaging layout can be edited to alter the package structure to fit your deployment needs.

简单包布局示例Simple packaging layout example

下面是一个简单的包布局示例:Here's an example of what a simple packaging layout looks like:

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

我们一起来分析这个示例,以了解其工作原理。Let's break this example down to understand how it works.

PackageFamilyPackageFamily

此打包布局将创建包含 x64 体系结构包和 "媒体" 资产包的单个平面应用包文件。This packaging layout will create a single flat app bundle file with an x64 architecture package and a “Media” asset package.

PackageFamily 元素用于定义应用程序包。The PackageFamily element is used to define an app bundle. 你必须使用 ManifestPath 属性为该捆绑包提供一个 AppxManifestAppxManifest 应该与该捆绑包的体系结构包的 AppxManifest 相对应。You must use the ManifestPath attribute to provide an AppxManifest for the bundle, the AppxManifest should correspond to the AppxManifest for the architecture package of the bundle. 此外,还必须提供 ID 属性。The ID attribute must also be provided. ID 在包创建过程中与 MakeAppx.exe 配合使用,因此你可以根据需要创建该包,并且它将用作生成的包的文件名。This is used with MakeAppx.exe during package creation so that you can create just this package if you want to, and this will be the file name of the resulting package. FlatBundle 属性用于描述要创建的捆绑包的类型,对于平面捆绑包(本文详细介绍的包类型),该属性为 true;对于经典捆绑包,该属性为 falseThe FlatBundle attribute is used to describe what type of bundle you want to create, true for a flat bundle (which you can read more about here), and false for a classic bundle. ResourceManager 属性用于指定该捆绑包中的资源包是否将使用 MRT 来访问文件。The ResourceManager attribute is used to specify if the resource packages within this bundle will use MRT in order to access the files. 该属性默认为 true,但截止 Windows 10 版本 1803,该功能还没准备好,因此必须将其设置为 falseThis is by default true, but as of Windows 10, version 1803, this is not yet ready, so this attribute must be set to false.

Package 和 AssetPackagePackage and AssetPackage

PackageFamily 中,定义了应用程序包包含或引用的包。Within the PackageFamily, the packages that the app bundle contains or references are defined. 这里,体系结构程序包(也称为主要包)使用 Package 元素定义,资产包使用 AssetPackage 元素定义。Here, the architecture package (also called the main package) is defined with the Package element, and the asset package is defined with the AssetPackage element. 体系结构程序包必须指定包适用的体系结构:“x64”、“x86”、“arm”或“neutral”。The architecture package must specify which architecture the package is for, either “x64”, “x86”, “arm”, or “neutral”. 你还可以(可选)使用 ManifestPath 属性直接为该包提供专门的 AppxManifestYou can also (optionally) directly provide an AppxManifest specifically for this package by using the ManifestPath attribute again. 如果没有提供 AppxManifest,则从为 PackageFamily 提供的 AppxManifest 自动生成一个。If an AppxManifest is not provided, one will be automatically generated from the AppxManifest provided for the PackageFamily.

默认情况下,将为捆绑包中的每一个包生成 AppxManifestBy default and AppxManifest will be generated for every package within the bundle. 对于资产包,你还可以设置 AllowExecution 属性。For the asset package, you can also set the AllowExecution attribute. 将该属性设置为 false(默认值)有助于缩短应用发布时间,因为病毒扫描程序不会阻止不需要执行的包的发布过程(有关详细信息,请参阅资产包简介)。Setting this to false (the default), will help decrease the publishing time for your app since packages that don’t need to execute won’t have their virus scan block the publishing process (you can learn more about this at Introduction to asset packages).

文件Files

在每个包定义中,可以使用 File 元素选择要包含在该包中的文件。Within each package definition, you can use the File element to select files to be included in this package. SourcePath 属性是文件在本地的位置。The SourcePath attribute is where the files are locally. 你可以从不同的文件夹(通过提供相对路径)、不同的驱动器(通过提供绝对路径)、甚至网络共享(通过提供类似于 \\myshare\myapp\* 的路径)选择文件。You can select files from different folders (by providing relative paths), different drives (by providing absolute paths), or even network shares (by providing something like \\myshare\myapp\*). DestinationPath 是文件在包中的最终位置(相对于包根目录而言)。The DestinationPath is where the files will end up within the package, relative to the package root. 可以使用 ExcludePath(而不是其他两个属性)选择要从同一个包中其他 File 元素的 SourcePath 属性选择的文件中排除的文件。ExcludePath can be used (instead of the other two attributes) to select files to be excluded from the ones selected by other File elements’ SourcePath attributes within the same package.

每个 File 元素都可以使用通配符来选择多个文件。Each File element can be used to select multiple files by using wildcards. 通常,可以在路径中的任意位置使用单通配符 (*) 任意次。In general, single wildcard (*) can be used anywhere within the path any number of times. 但是,单通配符将只匹配文件夹中的文件,不匹配任何子文件夹。However, a single wildcard will only match the files within a folder and not any subfolders. 例如,可以在 SourcePath 中使用 C:\MyGame\*\* 选择文件 C:\MyGame\Audios\UI.mp3C:\MyGame\Videos\intro.mp4,但它无法选择 C:\MyGame\Audios\Level1\warp.mp3For example, C:\MyGame\*\* can be used in the SourcePath to select the files C:\MyGame\Audios\UI.mp3 and C:\MyGame\Videos\intro.mp4, but it cannot select C:\MyGame\Audios\Level1\warp.mp3. 也可以使用双通配符 (**) 代替文件夹或文件名来递归匹配任何内容(但它不能与部分名称连用)。The double wildcard (**) can also be used in place of folder or file names to match anything recursively (but it cannot be next to partial names). 例如,C:\MyGame\**\Level1\** 可以选择 C:\MyGame\Audios\Level1\warp.mp3C:\MyGame\Videos\Bonus\Level1\DLC1\intro.mp4For example, C:\MyGame\**\Level1\** can select C:\MyGame\Audios\Level1\warp.mp3 and C:\MyGame\Videos\Bonus\Level1\DLC1\intro.mp4. 在打包过程中,也可以使用通配符直接更改文件名 - 即在源和目标之间的不同位置使用通配符。Wildcards can also be used to directly change file names as part of the packaging process if the wildcards are used in different places between the source and destination. 例如,为 SourcePath 指定 C:\MyGame\Audios\* 和为 DestinationPath 指定 Sound\copy_* 可以选择 C:\MyGame\Audios\UI.mp3 并使其在包中显示为 Sound\copy_UI.mp3For example, having C:\MyGame\Audios\* for SourcePath and Sound\copy_* for DestinationPath can select C:\MyGame\Audios\UI.mp3 and have it appear in the package as Sound\copy_UI.mp3. 一般情况下,对于单一 File 元素的 SourcePathDestinationPath,单通配符和双通配符的数量必须相同。In general, the number of single wildcards and double wildcards must be the same for the SourcePath and DestinationPath of a single File element.

高级包布局示例Advanced packaging layout example

下面是一个更复杂的包布局示例:Here's an example of a more complicated packaging layout:

<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 元素。This example differs from the simple example with the addition of ResourcePackage and Optional elements.

可以使用 ResourcePackage 元素指定资源包。Resource packages can be specified with the ResourcePackage element. ResourcePackage 中,必须使用 Resources 元素指定资源包的资源限定符。Within the ResourcePackage, the Resources element must be used to specify the resource qualifiers of the resource pack. 资源限定符是资源包支持的资源,在本例中,我们可以看到定义了两个资源包,每个资源包都包含英语和法语特定文件。The resource qualifiers are the resources that are supported by the resource pack, here, we can see that there are two resource packs defined and they each contain the English and French specific files. 资源包可以有多个限定符,这可通过在 Resources 中添加其他 Resource 元素实现。A resource pack can have more than one qualifier, this can be done by adding another Resource element within Resources. 如果存在维度(维度为语言、比例、dxfl),则还必须为资源维度指定默认资源。A default resource for the resource dimension must also be specified if the dimension exists (dimensions being language, scale, dxfl). 在本例中,我们可以看到默认语言为英语,这意味着对于没有设置法语系统语言的用户,他们将回退下载英语资源包并以英语显示。Here, we can see that English is the default language, meaning that for users that does not have a system language of French set, they will fallback to downloading the English resource pack and display in English.

每个可选包都有自己独特的包系列名称,必须使用 PackageFamily 元素进行定义,同时将 Optional 属性指定为 trueOptional packages each have their own distinct package family names and must be defined with PackageFamily elements, while specifying the Optional attribute to be true. RelatedSet 属性用于指定可选包是否在相关集中(默认为 true)- 可选包是否应随主包一起更新。The RelatedSet attribute is used to specify whether the optional package is within the related set (by default this is true) – whether the optional package should be updated with the primary package.

PrebuiltPackage元素用于将打包布局中未定义的包添加到要在生成的应用包文件中包含或引用的包。The PrebuiltPackage element is used to add packages that are not defined in the packaging layout to be included or referenced in the app bundle file(s) to be built. 在这种情况下,将在此处包含另一个 DLC 可选包,以便主要应用程序包文件可以引用它并将其作为相关集的一部分。In this case, another DLC optional package is being included here so that the primary app bundle file can reference it and have it be part of the related set.

使用包布局和 MakeAppx.exe 构建应用包Build app packages with a packaging layout and MakeAppx.exe

为应用准备好包布局后,就可以开始使用 MakeAppx.exe 构建应用的包了。Once you have the packaging layout for your app, you can start using MakeAppx.exe to build the packages of your app. 要构建包布局中定义的所有包,请使用以下命令:To build all of the packages defined in the packaging layout, use the command:

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

但是,如果你正在更新应用,而某些包的文件没有任何更改,则你可以只构建发生更改的包。But, if you are updating your app and some packages don't contain any changed files, you can build only the packages that have changed. 以本页面上的简单包布局示例为例,要构建 x64 体系结构程序包,我们可以输入以下命令:Using the simple packaging layout example on this page and building the x64 architecture package, this is what our command would look like:

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

/id 标志可用于从包布局中选择要构建的包,它与布局中的 ID 属性相对应。The /id flag can be used to select the packages to be built from the packaging layout, corresponding to the ID attribute in the layout. 在本例中,/ip 用于指示包的以前版本所在的位置。The /ip is used to indicate where the previous version of the packages are in this case. 必须提供以前的版本,因为应用捆绑包文件仍需要引用以前版本的媒体包。The previous version must be provided because the app bundle file still needs to reference the previous version of the Media package. /iv 标志用于自动递增要构建的包的版本(而不是在 AppxManifest 中更改版本)。The /iv flag is used to automatically increment the version of the packages being built (instead of changing the version in the AppxManifest). 或者,也可以使用 /pv/bv 开关分别直接提供包版本(用于要创建的所有包)和捆绑包版本(用于要创建的所有捆绑包)。Alternatively, the switches /pv and /bv can be used to directly provide a package version (for all packages to be created) and a bundle version (for all bundles to be created), respectively. 以本页面上的高级包布局示例为例,要只构建 Themes 可选捆绑包和它引用的 Themes.main 应用包,可以使用以下命令:Using the advanced packaging layout example on this page, if you want to only build the Themes optional bundle and the Themes.main app package that it references, you would use this command:

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

/bc 标志用于表示还应该构建 Themes 捆绑包的子项(在本例中,这将构建 Themes.main)。The /bc flag is used to denote that the children of the Themes bundle should also be built (in this case Themes.main will be built). /nbp 标志用于表示不应该构建 Themes 捆绑包的父项。The /nbp flag is used to denote that the parent of the Themes bundle should not be built. Themes 的父项是主应用程序包 MyGame,这是一个可选应用程序包。The parent of Themes, which is an optional app bundle, is the primary app bundle: MyGame. 通常,对于相关集中的可选包,还必须构建主应用程序包以便能够安装可选包,因为当可选包位于相关集中时,主应用程序包也会引用它(以确保在主包和可选包之间实施版本控制)。Usually for an optional package in a related set, the primary app bundle must also be built for the optional package to be installable, since the optional package is also referenced in the primary app bundle when it is in a related set (to guarantee versioning between the primary and the optional packages). 下图说明了包之间的父子关系:The parent child relationship between packages is illustrated in the following diagram:

包布局图