# 准备 * 参见[构建](Build.zh-CN.md) * 参见[开发说明](Development.zh-CN.md) * 配置满足[先决条件](Prerequisitions.zh-CN.md)的对应平台运行环境 # 平台环境   以下约定适用包含 YSLib 程序和其它程序的构建和运行环境的规则。   并非每一个程序都需要区分特定[平台](Terminology.zh-CN.md#平台),一些程序的行为是[平台中立](Terminology.zh-CN.md#兼容性和可移植性)的。 **注意** 特定平台可能有附加的限制和被支持的配置,详见[各个平台运行时环境](#特定平台运行环境)的说明。 ## 结构要求   YSLib 程序及其组件的构建时依赖项应兼容运行环境: * 除非另行指定,运行时[目标平台](Terminology.zh-CN.md#依赖和外延)的[系统软件环境](Terminology.zh-CN#平台标识)应和构建时的配置兼容。 * 这蕴含程序运行时组件及其依赖项(特别地,[系统库](Terminology.zh-CN#平台标识))的版本和配置应匹配,或其偏差在受到支持的范围内。 * 配置包括构建配置和运行时指定的参数。 * **注释** 例如通过命令行或环境变量分别在构建时和运行时指定的选项以及[配置文件](#外部文件)等。 * 构建配置可允许选择支持的目标平台。 * 配置可改变和默认的预期的目标平台中的系统软件环境。 * **注释** 程序实现被建议但不一定提供可选的配置。缺少配置选项可能削弱可移植性,如[要求使用的 API 不被较旧的运行环境支持](https://github.com/boostorg/filesystem/issues/172)。 * **原理** 配置可指定不同的[平台环境](Terminology.zh-CN.md#平台配置)而影响可移植性。修复不受支持的偏差可能需要重新构建程序并重新部署。提供兼容的预期以减少这类情形造成的影响。 * 作为完整性和安全保证的一部分,程序可检查程序组件或外部依赖项存在和版本,以确保环境符合预期。 * 若违反假定,则程序的行为未定义,但不得违反任意提供运行环境完整性和数据可用性的显式安全保证。 * **注释** 技术上,程序具有未定义行为。保证通常由宿主环境或其它平台环境实现机制对影响的资源范围进行限制而实现。 * **注释** 一般地,在可行时,建议程序的实现进行基本的检查明确拒绝非预期的环境,提供最小可预期的行为,以利于用户简化排查部署错误。 ## 硬件资源要求   除非另行指定,YSLib 程序及其组件构建和运行时占用的硬件资源不确定,取决于平台配置以及具体应用程序。 ## 文件系统   YSLib 项目对文件系统的使用满足以下约定。YSLib 程序可以使用这些约定作为默认的假设。 ### 文件系统特性   YSLib 项目仅依赖具有足够可移植性的文件系统特性: * **注释** 这些特性的可移植性具有相对性。 * **注释** 一般地,和 ISO C++17 定义的能兼容 [POSIX](https://eel.is/c++draft/fs.conform.9945) 和[特定操作系统](https://eel.is/c++draft/filesystems#fs.conform.os)(如 Microsoft Windows )的 [`std::filesystem`](https://eel.is/c++draft/filesystems) 支持的文件系统特性集合以及[兼容两者的文件名](https://www.boost.org/doc/libs/1_84_0/libs/filesystem/doc/portability_guide.htm)被视为可用。 * 若不可用,则使用回退(fallback) 实现且不引起其它行为的改变,而不需要在接口中显式依赖: * (非目录文件的)硬链接。 * 在不支持的硬链接的文件系统中,链接数总是 1 。 * 符号链接。 * 在不支持的符号链接的文件系统中,文件不是符号链接,总是不解析符号链接。 * 假定总是可用: * ISO/IEC 14882:2011 中的基本执行字符集中的可打印字符构成的文件名,但除非另行指定: * 避免大小写不敏感时引起重名。 * 排除 DOS [保留名称](https://learn.microsoft.com/windows/win32/fileio/naming-a-file)。 * 支持以 `char` 编码的本机路径的长度不少于 `MAX_PATH`(当实现的 API 中存在时)和 `31` 中的较大值。 * (不超过文件路径长度限制时的一级或多级)子目录。 * **注释** MS-DOS 2.0 前的文件系统不支持子目录,但当前总是普遍可用。 * 当实现可能支持时,允许可选引入(而不唯一依赖)特定的可能依赖具体文件系统的实现的特性: * 被特定的平台可选引入,仅作为优化实现,不引起其它行为的改变,而不需要在接口中显式依赖: * 可选地使用不属于 POSIX.1 等标准文件系统 API 支持的元数据。包括但不限于: * [`d_type`](https://www.gnu.org/software/libc/manual/html_node/Directory-Entries.html) : * [MinGW-w64](https://www.mingw-w64.org/) 不支持,但 [MinGW.org](https://osdn.net/projects/mingw) 支持。 * 许多文件系统在 Linux 上的实现支持,但 [ReiserFS](https://github.com/ggreer/the_silver_searcher/issues/36) 和 [XFS](https://docs.oracle.com/en/operating-systems/uek/4/relnotes4.6/uek4.6-KnownIssues.html) 不支持,结果总是 `DT_UNKNOWN` 。 * 支持超过 POSIX 或 Win32 的 `MAX_PATH` 长度限制的特定路径。 * [Windows 长文件名(LFN, long filename)](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation)。 * 在操作不支持时引起运行时错误或取得指定不支持情形的结果,包括但不限于: * [文件创建时间](#时间戳)的查询和修改在不同平台具有不同的支持: * Win32 在使用 FAT 和 NTFS 等文件系统时完全支持,但不同操作和在不同文件系统上使用的文件时间的[精度不保证相同](https://learn.microsoft.com/windows/win32/api/minwinbase/ns-minwinbase-filetime)。 * Linux 在使用 ext4 时可通过 [`statx`](http://man7.org/linux/man-pages/man2/statx.2.html) 读取,但不支持写入。 * 对其它文件系统特性保持中立。 * **原理** 允许最终用户修改文件系统运行时配置(如挂载参数)而不破坏 YSLib 程序的可用性。 **注释** 文件系统特性不是唯一限制文件系统路径可用性的因素。参见以下[文件系统路径](#文件系统路径)的规则。   一般地,YBase 不要求文件系统访问;使用 YFramework 框架的程序,由 YFramework 中的[框架初始化](#框架初始化)等逻辑引入文件系统访问。 ### 文件系统路径   为最大化可移植性,除满足[文件系统特性](#文件系统特性)的要求,YSLib 项目中的程序及其组件使用的文件系统路径同时具有以下约束: * 逻辑**不依赖自身组件具有显式编码的具体文件路径**,且默认**不要求在具体系统约定的周知(well-known) 的文件系统位置**,除以下例外: * 为实现接口约定的功能,特定平台环境中的实现可能隐式地依赖特定的文件系统路径。 * **注释** 例如,Linux 平台的实现可能依赖 `/proc` 访问进程相关的运行时信息。 * 具体安装配置可以自行限定外部文件的[目录布局](#文件系统布局)作为默认运行时环境,且外部文件可约定默认路径。 **注释** 系统约定的路径如 [Windows DLL 搜索路径](https://docs.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order)中的目录和 [FHS](https://zh.wikipedia.org/zh-cn/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A0%87%E5%87%86) 定义的目录。 * 最小化依赖特定的文件系统和路径表示。 * 特别地,不依赖文件系统是否具有文件名的大小写敏感性。 * **推论** 为保证可移植性,文件系统路径大小写敏感,需要显式区分而加以明确。 * **原理** 这样仍可兼容 FAT 等大小写不敏感的文件系统。 * 除非特定的接口约定,不依赖可选的大小写敏感的文件系统特性。 * **注释** Microsoft Windows 10 支持默认大小写不敏感的 NTFS 配置为每目录启用大小写敏感,用于[支持 WSL DrvFS 实现](https://devblogs.microsoft.com/commandline/improved-per-directory-case-sensitivity-support-in-wsl/)等场景。 * **注释** [JFS 提供选项](https://www.systutorials.com/docs/linux/man/8-jfs_mkfs/)以支持 OS/2 的大小写不敏感的文件系统特性。 * **注释** [Linux 5.2 起支持默认大小写敏感的 ext4 配置为每目录启用大小写不敏感](https://lore.kernel.org/lkml/20190507232823.GA28416@mit.edu/),可用于提升 WINE 应用性能等场景。 * 使用的路径应能支持符合 YSLib 项目规范的开发(参见项目的 `doc/ProjectRules.txt` )。 ### 时间戳   文件系统可支持不同的文件时间戳,以记录和文件系统操作有关的事件发生的时间。   典型地,文件系统支持文件修改时间(modification time) 和访问时间(access time) 。但有的时间戳仅在部分实现中支持,如创建时间(creation time) : * 部分文件系统支持创建时间;但其它文件系统不支持保存这种元数据。 * **注释** 例如,NTFS 和 ext4 原生支持保存文件创建时间。 * 部分实现提供支持访问文件系统时间;但是其它实现支持有限。 * **注释** Windows NT 和 Win32 可支持读取和写入文件修改时间的 API 。 * **注释** POSIX 没有原生支持文件系统的访问时间,但可以通过特定的原生方式提供,例如: * Linux 支持通过 `statx` 系统调用取得 ext4 等文件系统中保存的文件创建时间戳,但不支持修改。 * BSD 支持保存创建文件,不支持修改。 * **注释** 相同特性的名称在不同实现中不同: * 在 FAT 和 NTFS 称为 ctime(注意这和 POSIX 的 ctime(change time) 不同)。 * 在 JFS 和 Btrfs 称为 otime 。 * 在 ext4 称为 crtime(creation time) 。 * `statx` 支持 btime(birth time) 。 * BSD 支持 birthtime 。 * 另见[这里的讨论](https://lwn.net/Articles/397442/)。   程序应注意可移植性限制,避免依赖错误的假设: * 即使是被支持的时间戳,精度可能也会受到文件系统设计的限制。 * **注释** 例如,NTFS 支持微秒级的时间戳,但 FAT32 文件系统只支持到秒级的时间戳。 * 此外,文件系统的实现(操作系统的驱动)可能还会对时间戳进行一些调整或优化而具有不同的行为: * 因为时间戳的历元未指定,涉及时区或时间转换时,具有未指定的语义。 * **注释** 例如,NTFS 会将时间戳存储为 UTC 时间,在 Windows 上自动调整为本地时间。 * 一些文件系统在挂载(mount) 时可(通过用户指定的选项)调整行为。 * **注释** 例如,Linux 的 ext4 文件系统可通过选项来选择是否使用 UTC 时间戳。 * 因为性能原因,文件系统的实现更新访问时间戳可能不及时。 ## 二进制依赖项   按以上[文件路径约定](#文件系统),构建得到的目标程序没有预设特定的位置限制,可按需转移。   运行程序前,应确保程序能找到二进制依赖项: * 确保[先决条件](Prerequisitions.zh-CN.md)中的依赖项可以在 `PATH` 环境变量的目录被找到,或者复制到程序所在的目录。 * 对于静态链接 YFramework 和 YBase 程序,不需要其它二进制程序文件的部署。 * 使用 DLL 的程序可能依赖 YFramework.dll 和 YBase.dll ,或者 debug 配置的 YFrameworkd.dll 和 YBased.dll 。这些库已经在 sysroot 的 `bin` 目录下安装,因此可以直接把 `bin` 目录加入 `PATH` 环境变量,而不必复制或移动库文件。 **说明** 在任意被支持的平台上,YSLib 避免使用导致在特定平台上被特殊处理且无法可靠避免这种行为而确保兼容性一致的外部依赖项名称(若因为外部环境更新导致此类问题,则需在 YSLib 的新版本适当修改后支持)。例如,支持 [API Set Schema](http://www.geoffchappell.com/studies/windows/win32/apisetschema/index.htm) 的 Windows 版本加载 DLL 时,[`LdrLoadDll` 解析以 `api-` 或 `EXT-` 起始的文件名进行重定位](https://stackoverflow.com/a/47530043/2307646) ,因此设计时确保外部依赖项对应的文件名不以这些前缀起始。 **注意** 当前没有二进制兼容保证。使用不兼容的二进制文件的程序实现的行为未定义。特别地,使用不同工具链乃至编译或链接时仅仅使用了不同的构建选项可能生成互不兼容的二进制文件。使用这些文件时应当确定使用的源代码版本和构建环境匹配。例如,在支持动态库的 `POSIX` 平台,应当确保 `PATH` 和 `LD_LIBRARY_PATH` 这些影响加载程序二进制文件位置的环境变量被适当配置,以确保实际使用的二进制文件之间兼容。 ## 兼容平台 **注意** 示例程序最大化地使用了 YSLib 的[平台模拟](Development.zh-CN.md#平台模拟)特性,支持特定的[目标平台](Development.zh-CN.md#平台)在运行时作为[宿主平台](Development.zh-CN.md)对另一平台的平台模拟,即运行的原生示例程序和被模拟平台的原生程序保持基本相同的功能效果;但并不保证所有其它 YSLib 程序同样支持。   被正式支持的平台模拟列表如下: * PC 模拟 DS   被非正式支持的平台模拟列表如下: * Android 模拟 DS # 运行时交互   以下约定适用程序运行时可能进行交互的运行环境的规则,涵盖以下程序: * 通过用户界面运行 YSLib 项目中的程序。 * 以上方式间接运行的其它程序。 * **注释** 例如,在程序中调用[脚本](Development.zh-CN.md#脚本),或者程序使用基于 `std::system` 等本机 [API](Terminology.zh-CN.md#程序设计语言) 实现的互操作。YSLib 安装程序可能间接调用其它程序。   用于 YSLib 自举构建的工具(如 [SHBuild](Tools/SHBuild.zh-CN.md) )在运行时也满足以上要求,因此这类情形的构建时也满足上述要求。 ## 环境变量   [环境变量](https://zh.wikipedia.org/zh-cn/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F)是程序的实现环境(典型地,如操作系统)的运行[环境](Terminology.zh-CN.md#环境)提供的一种机制。程序在运行时可能访问这些环境变量,以提供不同的行为,或在程序之间、程序与外部环境之间传递信息。 **注释** 非宿主实现的平台可能不支持环境变量。本项目的程序在这些平台上不使用环境变量。   没有依赖这些具体应用或其提供的实现环境时,如有可能,应当避免使用依赖这些具体的使用方式。   一些情形程序通过判断特定的环境变量配置功能。特别地,设置特定的环境变量为非空值可能启用功能。   一般地,只要环境变量被支持,一个环境变量总是能被设置为非空的字符串(至少本项目支持的平台能假定如此)。但是,访问环境变量的行为仍可能依赖具体平台的不同细节,而影响可移植性: * 环境变量的名称可能是大小写敏感或不敏感的:名称仅有大小写不同的环境变量可能被视为不同或同一个。 * 例如,Windows(指 Win32 环境,下同)的环境变量名对大小写不敏感。其它大部分实现环境对环境变量的大小写敏感。 * 因此,作为公开接口提供的环境变量应当总是具有固定的、大小写敏感的拼写,且避免出现仅有不同大小写的可能因同名冲突的不同环境变量名。 * 环境变量可被设置为空值。具有空值的环境变量可能会或不会被视为没有被设置(或被取消设置)的环境变量。 * 例如,设置 Windows 的某个环境变量为空值,效果即删除环境变量。其它大部分实现环境中,设置环境变量为空值,环境变量不被删除。 * 一些环境可能指定不同的行为,如[较新版本的 MSYS2 支持配置 `MSYS=noemptyenvvalues`](https://www.msys2.org/news/#2022-09-24-changed-behavior-for-empty-env-vars) 。 * 特定的实现被设置的环境变量具有空值,如 [PowerShell](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_environment_variables) 。 * 为避免这些不同实现的复杂性,不直接判断环境变量被设置,而访问环境变量并检查是否为空值。未设置的环境变量被视为具有空值。 * 环境变量名可能具有不同的限制。 * 仅假定 ISO C 基本字符集的标识符(大小写拉丁字母、数字和下划线 `_` )可被用于环境变量名。 * 具体环境变量的访问方式可能和具体宿主平台中访问环境变量的具体程序相关,如: * [POSIX shell 变量](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_03)引用 [XBD 环境变量](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08),可使用 `$VAR` 访问变量 `VAR` 的值。 * [Windows `set` 命令](https://learn.microsoft.com/windows-server/administration/windows-commands/set_1) 显示或设置环境变量,可使用 `%VAR%` 访问变量 `VAR` 的值。 * [Powershell 支持环境变量](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_environment_variables),可通过 `$Env:VAR` 访问变量 `VAR` 的值。 * 以下*周知的(well-known)* 环境变量因在各个被本项目支持的宿主实现平台普遍可用,而可被直接使用,但使用方式并非完全相同: * **原理** 以下约定简化可移植的使用。 * [`PATH`](https://zh.wikipedia.org/zh-cn/PATH_%28%E5%8F%98%E9%87%8F%29) 表示程序中执行(外部程序提供的)命令使用的搜索路径。 * 对 Windows ,另见 [`path` 命令](https://learn.microsoft.com/windows-server/administration/windows-commands/path)(其中使用 `%PATH%` 引用环境变量 `PATH` )。 * 另见 [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01) 的规定。 * `PATH` 的值可支持多个路径名称,其中分隔符不一定相同,如 Windows 使用 `;` ,而 POSIX 使用 `:` 。 * Windows 可能直接提供 `Path` 环境变量,而在 `cmd.exe` 中仍然被识别为 `PATH` 。此时,使用 `PATH` 。 * [环境变量 `COMSPEC` 或 `ComSpec` (en-US)](https://en.wikipedia.org/wiki/COMSPEC) 指定命令解释器。 * Windows 可能直接提供 `ComSpec` 环境变量,而在 `cmd.exe` 中仍然被识别为 `COMSPEC` 。此时,使用 `COMSPEC` 。   除非需要依赖具体实现环境或另行指定,本项目中提供的环境变量应当避免上述依赖差异。 ### 应用程序变量   应用程序继承环境,可能根据环境变量指定不同的行为。   除非另行指定,以下环境变量若被本项目提供的程序支持,则具有一致的含义: * `NO_COLOR` :若这个环境变量被设置为非空值,宿主实现环境忽略终端支持的彩色和其它视觉效果格式(如下划线)特性。 * **注释** `NO_COLOR` 的检查符合 [no-color.org](https://no-color.org/) 的约定。 * **注释** 一些命令解释环境的实现直接支持 `NO_COLOR` ,如 [PowerShell](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_environment_variables)。   除标准库和系统配置外,YFramework 可使用特定的环境变量改变运行时行为,参见 YSLib 项目文档 `doc/YFramework.txt` 。   除非另行指定,程序可假定以上环境变量不变,即仅在程序初始化时检查一次。   提供给程序的环境变量可能被命令行工具的选项覆盖,如 [SHBuild 构建模式](Tools/SHBuild.zh-CN.md#构建模式)中指定变量定义的选项 。 ## 多任务环境   除非另行指定,通过 YSLib 安装程序调用的程序或者依赖 YSLib 的命令行程序假定以下的外部环境和交互方式: * 非[宿主环境](Terminology.zh-CN.md#环境):执行其它程序的*宿主(host)* 未指定。 * 被视为默认不支持外部程序。 * **注释** 程序使用 `std::system` 等实现定义的行为可能导致非预期但可被定义的结果。 * 宿主环境: * 以被执行的当前程序或脚本解释器作为执行其它程序的宿主。 * **注释** 一般由操作系统在用户空间规范提供约定,且系统的安装提供完整的环境。例如,Windows 提供 `cmd.exe` 作为命令行解释器,而 UNIX 系统提供 shell 程序解释 shell 语言命令和脚本。 * 以宿主定义的规则解析命令行参数。 * Windows :命令行[由程序实现指定](https://daviddeley.com/autohotkey/parameters/parameters.htm#WINPASS)。对未指定宿主程序的情形,视为和 `cmd.exe` 一致。 * **注释** Windows 可通过 shell 程序传递命令,这*不是*默认假定的环境。 * Windows :对引号的解析,兼容[未被文档指定的新规则](https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESDOC),即仅在引号有效地块中 `""` 转义未 `"`。 * 使用[环境变量](#环境变量)的约定。 * 若[环境变量 `SHELL`](https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap08.html#tag_21_08_03_03) 被设置,则: * 假定被正确设置至支持的 shell 程序的路径。 * 假定 [POSIX.1-2004 环境的命令](https://pubs.opengroup.org/onlinepubs/009695299/utilities/contents.html)可用。 * **注释** 在 shell 环境内部通常不需要检查。可能使用其它 POSIX 兼容 shell 兼容 POSIX shell 或 GNU bash 的运行环境。 * 非 Windows 宿主平台:使用 POSIX shell 或 GNU bash 作为执行环境的 shell 。 * Windows :使用随系统分发的 `cmd.exe` 作为命令行解释器。 * 不显式依赖[环境变量 `COMSPEC` 或 `ComSpec`](#环境变量)。 * 对使用 [MSYS2](https://www.msys2.org/) 或与之兼容的环境,使用[环境变量 `MSYSTEM`](https://www.msys2.org/docs/environments/) 指定活动的环境和子系统。 * 不要求支持嵌套不同的命令行环境最终重入到非 POSIX 兼容 shell 的情形。 * **注释** 若 `SHELL` 被设置为非空值,即视当前 shell 是 POSIX 兼容 shell 。 * **原理** 被继承的环境变量检查可能嵌套的命令行环境不可靠。可靠检查通常依赖进程间通信确定当前的程序,为减少复杂性,不被要求。 * 以上环境变量在相关规范定义明确允许的情形以外,可被程序假定不被修改。 * 运行其它程序时,不检查或隔离外部环境。 * **警告** 尽管 YSLib 项目提供的程序的大部分实现会避免明显的安全性问题,任何可能执行第三方程序仍然可能有潜在的安全性风险。因此,在完全审计环境并确保外部环境可信之前,**不建议使用特权运行包括 YSLib 安装脚本内的这些程序**。 * **注释** 利用方式如拦截在程序中已知会调用的外部命令(包括命令解释器),以及使用环境变量 `IFS` 等特定外部命令的机制注入。 * 使用包含带有 Windows 环境变量展开语法(以 `%` 起始和结尾)的参数未指定。 * **原理** Windows 命令行解释器脚本可使用 `%%` 转义,但这对交互式命令行环境的调用不适用。因此,包含这种形式的参数可因是否使用 Windows 命令行解释器表现不同的行为。 * **注释** [NPLA1 脚本](Development.zh-CN.md#npla1-脚本)可调用依赖 Windows 命令行解释器执行的命令。 * 除非被功能蕴含或另行指定: * 不和程序处理的外部对象或外部程序执行时的安全机制或其它附加元数据的机制显式交互。 * 外部对象可能是文件系统对象或其它对象。 * 安全机制可能是对象具有的权限(permission) 或权能(capability) 。 * 附加元数据在 API 的意义上明确,如依赖具体文件系统的扩展属性(extend attribute) 。 * 交互包含对状态的访问,如检查和配置。 * 若有必要使用: * 尽量使用可确定不具有全局(整个系统)影响的方式。 * 当不能避免全局影响时,使用影响较小的方式。 * **注释** 系统可能具有对用户可见的默认设置。例如,POSIX 进程可继承[文件创建掩码](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12),且可被用户通过 [`umask`](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/umask.html) 设置。 * **原理** POSIX shell 和 `cmd.exe` 是平台默认分发的应用,通过[构建平台或宿主平台](Terminology.zh-CN.md#依赖和外延)直接区分是可行的。 * 同时,一些语法兼容,使有些命令不需要区分两者。即便在一些基本语法上(如重定向错误流)不完全兼容,默认仍然可以通过特定的环境区分两者。 * 环境变量 `SHELL` 在不同实现的支持情形不同,不适合提供统一的假定: * 环境变量 `SHELL` 在 [POSIX.1-2017](https://pubs.opengroup.org/onlinepubs/9699919799/mindex.html) 的 [基本定义中出现](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08),而其含义决定若这个环境变量被定义,其值非空。[相关原理](https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap08.html#tag_21_08_03_03)解释了 `SHELL` 具有可被系统配置为非 POSIX shell 的含义。 * 环境变量 `SHELL` 不是[被定义的 shell 执行环境](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_03)的一部分,不被要求影响 shell 的执行,但仍能判断系统配置。 * 环境变量 `SHELL` 的值通常被初始化环境的启动程序(如传统的 `login`,仍被 [BSD](https://www.unix.com/man-page/posix/1/login/) 和 [Linux](https://man7.org/linux/man-pages/man1/login.1.html) 等使用)设置。 * GNU bash [确保启动时 `SHELL` 的值被设置](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-SHELL)。 * 其它兼容 POSIX shell 可使用环境变量 `SHELL` 执行命令,但通常不设置它的值(如 [Korn shell](https://www.ibm.com/docs/aix/7.1?topic=shell-variables-used-by-korn-posix))。 * 环境变量 `COMSPEC` 和 `ComSpec` 缺乏标准化,且会被继承。 * 这通常仅用于判断在 Windows 同时使用兼容 POSIX shell 环境。 * Windows 可能使用嵌套的兼容 POSIX 的 shell 而同时继承 `COMSPEC` 且被设置 `SHELL` 的情形。 * 为避免复杂性,一般避免单独检查这些环境变量。 * C 运行时实现使用 [`std::system` 和 `::_wsystem`](https://learn.microsoft.com/cpp/c-runtime-library/reference/system-wsystem) 实际可能总是依赖 `COMSPEC` 和 `cmd.exe` ,因此成功互操作时已经隐含依赖了这项运行时配置。 * 安全机制、附加元数据及其交互方式往往依赖具体外部环境,可移植性较低。除外部环境已明确的默认配置,仅在必要时使用。 * 一般地,这也适用于不同机制之间的比较。例如,若有必要使用不同上下文的文件系统附加属性,一般默认[仅在用户空间使用](https://www.freedesktop.org/wiki/CommonExtendedAttributes/)。 * **注释** [PowerShell](https://learn.microsoft.com/powershell/) 、[PowerShell Core](https://github.com/PowerShell/PowerShell) 或 [`pwsh`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pwsh) 默认不被支持。 * **原理** PowerShell 和上述命令行解释器支持的语法都不兼容,且不直接提供简便可靠的可移植的方式检查差异。 * **注释** 可靠检查 PowerShell 通常依赖[进程间通信](https://stackoverflow.com/questions/55597797)。   关于 YSLib 版本库内的及安装 YSLib 部署的脚本的运行环境,另见[脚本运行](Development.zh-CN.md)的相关说明。 ## 外部文件   外部文件包括: * 在文件系统中部署的外部[二进制依赖项](#二进制依赖项)。 * 配置文件:可被修改以改变程序的运行时行为。 * 其它数据文件。   数据文件提供程序运行时所需的必要或可选的信息。配置文件可能作为数据文件使用。   外部文件的路径应符合前述关于文件路径的约定。 ### 文件格式   除非另行指定,使用以下文件格式: * 配置文件是内容为 [NPL](Features/NPL.zh-CN.md) 配置的 UTF-8 文本文件,具有 UTF-8 BOM 。 * 其它数据文件为二进制文件 。 ### 文件系统布局   除[配置文件](#外部文件)可能指定默认的具体位置(详见[下文](#配置路径))外,YFramework 程序不需依赖特定的文件系统布局。   除非另行指定(暂无),YFramework 不创建文件系统目录。   [Sysroot](Sysroot.zh-CN.md) 使用类似 [FHS](https://zh.wikipedia.org/zh-cn/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E6%A0%87%E5%87%86) 的文件系统布局,称为*局部 FHS 目录布局(local FHS directory layout)* 。 **原理** 在不同环境下都可使用一般类 UNIX 系统使用的布局,有助于减少安装后运行时环境的差异,便于部署。 ## 其它外部环境交互 * 在功能明确的情形以外,除非另行指定,运行时不通过文件系统以外的方式访问远程资源。 * **原理** 隐式访问网络资源可具有不可预期的开销,影响用户体验。 # 特定平台运行环境 ## DS   以下说明中,`$NDSEmulator` 为模拟器 [DeSmuME](DeSmuME.en-US.md) 可执行文件的绝对路径,`$TargetPath` 为 ROM 映像文件( `.nds` 文件)的路径,$FAT_Path为 FAT 目录路径(见以下说明)。   之前经测试的最小版本为 0.9.6 。建议使用最新版本。   运行时,需在运行模拟器的宿主机中准备一个目录作为映射到设备中的虚拟 FAT 目录。宿主的目录在运行中不会被 DeSmuME 修改;宿主目录的文件系统不要求为 FAT 。运行时会加载虚拟目录的内容。 **注意** 官方版的 DeSmuME 不支持包含非 ASCII 字符的宿主路径而忽略这些文件。部分修改版可能没有这个限制。 **注意** 因为 DS 模拟器和烧录卡需要加载 ROM 映像到 RAM 后才能运行,映像大小会影响可用运行内存,使用 debug 配置构建的 YSTest 示例版本不一定在 DS 上支持所有功能。相关支持限制及变更详见 YSLib 项目文档 `doc/Test.txt` 。   可通过以下方式之一指定 FAT 目录。 ### Slot 1 映射   Slot 1 (即 NDS ROM 卡槽,硬件介质可以是 NDS 卡带,或烧录卡和存储卡的组合)的模拟从 DeSmuME 0.9.8 版本起支持。   运行命令行示例: ```shell "$NDSEmulator" "$TargetPath" --preload-rom --slot1-fat-dir="$FAT_Path" ```   其中 `--preload-rom` 对 0.9.10 和 0.9.11 版本是必须的(以支持自动 [DLDI(en-US)](https://wiki.gbatemp.net/wiki/DLDI) 补丁),否则加载失败。这个选项可以在模拟器(Windows 版本)的 GUI 菜单中 `Config` → `ROM Loading` 中选择。最新版本的源码会对自制 ROM 自动启用 ROM 预先加载的选项而不用另行配置,可以省略这个选项。 ### Slot 2 映射   Slot 1 (即 GBA ROM 卡槽,硬件介质可以是 GBA 卡带或其它扩展)的模拟需要配置为使用 `GBA Movie Player (Compact Flash)` 。   运行命令行示例: ```shell "$NDSEmulator" "$TargetPath" --cflash-path="$FAT_Path" ```   对 0.9.10 和 0.9.11 版本,需同 Slot 1 映射添加 `--preload-rom` 或使用菜单设置加载选项,否则同样无法加载(尽管源码中表示 `--preload-rom` 是适用 Slot 1 而不是 Slot 2 的选项)。   这个选项也可以通过 GUI 配置。在 0.9.9 版本前,使用菜单 `Emulation` → `GBA Slot` 。自 0.9.9 版本起,使用菜单 `Config` → `Slot 2 (GBA Slot)` 。 **注意** DeSmuME svn4030 及之后的一些版本(至少包括 0.9.8 )会在载入 MPCF 映像时错误地忽略 `--cflash-path` 选项,以 仿真(Emulation) → GBA 插槽(GBA Slot) 菜单中的设置或对应配置文件 `desmume.ini` 中指定正确的路径。   在 `desmume.ini` 中有效的最小配置: ```ini [GBAslot] type=1 [GBAslot.CFlash] fileMode=0 path=H:\NDS\efsroot\ ```   这里 `H:\NDS\efsroot` 是 `$FAT_Path` 的一个示例。 **注意** 自 Slot1 0.9.10 起,当 Slot 1 设置为 R4 时会覆盖 Slot 2 设置加载 R4 路径( Dev+ 版本显示自动 DLDI 补丁在 Slot 1 设置为 R4 从 `GBA Movie Player (Compact Flash)` 改为 `R4(DS) - Revolution for DS` ),因此需确保**Slot 1 设置不为 R4 ,或正确设置了 Slot 1 R4 的路径**(而改用 Slot 1 映射)。 ## MinGW32   自 build 431 起,YFramework 使用的 [FreeImage](https://freeimage.sourceforge.io/) 修改版集成 [libjepg-turbo](https://zh.wikipedia.org/zh-cn/Libjpeg#libjpeg-turbo) ,需要 CPU 支持 [SSE2](https://zh.wikipedia.org/zh-cn/SSE2) 指令集扩展。   除 2005 年前生产的硬件外绝大多数环境已经满足这个条件。当前几乎所有兼容 IA-32 的市售 CPU 都包含 SSE2 支持。特别地,支持 x86_64 指令集的 CPU 要求支持 SSE2 ,在使用 x86_64 的 64 位 Windows 上运行 32 位 Win32 程序(通过 [WoW64](https://zh.wikipedia.org/zh-cn/WoW64) )也支持 SSE2 。 ## Android   可使用 `adb` 命令安装 APK 包,如: ```shell "$ANDROID_SDK/platform-tools/adb" -e install -r "$1" ```   此处 `-e` 指定 TCP/IP 设备(通常是模拟器),若使用 USB 设备(通常是物理机器)应移除或使用较新的 `adb` 的 `-d` 选项代替;`"$1"` 指定 APK 文件路径。   之后,在 Android 的 GUI 环境下直接运行安装的程序。   若因签名变更等原因无法覆盖安装或更新,需要先卸载后再安装。 **注意** Android 安装 `.apk` 包时需要占用额外存储资源。为避免内置存储空间不足导致不直接表现安装失败(需要 `logcat` 查看)但直至运行时找不到二进制库而导致启动失败,应保留足够的空间,特别是对占用较大的 debug 配置构建的映像。保留空间的大小一般大于 `.apk` 作为 `.zip` 解压缩占用的空间。另见[此处报告的未确认的类似问题](https://code.google.com/p/android/issues/detail?id=21670)。 # 框架约定   除非另行指定,YFramework 的运行时行为适用以下约定。 ## 文件访问约定   除非另行指定,YFramework 对外部文件的访问适用本节约定。 **注意** 除非另行指定,程序访问配置文件的时机是未指定的。 ### 基本要求   应确保外部文件满足以下条件,否则运行时出错,或在另行指定(暂无)的特定情形下不保证行为符合预期: * 具有适当的(可读和可写)权限以允许程序访问其内容及元数据,或在必要时进行创建。 * 需要写入的文件所在的位置应该具有足够的空间。 ### 文件映射和内存映像   文件读写使用 YCLib::MemoryMapping ,优先使用内存文件映射。在文件无法访问时,部分文件可能使用内存映像代替。 **注意** 除非使用内存映像,应确保外部文件的内容可读;否则,可在之后引发无法恢复的错误;参见当前实现使用的 [YCLib::MemoryMapping 的注意事项](Features.zh-CN.md)。 ### 共享文件   宿主平台使用协同锁(advisory lock) 对并发文件内容访问提供有限的共享保护,避免基于 YFramework 程序并发访问外部文件时的竞争。不使用强制锁(mandatory lock) 以利于提升性能。 **注意** 除检查文件创建外,不提供相关文件系统元数据并发访问保护。 **注意** 因为锁定非强制,宿主不保证其它机制访问文件的程序(如非 YFramework 程序)在这里产生冲突,有可能破坏文件内容。用户应避免对相关文件造成此类访问的破坏性编辑。 **注意** 因为访问顺序未指定,不同 YFramework 实例可能修改相同的文件(如配置文件)而导致外部可见的影响。   未来可能添加完整的对共享文件内容修改的传递和检查的机制。 ## 配置生成和输入输出   配置文件可能提供默认内容,在外部文件不存在或读取失败时尝试创建。默认内容一般由框架直接提供。 * 自动生成并保存外部文件时需确保程序可在指定目录下创建文件。 * 具体目录视具体文件而定。 * 可在运行前部署正确的配置文件以避免自动生成。 * 若无法满足,生成保存在内存中的临时配置映像之后的修改不会保存到外部文件。 * **注意** 某些版本的 Windows (如 Windows Vista 以后的版本)在特定的目录(如 `%PROGRAMFILES%` )中创建文件默认需要管理员权限。 ## 公共配置文件   文件 `yconf.txt` 为 YFramework 程序的公共[配置](Tutorial/Configuration.zh-CN.md)文件,在框架初始化时,先于其它配置文件之前处理。若不存在,提供默认内容。其位置是预先指定的,和平台相关。详见下一章对框架初始化的说明。   为避免 YFramework 程序在运行时出错或行为不符合预期,注意确保满足前述访问约定的要求。若无法在程序映像文件所在目录(详见以下关于框架初始化的说明)创建文件,配置不能保存到外部文件。   在 Win32 上一个正确的公共配置文件的内容可能是这样的: ```yconf.txt YFramework ( DataDirectory "C:\Data\\" ) ( FontDirectory "C:\Windows\Font\\" ) ( FontFile "C:\Windows\Font\FZYTK.TTF" ) ```   这里指定的路径分别为数据目录路径、字体文件目录路径和字体文件路径。 ## 数据目录   用于存放 YFramework 程序必要的数据以及运行时的配置。   通过 `yconf.txt` 中项 `DataDirectory` 的值指定数据目录的路径。   自动生成的配置中数据目录的默认路径如下: * DS :`/Data` 目录 * Win32 :同默认生成配置文件所在的目录 * Android :SD 卡目录(自动检测同上)下的 `Data` 目录 * 其它平台:当前工作目录   当前可用数据文件位于 YSLib 存储库的 `Data` 目录下,可自行复制到数据目录。 ### CHRLib 的 GBK 转换例程初始化   对使用 CHRLib 提供的 GBK 编码转换的程序,需保证数据目录下存在数据文件 `cp113.bin` 。   对 Win32 平台,CHRLib 初始化 GBK 转换例程读取数据文件失败时,首先尝试使用 NLS 替代: * 通过读取注册表取得 NLS 文件路径,默认为系统目录下的 `C_936.NLS`(文件名大小写可能不同,这不影响加载)。 * **注意** 并非所有 Windows 安装带有指定的 NLS 文件。 * 具体支持的 NLS 文件名由注册表读取。这些文件在系统目录(`%WINDIR%\System32` 或 `%WINDIR%\SysWOW64`)存在。这些目录中是否存在注册表中指定文件的 NLS 文件首先和系统支持的语言相关。 * 新近版本的 64 位 Windows 10 可能在 `%WINDIR%\System32\C_936.NLS` 但不存在 `%WINDIR%\SysWOW64\C_936.NLS` 。由于 64 位 Windows 默认[对系统目录重定向](https://msdn.microsoft.com/library/windows/desktop/aa384187.aspx),导致 32 位系统中直接读取系统目录找不到 NLS 文件。在 build 937 前,YFramework 初始化加载 NLS 文件时不特别处理这种情况,因此 NLS 不可用。 * 若 Win32 NLS 初始化仍然失败,则 CHRLib 初始化 GBK 转换例程失败。   对其它平台,数据文件读取失败则 CHRLib 初始化 GBK 转换例程失败。   初始化失败后,使用 CHRLib 转换 GBK 编码的程序在调用转换例程时抛出异常,可能导致程序非正常退出。 ### MIME 数据   数据目录下的配置文件 `MIMEExtMap.txt` 存储 MIME 数据。若不存在,提供默认内容。 ### 字体目录和文件路径   只要其中之一有效即可(若没有成功指定字体文件路径,则默认字体文件路径不确定)。   自动生成的配置中字体文件的默认路径如下: * DS :`/Font/FZYTK.ttf` * Win32 :系统字体目录下的 `SimSun.ttc` * Android :`/system/fonts/DroidSansFallback.ttf` * Linux :`./SimSun.ttc`   自动生成的配置中字体目录的默认路径如下: * DS :`/Font` 目录 * Android :`/system/fonts` 目录 * 其它平台:同数据目录 ## 框架初始化   框架初始化服务于整个框架。   初始化的重要策略之一是在程序启动时减少不必要的初始化,以允许实现以下目的: * 减少可能的外部依赖(如不需要使用文字的程序就不初始化字体缓存,也不需要依赖外部字体文件和字体配置等)。 * 减少可能的运行时程序资源占用。 * 保留静态链接时优化去除没有调用的代码以减少二进制可执行文件大小的可能性。   按当前框架的设计,框架初始化在 DS 平台在程序运行初始阶段完成。其它平台可延迟初始化,按需调用。   并非所有 YFramework 中的 API 都要求框架初始化。以下功能隐含自动进行的框架初始化: * 使用默认字体缓存。 * 使用 MIME 数据时。   框架初始化加载配置文件,其中配置文件路径的确定方式参见以下节的说明。成功公共加载配置文件后,框架初始化检查配置文件的内容,并访问框架公共配置。若检查失败,抛出异常。   修改 YFramework 模块 Helper::Initialization 重新编译后,可修改默认设置改变初始化行为。以下行为**可能会在未来改变**。 ### 根路径   **根路径(root path)** 是框架初始化时参考的基本路径,由如下方式确定: * Win32 和 Linux(除 Android ):程序映像所在的目录。 * Android :SD 卡目录(自动按顺序检测 `/sdcard` 、`/mnt/sdcard` 或 `/storage/sdcard0` 之一)。 * 其它平台:第一次初始化时的当前工作目录。   确定根路径在框架初始化或要求确定根路径时进行。若定位程序映像,同时会解析符号链接。若定位根路径失败,抛出异常。   抛出的异常默认不被框架处理,可使程序退出。用户代码捕获特定异常可改变默认退出行为。 ### 配置路径   公共配置文件 `yconf.txt` 所在目录的路径前缀(结尾带有路径分隔符)称为框架的**配置路径(configuration path)** ,决定和平台相关的配置加载起始位置。配置路径和配置文件相对路径(对 `yconf.txt` ,即配置文件名)组合得到配置文件路径。   配置路径的确定方式和平台相关。   任意平台总能确定一个首选的配置路径。   除首选的配置路径外,一些平台还支持一个或多个不同的**后备(fallback) 配置路径**。在读写特定的配置文件时,若根路径访问失败,但存在后备配置路径,依序使用这些路径重试直至成功或全部访问失败。这样的配置文件当前包括 `yconf.txt` 。具体应用可使用初始化 API 以类似的方式加载其它配置文件。   以下是具有后备配置路径的平台中确定配置路径的顺序: * Linux(除 Android ): * 首先使用首选的配置路径。 * 若环境变量 `HOME` 的值非空,则路径 `$HOME/.YSLib/` 是后备配置路径。 * Win32 : * 同 Linux 平台的顺序(对应使用 Win32 的环境变量和路径语法,即后备配置路径 `%HOME%\.YSLib\` )。 * 若环境变量 `USERPROFILE` 的值非空,则路径 `%USERPROFILE%\.YSLib\` 是后备配置路径。   后备配置路径中若子目录 `.YSLib` 不存在则被创建。若创建失败,配置路径访问失败。   对不具有后备路径的平台,首选的配置路径总是根路径。否则,首选的配置路径由以下方式确定: * 程序映像所在的位置可推断出[局部 FHS 目录布局](#文件系统布局),则首选的配置路径为程序映像所在的目录的上一级目录的 `var` 子目录下的 `YSLib` 子目录。 * 推断文件系统布局为以 POSIX 环境变量语法表示为 `$PREFIX/`、`$PREFIX/$BIN/`、`$PREFIX/lib/` 和 `$PREFIX/share/` 这些路径前缀都存在且可作为目录访问,其中 `$PREFIX` 是程序映像所在的目录的上级目录,而 `$BIN` 是程序映像所在的目录的名称(按 FHS 通常为 `bin` ,此处不检查)。 * 这保证创建映像的可执行程序映像的目录中内容不被修改,以符合 FHS 。 * 否则,首选的配置路径为根路径。 * 确定配置路径要求确定根路径。   对不具有后备路径的平台不检查文件系统布局,也不要求实现确保程序映像路径的操作,以简化实现。 **注意** 若后备配置路径的配置文件可访问,直接使用此配置文件保存配置,不再创建配置文件。若需要恢复在首选配置路径创建配置文件的行为,需确保后备配置路径的配置文件不可访问(例如,移除所有后备配置路径的这些配置文件)。 ### 配置文件加载   加载配置文件 `yconf.txt` 时,首先按上述的顺序确定各个配置路径,每确定一个路径时访问其中的配置文件。若全部失败(如找不到可读的文件),则尝试自动生成配置并创建配置文件。创建配置文件的位置和顺序同上述确定配置路径的顺序。若创建配置文件全部失败,则放弃创建配置文件,直接使用生成的配置。   不存在配置文件时,配置不能通过 Helper::Initialization 的 API 持久化保存,尝试保存配置会失败。 ### 其它初始化   成功后,框架进一步处理公共配置文件以外的其它初始化;详见以上具体配置文件的相关章节。