-
Notifications
You must be signed in to change notification settings - Fork 19
Development.zh CN
关于环境配置,详见先决条件和运行。其中后者也包含部分面向最终用户的说明。
以下为面向开发者的说明。
YSLib 项目文档位于 YSLib 项目而不是本 wiki 项目中。
维护者参考的细节和一般规则详见 YSLib 项目文档 doc/ProjectRules.txt
;术语的完整定义详见 YSLib 项目文档 doc/CommonRules.txt
。
基本规则参见 YSLib 项目文档 doc/ProjectRules.txt
。
本 wiki 项目作为用户手册和开发者补充文档的形式作为实现及之后阶段输出。
实现的附加输出为库和工具。
之前阶段(如设计)为前期过程,其文档和适用于维护过程的项目规则位于 YSLib 存储库中的项目文档(位于 doc/
)。其中整体过程由 doc/Designation.txt
指定。当前内联设计以外的过程,因此不存在设计外前期过程的单独文档。
关于外部依赖、平台、目标平台和宿主平台等概念的一般定义参见术语概要。
YSLib 项目约定一个体系结构和使用的外部依赖是一个平台。
YSLib 支持不同的目标平台。类似 ISO C/C++ 的独立实现(freestanding implementation) 和宿主实现(hosted implementation) ,平台分为两类:独立实现平台和宿主实现平台。后者存在操作系统的支持而前者没有。
本节适用于 YSLib 项目,不直接限制依赖项和用户程序。项目中特定部分的规则及适用性详见 YSLib 项目文档 doc/ProjectRules.txt
。
本节不保证所有具体要求都是完备的。特别地,这里不指定仅具体平台配置适用的要求。对一般开发者,先决条件中应已足够配置开发环境。
原理 本节中的要求和配置补充先决条件,和开发环境的选型可能直接相关,为维护者提供设定平台配置要求提供基准依据。
除脚本(见以下相关章节)外,使用 ISO C++ 作为主要开发语言。
不使用和 ISO C++03 以后被接受的特性不兼容的特性,包括但不限于:
- 被取消的特性,如 ISO C++03 后导出模板的
export
关键字。- 不限制有条件使用的之后的其它特性,如
export
被作为模块。(当前不使用模块。)
- 不限制有条件使用的之后的其它特性,如
- 在 ISO C++03 中标记为 deprecated 而在之后版本去除的特性,如
const char
数组类型左值到char*
右值的转换。 - 在 ISO C++03 中标记为 deprecated 但在之后版本重新取消 deprecated 的特性,如修饰命名空间作用域声明的
static
。 - 实现的 Defect Report ,如 CWG 615 。
- 显式排除的特定的语言特性:
-
P0145R3 :ISO C++17 指定的特定表达式的求值顺序。
-
注释 使用 G++ 的
-fno-strong-eval-order
选项可显式指定假定避免依赖这项特性。
-
注释 使用 G++ 的
-
P0145R3 :ISO C++17 指定的特定表达式的求值顺序。
注意即使使用特定模式,一些实现也可能引入之后的 Defect Report 而不保证兼容,如 GCC PR 65890 。
关于精确的特性使用规则、具体使用及备选的特性的清单等,详见标准使用(英文) 。
以下对语言实现的要求和支持情况适用整个项目。具体内容可能会在未来变动。
YSLib 依赖 ISO C++ 独立实现或宿主实现,附加以下要求:
- 满足标准使用(en-US) 中具体特性的要求。
- 注释 默认基于 ISO C++11 环境,但并不要求实现完整支持所有特性。
- 注释 一些特性具有替代实现或者是可选的,而不被依赖。
- 满足 ISO C++11 [implimits] 建议的最小实现要求。
- 标准库基于 ISO C++11 定义的宿主实现,并满足以下要求:
- 注释 语言实现不需要是完整的宿主实现。
- 提供以下符合标准的整数类型:
- 定宽整数
std::intN_t
和std::uintN_t
(其中 N 为 8 、16 、32 或 64 )。 - 类型
std::uintptr_t
。 - 注释 定宽整数在 ISO C++11 中为可选支持。
- 定宽整数
- 满足以下实现定义行为的要求:
- 至少支持
std::placeholders::_7
。 - 注释 按 ISO C++11 Annex B [implimits] ,符合标准的下限为 10 ,因此已被上述规则涵盖。
- 至少支持
- 假定用于迭代器的
difference_type
或坐标计算的有符号整数作为显式转换的目标类型且结果不能在范围内表示时,不引起副作用且结果的值不是能在此范围内表示的任意值(即为小于0
的值)。-
注释 一个典型的例子是
std::ptrdiff_t
。 - 当前标准中,转换到有符号数的结果由实现定义。
- WG21 P0907 已提议修改使用补码表示,并在 ISO C++20 采纳,按,符合此要求。
- 注释 一般的到有符号数转换的由实现定义的行为仍不被依赖。
-
注释 一个典型的例子是
- 假定特定类型的特定操作无异常抛出(但不依赖异常规范的行为),当前包括:
-
std::string
的默认构造函数( WG21 N4002 引入了显式指定noexcept
,仅从标准草案 WG21 N4296 起有效)。
-
- 假定被包含在具有外部链接实体的函数体或声明命名空间作用域中外部链接名称的被 ODR 使用(odr-used) 的 lambda 表达式相同。
- 否则会引起被包含的代码中的 lambda 在多个翻译单元 ODR 使用时违反 ODR 引发未定义行为 。此类未定义行为包括以下情形:
- 所在函数体在
extern inline
函数。这被 CWG 765 解决并存在于 ISO C++11 和之后的标准文本中。- 对符合 ISO C++11 及之后版本标准的实现,这个变通 对其中的示例代码因此是不必要的;其它情况也有代码膨胀的缺陷,不应使用。
- 已知当前所有版本的 Microsoft VC++ 编译器都没有实现这个特性且会在内联代码后引起未预期的行为,计划在下一版本修复。
- 命名空间作用域中的 lambda 表达式,或所在函数体在其它实体,包括函数模板、类模板的成员函数以及类模板的成员函数模板。这是未被提交和解决的 ISO C++ 缺陷。此处的假定作为用于避免这个问题。
- 所在函数体在
- 基于 Itanium ABI 的实现符合这个条件。
- 否则会引起被包含的代码中的 lambda 在多个翻译单元 ODR 使用时违反 ODR 引发未定义行为 。此类未定义行为包括以下情形:
- 对宿主环境中程序外部的状态的并发修改不引起未定义行为。
- 现有操作系统和文件系统提供的接口和实现普遍不能保证避免 TOCTTOU 访问(en-US) 导致的问题。具体修改的结果未指定,但应不直接引起无法预测的程序行为。
- 除非另行指定,本项目的实现不保证检查外部程序的修改。
假定 YSLib 实现和用户程序的代码满足以下要求:
- 假定异常和标准库 RTTI 对象满足 ODR ,即使是在使用动态库的宿主实现中。
- 但影响用户代码生成的实现的二进制约定(如 ARM64 )且不使用以下错误实现变通的情形除外。
- 这要求用户代码不依赖影响相关符号可见性而导致 ODR 失效的特性。
- 例如,这不允许如
dlopen
使用RTLD_LOCAL
加载具有相关符号的库。
- 例如,这不允许如
- 这允许
std::type_info
的比较操作和散列操作的高效实现,并避免一些实现错误。- 使用 libstdc++ (libsupc++) 时,需要重定义宏
__GXX_TYPEINFO_EQUALITY_INLINE
和宏__GXX_MERGED_TYPEINFO_NAMES
为1
。- 这是对
std::type_info
比较操作的错误实现的变通。- 参见 GCC r179236 的修改。
- 参见 GCC PR 103240 。
- 一般在编译器命令行中使用
-U
选项取消可能存在的预定义再使用-D
选项重新显式定义,以避免预定义宏被重定义。 - 调整
__GXX_TYPEINFO_EQUALITY_INLINE
对启用__GXX_MERGED_TYPEINFO_NAMES
是必要的。 -
注释
__GXX_MERGED_TYPEINFO_NAMES
默认值被修改 以支持上述影响符号可见性的特性。
- 这是对
- 其它实现暂不使用选项支持,以避免和上述二进制约定冲突。
- libc++ 不支持另行调整,由实现根据平台环境指定
_LIBCPP_HAS_NONUNIQUE_TYPEINFO
。 - 另见以下关于
std::type_info
比较的限制。
- libc++ 不支持另行调整,由实现根据平台环境指定
- 使用 libstdc++ (libsupc++) 时,需要重定义宏
- 链接时除上述二进制约定,假定
std::type_info
相关的符号被共享。
- 不依赖 RTTI 的
std::type_info
对象在未命名命名空间中同名不同实体的比较结果:- libc++ 没有正确支持这些比较,参见 LLVM PR 34907(LLVM GitHub issue 34255)。
- Clang++ 当前没有正确支持生成相关名称。
- 注释 在未命名命名空间的实体和命名命名空间的实体之间的比较不被涵盖。
- 假定在标准库宏
NDEBUG
被定义的翻译单元中的代码不违反异常规范。- 这允许改进代码生成,如使用 G++ 的
-fno-enforce-eh-specs
选项。 - 程序应不依赖违反异常规范时调用标准库的函数的行为。否则,程序行为未定义。
- 这允许改进代码生成,如使用 G++ 的
YSLib 中具体子项目可要求更严格的实现假定,违反这些假定要求诊断,并可能在不保证支持的构建配置环境下终止构建。需要显式提供诊断时,若可行,应使用符合上述要求的可移植特性。此外,YSLib 实现可依赖可选的语言实现扩展。具体的更严格的假定、可选特性或非全局的特性使用参见项目版本库中 doc
目录下的相关项目文档。
注释 一些诊断通过语言规则保证而无需显式提供。提供诊断使用的可移植特性的一个例子是 #error
预处理命令。
原理
本节中指定的要求通常难以以源代码的形式检查或检测,因此在此作为前提明确。一些普遍但并非严格必须作为全局依赖的特性和实现假定,可以通过源程序表达时,不在这里指定;但为简化实现,也可以通过要求的形式可选地指定。
YBase 对标准库的修正实现要求更严格的关于实现细节的假定。YBase 对标准库的不分替代(如 std::addressof
)需依赖更严格的实现特性才能完整实现和标准库相同的保证(尽管不分保证作为 YBase 替代是可选提供的)。依赖更严格的实现假定也允许依赖和向用户代码提供特定平台配置的功能特性,以及提供质量更好的高性能实现。
注释
非全局的特性涉及的假定可能以子项目或更小的模块作为单位明确其适用范围。关于可选指定的实现要求,参见以下的可选实现要求。
以下相对基准实现要求更严格的实现要求默认不作为全局默认要求,而可供特定的子项目和平台配置按需启用。具体使用参见存储库中 doc/
目录下有关具体子项目的项目文档。
- 关于整数类型的假定:
- 1 字节具有 8 位(即
CHAR_BIT == 8
)。 - 相对 ISO C++ 要求更严格的特定整数类型的取值范围。
- 语言实现和外部环境满足以下表示的要求:
- 除指定的例外,整数类型的表示不具有填充位(padding bit) ,即构成其对象表示(object representation) 的位和值表示(value representation)一致,但这里指定的例外除外。
- 注释 关于填充位,参见 ISO C 关于整数类型的描述。
- 指定的例外包含以下情形:
- (可能 cv 限定的)
bool
类型。 - 语义和表示可能兼容 ISO C23
_BitInt(N)
的类型。-
注释 一个例子是 LLVM 扩展
_ExtInt(N)
。
-
注释 一个例子是 LLVM 扩展
- 其它在 YSLib 中的 API 或互操作规范中指定的允许接受的特定整数类型。
- (可能 cv 限定的)
- 除指定的例外,整数类型的表示不具有填充位(padding bit) ,即构成其对象表示(object representation) 的位和值表示(value representation)一致,但这里指定的例外除外。
- 1 字节具有 8 位(即
以下要求已被修改或取消。
- 假定提供撤销标准库未定义行为的保证:
- 撤销 [res.on.functions]/2.5 对特定不完整类型作为模板实际参数引起未定义行为的限制,包括:
- 使用默认分配器(
std::allocator
的实例)的std::vector
的value_type
类型。- 这被包含在 WG21 N4510 。
-
ystdex::pmr::pool_resource
的实现从 b843[2018-11-10] 起依赖这项特性。 - 从 b863[2019-07-26] 起不使用不完整类型的元素而不再依赖这项特性。
- 使用默认分配器(
std::allocator
的实例)的关联容器(即std::map
和std::set
)的value_type
类型。- 对
std::vector
、std::list
和std::forward_list
,WG21 N4510 引入了不完整类型的支持。这不在此处要求。关联容器的要求预期在未来被添加。 - 已知 libstdc++ 的实现符合这个条件。
- 在 YSLib 中仅被
YSLib::ValueNode
通过在std::map
使用递归的键类型的实现从 b338[2012-09-13] 起依赖。 - 因为使用
ystdex::map
替代std::map
,从 b830[2017-08-11] 起取消这个要求,不再依赖标准库实现提供的扩展特性。
- 对
- 使用默认分配器(
- 撤销 [res.on.functions]/2.5 对特定不完整类型作为模板实际参数引起未定义行为的限制,包括:
- 假定特定类型的特定操作无异常抛出(但不依赖异常规范的行为),包括:
-
std::function::swap
( LWG 2062 起有效)。- 因为使用
ystdex::function
替代std::function
,从 b848[2018-12-24] 起取消这个要求。
- 因为使用
-
允许使用 ISO C++11 以后兼容最新标准草案的正式标准中的特性(可通过 __cplusplus
宏和 SG10 建议的特性检查判断)。
除非另行指定,不依赖实现的方言扩展。
注释 在确保实现能支持时,在特定的代码中可通过条件包含等方式选用。
关于语言特性中的具体使用以及启用的扩展,参见 YSLib 项目文档 doc/LanguageRules.txt
和 doc/YBase.txt
等具体部分的相关开发文档。
若实现默认具有不符合标准的特性,在本项目的代码中不依赖这些特性,即便外部依赖项可能对此进行配置(如 MinGW G++ 为了和 Microsoft VC++ 兼容启用的 -mms-bitfields
,而 MSYS2 安装的 freetype2 的 pkg-config
的 CFLAGS 隐含此参数)。
YSLib 项目中,除了 YBase.LibDefect 是对标准库实现的修正外,并不是语言的实现,因此公开接口遵循 ISO C++ 对保留名称的使用,如不引入以 __
起始的标识符。
对实现环境已经以保留标识符提供的接口,适用以下规则:
-
<ydef.h>
提供宏包装特定实现的标识符。 - 除标准预定义的(如
__cplusplus
)和用于特性检查的标识符(以__cpp
或__has
起始),以及上述被包装时的宏定义,不在注解(作为宏YB_ATTR
和YB_ATTR_STD
的参数)外直接使用保留标识符。
<ydef.h>
和其它一些 YSLib 项目头文件保留特定的不被标准保留的标识符,详见 YSLib 项目文档 doc/Definitions.txt
。
YSLib 项目由多个子项目组成。其中主要的有顶级子项目:YBase 和 YFramework 。它们是开发 YSLib 应用的必备的库。每个库被构建为单独的映像(静态库或动态库)。
YSLib 的组件有些是依赖于特定平台的,但更多是平台中立的。关于库的组件在此的不同,详见下文的解释。
静态库、动态库或其它可能被库构建时依赖的输入以及构建使用的工具是库构建的依赖项。关于依赖项的一般说明,详见术语中关于依赖管理的说明。
除非另行指定,文档中的狭义的“模拟”概念指程序模拟。
YSLib 项目中,平台模拟(platform emulation) 主要指直接以运行时环境适配层嵌入宿主平台运行时,在具体程序中提供类似被模拟的目标平台的具体特性和接口。
完整的定义详见 YSLib 项目文档 doc/CommonRules.txt
。
外部依赖项在构建项目的上下文中,指构建时可能使用的外部依赖,是不由本项目维护和单独发布的依赖项。
YSLib 项目严格使用 ISO C++ 的子集和特定实现的可选的扩展。关于依赖的语言特性,参见以上语言使用和实现要求的说明。
在所有目标平台上,除了系统库外,外部依赖项是相同的,但可能使用不同的版本,也不一定按相同的配置构建。系统库的概念和 GNU GPLv3 的 system library exception 中的 system library 定义类似,在此指特定平台或操作系统提供运行时支持的、由特定第三方环境提供开发支持的外部依赖,例如提供特定平台的 ISO C++ 标准库部分实现的 libstdc++ 和提供 Windows API 实现的 GDI32 等。
除了系统库外,一部分外部依赖项可选或必须使用自行构建的(可能被修改的)版本。这些外部依赖项的修改和构建脚本位于版本库的 3rdparty
目录,默认按原始许可证发行。
YBase 只直接依赖 ISO C++ 标准库。
YFramework 默认依赖经过修改的 FreeType2 和 FreeImage 。其中前者当前仅修改头文件,经过特别处理和官方发布的直接构建的版本二进制兼容,可以被系统库替换。
当前版本中,存储库中不同宿主平台对应的静态库文件(后缀名为 .a
)的目录如下:
YFramework/DS/lib
YFramework/MinGW32/lib
YFramework/MinGW64/lib
YFramework/Android/lib
YFramework/Linux/lib
其中,使用的 FreeImage 静态库对应 YFramework 的 debug
和非 debug
配置,文件名分别为 libFreeImaged.a
和 libFreeImage.a
。这可在同一个目录树中共存。
(当前 Android 和 Linux 仅支持单一本机体系结构,实际仅测试 Android ARMv7 和 Linux x86_64 。)
构建 YFramework 时需包含 3rdparty/include
目录的头文件。Sysroot 安装脚本 Tools/install-sysroot.sh 会复制包括上面的头文件在内的文件。
当前已经使用的详细外部依赖项详见 YSLib 项目文档 doc/Dependencies.txt
。
注意 版本库历史中包括静态库(.a
) 文件,但为减少版本库大小,不再更新且可能移除,使用外部源或自行构建的方式替代。
可使用以下方式安装外部依赖项:
- 参见归档,获取单独或源代码目录集成的已编译的外部依赖项。
- 非正式支持并发布的平台当前不都提供归档。
- 关于正式支持,另见存储库中的
doc/ProjectRules.txt
的说明。
- 若在宿主平台使用 Sysroot ,脚本可自动从网络下载安装归档。
- 非宿主平台当前不支持使用 Sysroot 构建。
- 对全新安装,设置环境变量
SS_DirectExtract
为非空值可能(很小地)提升性能。
- 参见存储库中的
3rdparty
中的内容,手动构建所需的外部依赖项。
在 build 885 之前,版本库历史的对应的宿主平台中的静态库位于以下位置:
YFramework/DS/lib
YFramework/MinGW32/lib-i686
YFramework/Android/lib
YFramework/Linux/lib-x86_64
其中 Android 平台只包括 FreeType2 ,Linux 平台只包括 FreeImage 。其它平台包括 FreeType2 和 FreeImage 库文件。
其它平台中,只有随其它文件的发布版本包含完整更新,否则压缩包中可能只有其中一个库文件。
关于自行构建外部依赖项的方法,参见构建说明。
YSLib 项目组织为一个逻辑上的树形结构,其叶节点称为模块。类似文件系统,非叶节点称为模块目录。在源代码中使用模块路径来标识不同的模块,其分隔符为 ::
。在 YSLib 项目下直接划分的顶级子项目的名称仅在必要时出现在模块路径中,完整模块路径一般从次级子项目的名称起始。
C 和 C++ 源代码的一个模块由以下三种形式之一构成:
- 一个头文件
- 一个非头文件的源文件
- 一个头文件和对应的源文件
作为公开接口的模块是公开模块。公开模块的头文件在单独的目录中以便部署。
按 ISO C 和 ISO C++ 规定,C 或 C++ 模块中存在的非头文件的源文件和包含的头文件构成一个 C 或 C++(预处理)翻译单元,简称单元。注意这里包含的头文件不仅限于模块中的头文件。
关于模块的进一步说明以及模块路径的形式文法和头文件依赖的基本规则,参见 YSLib 项目文档 doc/ProjectRules.txt
。
作为 C++ 项目,YSLib 把每个顶级子项目的源文件和公开模块头文件分别保存在不同的目录中,即 include
和 source
。非公开模块若有头文件,也位于 source
。
特定于目标平台配置的代码会直接位于 平台名
目录下,称为平台扩展。对应的两个目录为 平台名/include
和 平台名/source
。
除了平台扩展的内容,文件系统目录和模块目录的每一级对应。平台扩展的模块目录名是对应平台中立部分加上后缀 _(平台名)
。
举例:顶级子项目 YFramework 下的次级子项目 Helper 的非平台扩展的源代码在目录 YFramework/include/Helper
和 YFramework/source/Helper
中,它的 DS 平台扩展的源代码位于 YFramework/DS/include/Helper
和 YFramework/DS/source/Helper
中。
提供平台扩展的次级子项目只有 YCLib 和 Helper 。
编译项目时包含的头文件是合并的,如编译器命令行 -IYFramework/DS/include -IYFramework/include
在一次编译中同时使用平台中立和特定于平台 DS 的模块的 YFramework 头文件。
在 YBase 和 YFramework 分离之前,YSLib 是一整个库。原 YSLib 大部分仍然在 YFramework 中,仍然可称为 YSLib ,是一个 YFramework 下的次级子项目。注意和整个项目名的不同,以下称为 YSLib 库,以示区分。
YSLib 库中,Adaptor 用于适配特定于具体外部依赖的接口。可以通过修改其中的代码替换外部依赖,包括部分标准库兼容接口。
其它部分的接口和实现都是严格平台中立且不依赖外部特定接口而变化的,称为本体。本体中提供了 YSLib 的主要功能。
若需要开发依赖平台特定的应用,本体接口可能不足,而需要使用平台扩展。此外,可能需要一些便利功能。
在 YSLib 库之上,Helper 对此类需求提供了一致而灵活的接口。
若需要更接近特定平台实现的接口,可以使用 YCLib 及其平台扩展。
YCLib 和 Helper 在宿主实现上都提供了更加丰富的功能。
YSLib 项目维护的代码规范符合 YSLib 项目中的文档约定的规则,包括:
-
doc/CommonRules.txt
:一般规则。 -
doc/ProjectRules.txt
:项目规则。 -
doc/LanguageConvention.txt
:语言使用约定。
YSLib 中的脚本代码应符合以下相关章节的约定。
doc/CommonRules.txt
中规定了命名风格和参考的代码格式。由于格式化代码涉及语义分析,并不保证可以完全自动化进行,需要在编码时注意调整。
以下工具配置可以把其中的主要工作自动化进行:
-
clang-format
- 可通过 MSYS2 包
mingw-w64-i686-clang
或mingw-w64-x86_64-clang
安装,以下配置以这里的 3.7 版本为基准测试 - 配置选项的文档参见这里
- 通过命令行
-style=
指定使用选项文件Tools/YSLib.clang-format
-
注意 至少以下格式需要手动调整
- 在 LLVM r278121 添加 SpaceAfterTemplateKeyword 支持 前 只支持
template <
而不支持template<
- 行末的
\
前可能附加多个空格 -
{}
会被拆分 - 部分宏会被作为块的起始叠加缩进
- 选项
AlwaysBreakAfterReturnType
未被支持,部分函数和函数模板声明的返回类型后缺少换行
- 在 LLVM r278121 添加 SpaceAfterTemplateKeyword 支持 前 只支持
- 通过命令行
-i
直接编辑文件而不是打印结果到标准输出 - 命令行示例:
find YBase YFramework YSTest -name "*.h" -o -name "*.hpp" -o -name "*. cpp" | xargs clang-format -i -style=file
- 命令行示例:
find YBase YFramework YSTest -name "*.h" -o -name "*.hpp" -o -name "*. cpp" | xargs -i sh -c "clang-format -i -style=file {}"
- 命令行示例(同时替换
template <
):find YBase YFramework YSTest -name "*.h" -o -name "*.hpp" -o -name "*. cpp" | xargs -i sh -c "clang-format -i -style=file {} && sed -bi 's/template </template</g' {}"
- 可通过 MSYS2 包
-
astyle
- 可通过 MSYS2 包
mingw-w64-i686-astyle
或mingw-w64-x86_64-astyle
安装,以下命令行以这里的 2.05.1 版本为基准测试 *(版本 2.05 )命令行选项的文档参见这里-
--help
取得的文档可能有问题,--delete-empty-lines
对应的短选项应为-xe
而不是-xd
-
- 使用命令行选项
-A1 -T -p -U -k1 -xj -xy -xC80
- 短选项可以缩写
- 和上述
clang-format
比较,少了部分功能,主要有AlignAfterOpenBracket: DontAlign
AlignTrailingComments: true
AlwaysBreakAfterReturnType: All
MaxEmptyLinesToKeep: 2
-
注意 至少以下格式需要手动调整
- 无法正确识别
constexpr
导致错误的缩进 - 虽然右值引用识别问题已解决,但实测对模板参数无效
- 对
extern "C"
块冗余缩进 - 断行后的缩进
- lambda 表达式的捕获列表中的
=
周围的冗余空格以及对应的{
断行 -
static_cast
等关键字后的<>
周围的冗余空格 - 宏实际参数列表头部的
(
和作为第一个参数的标点可能有冗余空格 - 宏实际参数列表尾部的
,)
没有以空格隔离 - 初始化数组的列表
{
和之前的]
存在冗余空格
- 无法正确识别
- 不使用
-xp
,尽管一些注释需要(移除行首*
后保持一级缩进)的此类格式,但它会不必要地影响大部分 Doxygen 注释块 - 默认备份文件后缀
.orig
;可选使用-n
取消备份文件,或--suffix=
修改备份文件后缀 - 使用
-r
递归处理子目录 - 可选使用
-v
显示详细过程 - 可选使用
-Q
只显示被处理的文件 - 可选使用
--dry-run
不实际处理文件 - 命令行示例:
astyle -vQnrA1TpUk1xjxyxC80 YBase/*.h* YBase/*.cpp YFramework/*.h* YFramework/*.cpp YSTest/*.h* YSTest/*.cpp
- 可通过 MSYS2 包
一般地,在脚本中默认指定的工具链的警告选项应能支持代码规范。
YSLib 版本库中包含若干脚本。这些脚本和 YSLib 安装部署的脚本使用命令行程序运行环境和本章中的约定。
脚本一般通过调用解释器运行。脚本的运行要求宿主环境(符合 YSLib 源码 YF_Hosted
定义支持的平台,指具有操作系统的环境)。
除非另行指定,脚本以文件的形式部署。
- 除非另行指定,脚本不假定自身的存储位置。
- 一般地,这允许脚本的内容不总是可通过文件系统访问。这包括执行时脚本的来源被移除和不公开以文件的形式部署等情形。
- 对文件系统中的脚本:
- 脚本文件总是普通文件或可解析到普通文件的有效的符号链接。
- 除非另行指定,不假定脚本文件所在的位置或其父路径可写。
项目中提供的脚本在版本库中具有固定的相对路径。除解释环境适用的公共的约定(如提供一般类 UNIX 系统使用的文件系统布局的 FHS(文件系统层次结构标准)),脚本不预设绝对路径的假定。
警告 小心处理路径。当前脚本直接或间接使用的所有涉及文件系统递归操作均不假设检查遍历的目标之间是否重复,若使用不恰当的目录链接,可能造成未预期的行为(如复制指向父目录的目录链接时可引起无限递归)。部署时应提前确保不存在这样的链接。
脚本实现假定没有可能冲突的并发文件访问。
脚本文件总是使用以下约定的扩展名。
除了 Windows 命令解释器使用的 .cmd
文件以及 NPLA1 脚本使用的 .txt
文件,所有脚本使用 Shebang 明确需要使用的解释环境(详见 shell 脚本的说明)。
Windows 命令解释器脚本因为实现限制使用本机 ANSI 代码页的编码,约定为兼容 ASCII 。
NPLA1 脚本约定使用带有 BOM 的 UTF-8 编码(去除 BOM 的脚本可能兼容 ASCII )。详见 stage 1 SHBuild 中关于 NPL 支持的说明。
脚本变量可能被读取和加载。
除非另行指定,脚本引入的非导出的变量和名称以 _
结尾的变量,都不是公开接口。
原理 基于名称约定便于和构成公开接口的名称区分。
路径(path) 是由有限的路径组件构成的序列。在不引起歧义时,路径即指文件系统路径。文件系统的路径组件能以字符串形式表示。
在支持相对路径的环境中,是否支持相对路径取决于具体脚本。
内部的路径的分隔符使用 /
。通过工具脚本提供的 SHBuild_2w
等函数,可转换为带有不同路径分隔符的路径字符串。这种转换通常仅在必要时(如明确作为外部工具的输入)使用。
字符串形式的路径(即路径字符串)可能是保证不以分隔符结尾的文件名和确保以分隔符结尾的目录路径字符串。目录路径指目录路径字符串或其对应的非字符串形式的路径。
两个路径字符串的拼接可构成新的路径字符串。这两个路径字符串分别是路径前缀和路径后缀。除非另行指定,作为路径前缀使用的路径字符串是文件名。这要求通过串接路径后缀构成访问前缀指定的目录的新的路径时,路径后缀需要以分隔符起始。
注意 YBase 的 ystdex::path
和 YFramework 的 YSLib::IO::Path
等数据结构表示根路径外无视结尾分隔符的非字符串形式的路径,因为有效的分隔符仅在根路径中出现。这些实现一般不检查分隔符的合法性,如其中具有包含分隔符的路径组件,也可能正常转换为字符串形式的合法路径。当前脚本只使用字符串形式的路径。
扩展名 .sh
的脚本文件是 shell 脚本文件。大多数脚本需要使用 GNU bash 运行,如:
#!/usr/bin/bash
需要考虑兼容性时,一般使用以下替代:
#!/usr/bin/env bash
其它可以直接兼容 POSIX shell 的 .sh
脚本使用 Shebang 如:
#!/usr/bin/sh
需要考虑兼容性时,一般使用以下替代:
#!/usr/bin/env sh
为简化脚本代码,用户需要保证调用 shell 脚本时,环境应满足以下条件,否则行为未指定:
- 使用满足要求的 Shell 语言实现:
- 不论是否设置了变量
POSIXLY_CORRECT
,命令解释环境的变量满足:- 特殊内建工具没有被用户定义的同名变量或别名覆盖。
- 被脚本调用的 POSIX 定义的工具命令没有被用户定义的同名变量或别名覆盖为调用时可观察行为不等价的实体。
- 变量
IFS
未设置或设置为默认值。
- 命令解释器同
bash
的-p
选项启用时效果相同。- 用户应保证不设置
CDPATH
等-p
忽略的环境变量及$BASH_ENV
等启动文件,以避免未预期的不同行为。
- 用户应保证不设置
- 脚本文件不是目标在不同目录中的符号链接。
- 对 bash 脚本,允许使用变量
BASH_SOURCE
的值(如${BASH_SOURCE[0]}
或${BASH_SOURCE%/*}
等形式) 较可靠地判断脚本文件的路径。此时,隐含需要附加假定通过脚本文件路径确定其它资源的位置。 - 部分脚本可能有更进一步的使用限制或不依赖
.
命令及命令解释器调用的差异。此时,可直接使用"$0"
判断路径。
- 对 bash 脚本,允许使用变量
- 除非另行指定,调用的外部命令的程序满足以下可用性要求:
- 以 POSIX 实用程序名称直接调用的满足 POSIX 对应的要求。
- 当前使用的 shell 的名称(如
bash
)总是能被 POSIXenv
实用程序搜索到并调用。 -
/usr/bin/env
总是提供可用的 POSIXenv
实用程序。-
注释 这允许上述 Shebang 可用。同时,
/usr/bin/env
作为命令名称一般等同于env
。
-
注释 这允许上述 Shebang 可用。同时,
-
注释 除非另行指定,不使用
which
。- 在 GNU bash 中,对检查一个操作数的情形,使用
type -P
代替。 - 注意
command -v
对别名等非$PATH
中可搜索的程序处理不同。 - 另见 ShellCheck 警告 SC2230 和相关讨论。
- 在 GNU bash 中,对检查一个操作数的情形,使用
脚本实现中的调用不受以上限制。
若脚本可以确保兼容 POSIX shell ,使用后者而不是前者。
如果对应解释器位于其它目录,可以符号链接到上面指定的路径。
除非另行指定,本文档约定使用的路径字符串的分隔符为 /
,不连续出现在路径中,且不在结尾出现。例外:
- 平台相关的绝对路径转义可出现
//
。 - 构成路径的路径前缀可能以分隔符结尾,直接表示上层目录。
注意 当前版本库中的文件不保证跟踪权限。在一些环境中可能因为可执行权限问题导致无法立即执行脚本,参见这里的说明 。
正式支持的发布版本中的公开 Shell 脚本应保证用 ShellCheck 0.7 或以上版本(任选)检查没有诊断消息。检查的命令行为 shellcheck -x -P SCRIPTDIR
跟随文件名。除非另行指定,使用明确的方式避免引起检查的问题而不是使用指令消除检查的结果,如使用 ${BASH_SOURCE[0]}
代替 $BASH_SOURCE
以避免 ShellCheck 警告 SC2128 。
脚本程序不调整影响别名扩展的 shell 选项(如 bash 的 shopt -u expand_aliases
)。
依照运行时环境的约定,除非另行指定,脚本不区分未设置的环境变量和已设置但具有空值的环境变量,以允许在外部环境配置脚本的执行。这两种情形下,被用于脚本中的变量的环境变量都可能指定一个非空的初始值,称为变量的默认值(default value) 。变量是否使用默认值总在先于第一次使用变量的值的初始化时确定。
变量的初始化和默认值满足以下约定:
- 初始化的变量是否只读未指定。
- 初始化变量时若需使用默认值,可能发生附加的求值(如命令调用)以其结果确定具体默认值。
- 除非变量的初始化被指定为由特定的函数确定,变量在未指定的脚本被执行时无条件初始化,而不需要使用变量的值前调用特定的函数。
- 除非另行指定:
- 在特定函数中初始化的变量由此函数指定其默认值。
- 初始化时使用的外部环境变量的值可假定和脚本运行环境直接或间接调用脚本前相同。
除非另行指定,shell 函数满足以下约定:
- 已定义的 shell 函数假定不被其它变量覆盖。
-
原理 这允许实现省略
readonly -f
或declare -g -r -f
声明,且允许使用相同的定义覆盖以简化调试。
-
原理 这允许实现省略
- Shell 函数返回值是通过接口语义中蕴含的最后的命令调用的返回值;或当不存在这样的命令时,默认为
0
。 - Shell 函数的结果是标准输出的内容。
-
注释 通常使用
echo
或功能蕴含echo
的命令调用输出返回的值。
-
注释 通常使用
非生成的脚本源代码中不使用包含 __
的名称。
公开函数的函数名及公开的变量名不以 _
结尾,否则以一个 _
结尾。
对 SHBuild 相关工具使用的函数,使用前缀 SHBuild
。
函数出错且无法恢复时退出脚本。
版本库中的提供的脚本一般应能通过 ShellCheck 检查。对需要使用 ShellCheck 指令指定脚本起始构造的检查时,在之前添加一行空命令(对 bash 脚本使用 :
,非 bash 脚本命令使用 true
)以避免非预期地使指令作用于整个脚本。
Shell 脚本可使用以下能通过外部环境指定值的环境变量:
- 用于指定入口位置:
-
SHBuild
:外部 SHBuild 可执行文件路径。- 默认值和特定脚本被设计适应的部署环境相关(例如,视不同情形,脚本可使用
${BASH_SOURCE[0]}
或"$0"
)。
- 默认值和特定脚本被设计适应的部署环境相关(例如,视不同情形,脚本可使用
-
SHBuild_ToolDir
:工具目录中的脚本目录(参见脚本)。- 默认值由脚本根据存储库所在目录确定。
-
- 用于指定存储库中的资源:
-
SHBuild_BaseDir
: SHBuild 目录,是工具目录中提供 SHBuild 的源代码的目录。- 默认值按
SHBuild_ToolDir
初始化后的相对位置确定,为"$SHBuild_ToolDir/../SHBuild"
。
- 默认值按
-
YSLib_BaseDir
:YSLib 目录,即版本库检出后的工作目录。- 默认值按
SHBuild_ToolDir
初始化后的相对位置确定,为"$SHBuild_ToolDir/../.."
。
- 默认值按
-
- 用于指定输出目录:
-
SHBuild_BuildDir
:构建使用的中间输出文件路径。- 默认值定义如下:
- 对不依赖 Sysroot 的以及
YSLib/YSTest
目录下的项目构建脚本,默认值指定在完整的版本库中和"$YSLib_BaseDir/build/$(SHBuild_GetBuildName)"
相同的目录,且构建 stage 1 YSLib 时确保为绝对路径。 - 对
YSLib/YDE
目录下的项目构建脚本,默认值是".$(SHBuild_GetBuildName)"
。 - 否则,默认值是当前工作目录
.
。 - 以上调用的函数
SHBuild_GetBuildName
在脚本Tools/Scripts/SHBuild-common.sh
中,依赖具有非空值的变量SHBuild_Host_OS
和SHBuild_Host_Arch
。
- 对不依赖 Sysroot 的以及
- 默认值定义如下:
-
SHBuild_SysRoot
:输出的 Sysroot 根路径。- 在 stage 1 中,默认值是
"$YSLib_BaseDir/sysroot"
。
- 在 stage 1 中,默认值是
-
- 用于配置构建环境:
- 其它情形不用于指定输出目录,不保证具有默认值。
-
SHBuild_Env_TempDir
:构建时使用的临时目录路径。- 由脚本
Tools/Scripts/SHBuild-common.sh
的函数SHBuild_PrepareBuild
初始化。 - 默认值由脚本
Tools/Scripts/SHBuild-common.sh
中的函数SHBuild_GetTempDir
的调用确定。
- 由脚本
-
SHBuild_Env_Arch
:构建系统架构。- 由脚本
Tools/Scripts/SHBuild-common.sh
的函数SHBuild_CheckUName
初始化。
- 由脚本
-
SHBuild_Env_OS
:构建系统操作系统。- 初始化和确定值的方式同
SHBuild_Env_Arch
。
- 初始化和确定值的方式同
-
SHBuild_Host_Arch
:宿主架构。- 由脚本
Tools/Scripts/SHBuild-common.sh
的函数SHBuild_PrepareBuild
初始化。 - 默认值使用以下方式指定:
- 当
SHBuild_Env_OS
的值是Win32
且外部变量MSYSTEM
的值是MSYSTEM64
时,默认值为x86_64
。 - 当
SHBuild_Env_OS
的值是Win32
且外部变量MSYSTEM
的值是MSYSTEM32
时,默认值为i686
。 - 其它情形默认值同
SHBuild_Env_Arch
初始化后的值。
- 当
- 由脚本
-
SHBuild_Host_OS
:宿主操作系统。- 初始化的方式同
SHBuild_Host_Arch
。 - 默认值同
SHBuild_Env_OS
初始化后的值。
- 初始化的方式同
-
SHBuild_VCS_
起始的变量:指定使用的版本控制系统。-
SHBuild_VCS_hg
:若非空,指定使用 Mercurial 。 -
SHBuild_VCS_git
:若非空,指定使用 Git 。 - 除非另行指定,若同时指定使用 Mercurial 和 Git 两者,则使用 Mercurial 。
- 指定使用版本控制系统是提示,不保证是实际的选择。具体使用前,脚本可能自动对可用性(命令行
hg
和git
及当前工作目录是否位于对应的版本库)进行检查,当不满足要求时可能忽略提示。 - 原理 若当前工作目录同时位于多个版本控制系统的版本库,使用提示可以明确选择其中之一。
- 注释 本项目的源代码可从 Mercurial 或 Git 中取得。
-
-
SHBuild
构建使用的变量:详见 SHBuild 的帮助信息。 - 其它构建脚本使用的变量:详见以下具体脚本的说明。
若以上变量在上述任一情形存在作为路径的默认值且外部环境指定变量的值,则指定的值应表示合法的路径。
一般地,部署的 shell 脚本需引用其它脚本时,以如下方式使用指定入口的环境变量:
- 若脚本只支持 Sysroot ,以
SHBuild
在 Sysroot 的安装位置推断其它脚本的路径。 - 若脚本只支持工具目录中的脚本目录布局,以
SHBuild_ToolDir
指定。- 这隐含目录在版本库中的布局,可以此初始化
YSLib_BaseDir
等变量的默认值。
- 这隐含目录在版本库中的布局,可以此初始化
- 若脚本同时支持 Sysroot 中部署的位置和工具目录中的脚本目录布局:则不使用以上的变量。
- 引用其它脚本文件时可直接指定包含,如:
. "$(dirname "${BASH_SOURCE[0]}")/SHBuild-common.sh"
。
- 引用其它脚本文件时可直接指定包含,如:
除指定入口的环境变量,是否支持外部可覆盖默认值的上述环境变量是可选的,取决于各个脚本的具体支持。
典型地,Shell 指定的环境变量可被以下方式在被 shell 运行的命令中使用:
- 标记导出变量,在调用的命令访问变量:
-
export
命令。 -
declare -x
命令。 - 使用
set -a
命令后的变量声明。
-
- 启用子 shell(subshell) 或调用 shell 解释器等方式运行 shell 脚本或以下命令等方式使用当前环境或继承环境:
- 临时指定特定的环境并调用命令:
- 使用
env
命令。 - 调用命令时以兼容 Bourne shell 的使用前缀参数赋值的命令调用临时指定特定的环境。
- 使用
set -k
命令后的参数赋值。
- 使用
- 注释 另见 POSIX 命令执行环境 和 GNU bash 命令执行环境。
关于这些环境变量如何影响其它程序,参见运行时的环境变量。
NPLA1 脚本可被 SHBuild 调用。SHBuild 支持特定的选项作为 NPLA1 的脚本解释器。脚本解释器支持 NPLA1 脚本文件。此外,在 shell 脚本中,NPLA1 脚本代码可能以字符串的形式存储和被 SHBuild 调用。
NPLA1 脚本可使用和 shell 脚本通用的环境变量,如 SHBuild
。
除 stage 1 外,shell 脚本使用环境变量 NPLA1_ROOT
指定的根目录作为 SHBuild 加载 NPLA1 脚本使用的起始目录。
脚本(包括直接或间接调用 NPLA1 脚本的其它脚本)除默认值外不需要依赖 Sysroot 的安装路径下的目录和文件布局,环境变量 SHBuild
和 NPLA1_ROOT
可分别指定相互无依赖的路径。当仅指定 SHBuild
时,通过指定的 SHBuild 路径推断 NPLA1_ROOT
默认值,此时使用假定符合 Sysroot 约定的相对路径。
NPLA1 脚本可使用以上约定含义和默认值的 shell 环境变量并初始化 NPLA1 脚本内的同名变量,但部分变量的作用域和初始化方式可能不同:
- 在 Tools/Scripts/SHBuild-YSLib-common.txt 中直接初始化变量
SHBuild_Env_Arch
和SHBuild_Env_OS
。 - 在 Tools/Scripts/SHBuild-YSLib-common.txt 的函数
SHBuild_GetPlatformStrings
(详见版本库中的doc/NPL.txt
)的调用中初始化变量SHBuild_Host_Arch
和SHBuild_Host_OS
。
注意在 NPLA1 脚本中创建的变量不是环境变量;但和 shell 脚本类似,若需作为其它外部命令的环境变量,可先导出再调用对应的程序。
NPLA1 脚本可提供部分 shell 脚本中的同名函数。除非另行指定,这些函数的调用接口以及功能和 shell 脚本中的同名函数一致,但实现和以下行为不保证相同:
- 出错时的诊断方式。
- 缓存的变量及相关的输出提示信息。
- 性能。
- 影响的非公开环境变量等其它外部环境状态。
作为公开接口的 NPLA1 函数另见 doc/NPL.txt
中的描述。
一些开发脚本被用于构建。构建脚本的一般形式提供配置和生成阶段的自动化功能。
配置阶段设置交互环境。典型地,通过执行命令前设置的环境变量指定可配置项。
生成阶段调用合适的工具完成构建。
配置阶段的接口可能对外隐藏,此时使用默认配置进行构建。
一些公用的构建脚本可适用于整个项目。其它的构建脚本可构建具体子项目中的目标。
构建脚本可以是 makefile 或其它可执行的脚本。其中,makefile 可能使用以上约定含义的 shell 环境变量,但不保证可被外部指定覆盖脚本中指定的默认值。
- YSLib 项目文档
doc/Dependencies.txt
了解组织结构、开发规则、默认使用的外部依赖项(包括语言实现)和相关约定。 - 结构和特性 中的树形结构了解项目依赖性。
- YSLib 项目文档
doc/ProjectRules.txt
了解组织结构、开发规则和相关约定。 - YSLib 项目文档
doc/YBase.txt
了解顶级子项目 YBase 。 - YSLib 项目文档
doc/YFramework.txt
了解顶级子项目 YFramework 。 - YSLib 项目文档
doc/YSLib.txt
了解 YFramework 的次级子项目 YSLib 。