包必须遵循语义版本控制 (SemVer)。语义版本控制是一种策略,让包创作者能够以一种自动化工具可以使用的格式来提供有关给定版本中与先前版本相比的更改类型的信息。
语义版本控制将版本表示为 MAJOR.MINOR.PATCH,MAJOR 表示引入了一个或多个重大更改,MINOR 表示引入了一个或多个向后兼容的 API 更改,而 PATCH 表示仅引入了错误修正,API 无任何变化。
开始开发包时,版本号从 0.1.0
开始。MAJOR 版本号 0
保留给包的初始开发阶段。在最初的开发过程中,包 API 经常频繁进行重大更改,因此,在您认为包足够稳定并可以在实际生产中使用之前,请将 MAJOR 版本号保持为 0
。
在包正式进入实际生产阶段后,应将 MAJOR 版本递增到 1
,并在后续更改时遵循以下准则:
递增该值: | 在这些条件下: | 示例: |
---|---|---|
MAJOR | 至少有一个重大更改,而该包的任何一个版本都不能替代另一个版本。重大更改包括: • 更改 API 表面(API 的公开部分)或功能,可能导致编译或运行时错误。 • 删除非 API 功能,包括删除资源或更改资源的 GUID。 • 删除或重命名程序集和资源(因为编译器可能无法找到它们)。 注意:将主要版本递增时,请务必将 PATCH 和 MINOR 值重置为 0 。 |
版本 1.2.3 和 2.0.0 不兼容,不能安全地互换使用。 |
MINOR (MAJOR 值相同) |
最高的 MINOR 以向后兼容方式引入功能。向后兼容(或非重大)的 API 更改包括: • 更改 API 表面或功能,但不存在导致编译或运行时错误的风险。 • 添加非 API 功能。 • 添加程序集和资源(因为新项没有预先存在的引用)。 注意:将次要版本递增时,请务必将 PATCH 版本重置为 0 。 |
可以使用 1.3.0 版来实现对 1.2.0 的依赖,因为 1.3.0 向后兼容。 不能使用 1.2.0 来实现对 1.3.0 的依赖。 |
PATCH (MAJOR.MINOR 值相同) |
最高的 PATCH 以向后兼容的方式引入了错误修复,但不更改任何 API。如果满足以下条件,表示 API 没有更改: • API 表面相同,并且功能保持不变。 • 所做的更改不会更改公共 API。 |
版本 1.3.0 和 1.3.1 应该可以互换,因为它们具有相同的 API,即使 1.3.1 包含 1.3.0 中不存在的错误修复。 |
通过遵循这些版本控制做法,Package Manager 可以自动解决冲突(如果可能),或将包升级到更高的向后兼容版本。
以下各部分介绍的一些情况可以帮助您确定这些规则如何影响各种包元素:
除了上述情况之外,某些通常仅需要增加 MINOR 或 PATCH 版本的更改可能还会受到如下因素的影响:启用还是禁用了 Auto Referenced 属性。
您可以为程序集定义设置的其中一个属性是 Auto Referenced,该属性控制 Unity 在编译过程中是否自动引用文件。启用此属性后,某些通常只需要增加 MINOR 或 PATCH 版本的更改现在将成为重大更改。
禁用自动引用时,如果进行的任何更改导致有新的程序集可用,则会引入向后兼容的 API 更改。向后兼容的 API 更改(比如添加平台、禁用 Unity 测试引用、添加新的 .asmdef 或删除定义约束)只需要增加 MINOR 版本。
但在启用自动引用时,新添加的程序集将隐式添加到其他各种程序集的引用中。由于这些情况可能导致这些其他程序集中出现编译错误,因此需要增加 MAJOR 版本。
另一种常见情况是在因为包依赖关系而添加或更改版本时。在大部分情况下,更改包依赖关系仅需要增加 PATCH 版本。但是,新的包版本可能包含启用了 Auto Referenced 属性的程序集,这就会成为一个重大更改,因此需要增加 MAJOR 版本。
为避免此类问题,请始终避免将第三方 DLL 文件放入不相关的包中(例如,在 SaveGameManager 包中包含 Newtonsoft.Json.dll)。
项目可以引用资源数据库可见的任何资源。资源数据库使用在 .meta 文件中定义的 GUID 来唯一跟踪这些资源。
将以下某一个更改引入公共 API 中时,需要新的 MAJOR 版本,因为它们属于重大更改:
情况: | 为何属于重大更改: |
---|---|
删除资源数据库可见的资源 | 如果删除资源,这可能会破坏用户项目中的引用或其他包。 |
更改资源的 GUID | 如果更改资源 GUID,则资源数据库将此做法理解为先删除原始资源再添加新的(相同)资源。这会导致引用被破坏,因为原始 GUID 不再指向该资源,因此资源数据库无法解析该引用。 |
程序集定义 (.asmdef) 定义一组脚本,Unity Editor 的编译管线会将这些脚本转换成单独的托管程序集 (.dll)。这些 .asmdef 资源包含的属性可驱动生成的程序集的属性。这些属性包括:
大多数属性都会影响程序集的使用者,因此更改其中任何一个属性都将更改包的公共 API。其他属性对程序集的使用者没有影响,因此更改其中任何一个都不会被视为更改包 API。
警告:Auto Referenced 属性是一种特殊情况,因为很多通常完全不会更改 API 或以向后兼容方式更改 API 的更改都可能会导致编译错误,具体取决于是否启用了此属性。有关更多信息,请参阅自动引用。
Unity 既可以预编译程序集,也可以通过脚本和程序集定义来编译程序集。因此,适用于程序集定义的所有内容通常也适用于预编译的程序集。
本节详细介绍了程序集定义和预编译程序集的更改,以及对包版本的影响:
在引入公共 API 的重大更改时,这需要新的 MAJOR 版本,因为这可能会导致编译错误和运行时错误。所有这些情况下都会从引用某个程序集的所有其他程序集中删除或隐藏该程序集。如果编译的程序集使用了在引用的程序集中定义的类型,则在编译器找不到另一个程序集的情况下,会导致编译错误。有关使用程序集和程序集定义的更多信息,请参阅程序集定义。
请注意,以下内容适用于包使用的运行时程序集和 Editor 程序集。这些内容不适用于测试程序集,因为包通常不使用它们,所以它们不是包 API 的一部分。
情况: | 为何编译器找不到引用的程序集: |
---|---|
删除程序集定义或预编译的程序集 | 删除程序集定义文件会阻止编译管线生成对应的程序集。 注意:从 2019.1 开始,允许缺失的引用,目的是支持“可选引用”用例,但如果重命名 Unity 在编译程序集定义时所需要的程序集,则会导致编译错误。同样,如果编译后的代码需要程序集中的某个类型,则重命名该程序集可能会导致运行时错误,例如 TypeLoadException 。 |
更改程序集名称(在 .asmdef 文件中更改,或者重命名 .dll 文件) | 更改程序集名称等同于先删除程序集,然后又添加不同名称的新程序集。这意味着虽然 API 仍包含相同的程序集代码,只不过名称不同,但 Unity 也会认为缺少原始程序集。 |
在 .asmdef 中添加定义约束 | 如果添加定义约束,那么只要不满足定义约束,Unity 就会跳过程序集的编译。这将导致程序集丢失的情况,即使程序集以前可用。 |
删除平台 | 如果删除对特定平台的支持,则 Unity 不再在该平台上导入程序集,这相当于删除程序集。 您可以通过启用以下属性之一来删除平台: • includePlatforms,这将破坏与所有未列出平台的兼容性 • excludePlatforms,这会向其中添加条目 |
将公共 API 从一个程序集移动到另一个程序集 | 当您将可公开访问的代码从程序集 A 移到程序集 B 时,任何引用 A 但未引用 B 的程序集都将编译失败。 对于程序集定义,如果移动脚本,则可能会将公共 API 移至其他程序集。 |
更改 Auto Referenced 属性 |
禁用 Auto Referenced 属性时,如果不显式引用,那么您将无法再使用该程序集的公共 API: • 对于预编译的程序集,禁用此属性会阻止 Unity 隐式添加预编译的程序集作为对程序集定义和项目编译的程序集的引用。 • 对于程序集定义,禁用此属性会阻止 Unity 隐式添加生成的程序集作为对项目编译的程序集的引用。 启用 Auto Referenced 属性时,可能会与 API、属性或依赖关系的其他更改产生冲突。有关更多信息,请参阅自动引用部分。 |
在程序集定义中启用 Unity References → Test Assemblies 属性 | 启用 Unity References → Test Assemblies 属性会将此程序集标记为测试程序集,而 Unity 通常不会将测试程序集包含在构建中(也不会在某些情况下进行编译)。发生这种情况时,任何引用缺失程序集的程序集都无法找到该程序集,除非它也是测试程序集。 |
下列更改属于向后兼容的、非重大的 API 更改。这些情况都添加了程序集,这与删除程序集的重大更改不同。由于添加程序集会增加 API 表面(API 的公开部分),因此被认为是 API 更改。但是,由于没有现有引用,因此添加新程序集不会影响使用更低版本 API 创建的其他程序集。
向后兼容的更改至少需要新的 MINOR 版本。如果您要包含属于重大更改的其他更新,则这些更新也可以成为新的 MAJOR 版本的一部分。
警告:仅当禁用 Auto Referenced 属性时,这些更改才向后兼容。启用 Auto Referenced 属性后,此表中列出的更改可能会导致重大更改。有关更多信息,请参阅自动引用部分。
情况 | 为何这些更改不会破坏编译 |
---|---|
在 .asmdef 中删除定义约束 | 删除定义约束意味着编译和脚本管线不再跳过该程序集。由于 Unity 始终会构建该程序集,因此无论是否满足这个定义约束,编译器都可以始终解析对该程序集的引用。 |
添加平台 | 添加平台对现有平台支持没有影响,因此是向后兼容的。这是 API 更改,因为增加了 API 表面。 可以通过修改以下属性来添加平台: • 向 includePlatforms 属性中添加条目。 • 完全删除 includePlatforms 属性。这等效于添加 includePlatforms 属性中还不存在的所有平台。 • 从 excludePlatforms 属性中删除条目。 |
使用新脚本创建程序集定义(与以前不同,使用不同的 .asmdef 文件) | 添加新程序集将增加 API 表面(API 的公开部分),而不会改变任何现有的实现。 |
在程序集定义文件中禁用 Unity References → Test Assemblies 属性 | 禁用 Unity References → Test Assemblies 属性会将此程序集标记为常规程序集,因此 Unity 不再将此程序集与任何程序集定义区别对待。这是 API 更改,因为它会增加 API 表面。 |
以下更改不会影响公共 API,并且在 PATCH 版本中允许。这些情况下的更改不会改变公共 API,因为它们不会影响 API 表面(API 的公开部分),并且不会对其他使用者产生任何影响。
不会改变公共 API 的更改至少需要新的 PATCH 版本。如果您要包含引入了重大更改或非重大更改的其他更新,那么也可以将这些更新包含在 MAJOR 或 MINOR 版本中。
情况 | 为何这些更改不会影响其他使用者 |
---|---|
在 .asmdef 文件中更改引用的程序集和程序集定义的列表 | 引用另一个程序集的程序集不会自动引用另一个程序集自身的引用,而必须显式列出这些引用。因此,更改程序集定义或程序集内的引用不会影响其他使用者。 |
更改程序集定义中的 Allow unsafe code 属性 | 此属性控制是否允许编译器编译具有 unsafe 修饰符的代码。仅更改该标志并不会改变公共 API。 |
更改程序集定义中的 Override References 属性 | 此属性控制 Unity 如何为该程序集调用编译器,并且对生成的程序集的使用者没有影响。仅更改该标志并不会改变公共 API。 |
包清单文件 (package.json) 指定名称、版本、包依赖关系以及有关包本身的其他元数据。
本节详细介绍包清单文件中的更改以及对包版本的影响:
更改 name 属性等同于删除一个包并添加一个不同名称的新包,这是不支持的。不能通过尝试发布更新来重命名包:必须作为一个全新的包来发布。不允许更改名称,因为现有的项目和包无法将这些名称解释为同义词。
更改项目中的依赖关系本身并不需要改变 MAJOR 或 MINOR 版本,除非它是 API 更改的一部分,或者启用了 Auto Referenced 属性。
本节提供了依赖关系更改及其应用上下文的示例(假设已禁用 Auto Referenced 属性,并且除了每种情况所描述的更改以外,没有其他 API 更改):
依赖关系更改 | 上下文 | 最低版本更改 |
---|---|---|
添加新的依赖关系 | • 使用新包,但不更改功能行为,也不更改 API 表面。 | PATCH |
• 使用新包引入新行为,但不修改 API 表面。 • 创建新的 API 以公开在新包中定义的类型。 |
MINOR | |
• 使用新包以非向后兼容的方式更改现有行为,但不修改 API 表面。 • 以非向后兼容的方式修改现有的 API 以公开新包中定义的类型。 |
MAJOR | |
删除依赖关系 | • 删除包,但不更改功能行为,也不更改 API 表面。 | PATCH |
• 删除包会导致以非向后兼容的方式更改现有行为,但不会更改 API 表面。 • 删除公开该依赖关系中定义的类型的 API。 |
MAJOR | |
更改依赖关系 | • 使用修改后的包,但不更改功能行为,也不更改 API 表面。 | PATCH |
• 使用修改后的包引入新行为,但不修改 API 表面。 • 创建新 API 以公开修改后的包中定义的类型。 |
MINOR | |
• 使用修改后的包以非向后兼容的方式更改现有行为,但不修改 API 表面。 • 以一种非向后兼容的方式更改现有的 API,以公开修改后的包中定义的类型。 • 公开 API 中的某些类型,这些类型在修改后的包中以非向后兼容的方式更改。 • 公开 API 中的某些类型,这些类型在修改后的包中不再定义。 |
MAJOR |
您可以更改对任何发行版中的 Package Manager、构建管线、脚本管线或资源数据库没有特殊影响的包清单属性。这包括更改 description、category、keywords 或 displayName。
如果更改了这些字段,可能表明您所做的更改不仅仅涉及错误修复。请务必考虑新版本中的其他更改是否实际上需要新的 MINOR 或 MAJOR 版本,而不是 PATCH 版本。
需要从 API 中删除某些功能时,请首先发布至少一个包含弃用功能的 MINOR 版本。这会提醒用户即将删除功能,让他们能够顺利过渡到新的 API。然后,您可以在新的 MAJOR 版本中删除该功能。
如果另一个开发者通过警告将一个包标记为过时,并且您在项目中启用了 Warnings as Errors 属性,则从技术上来讲,过时的包可能会破坏您的项目,但这不是真正的破坏,因为代码仍然可以像之前一样正常工作。
在这种情况下,您可以选择如何修复警告型错误(按照常规的合理性降序排列):
#pragma warning
指令中包裹使用该 API 的代码,以便不再显示该警告。