YSU
总览

版本号规则

项目里有三个互不相干的版本号,混起来会出问题,所以分开放。

项目里有三个互不相干的版本号,混起来会出问题,所以分开放。

产品版本

仓库根的 version.toml 是唯一来源:

[version]
kernel_version = 21.0
browser_version = 0.1.0
jse_version = "v9.0.1.0"

[settings]
edition = "release"

browser_version 是外壳显示的那个号,也是 ysu_engine_version() 返回的值。外壳不硬编版本号,它问内核要——两处各写一份迟早会飘。

这个文件编译期被 include_str! 嵌进程序。嵌进来而不是运行期读文件,因为版本号是构建期就定死的东西,程序不该因为找不到一个文本文件而起不来。

清单是人手写的,所以解析器手写了一小段,不引 TOML 库:跳过注释与空行,认段头,只取 [version] 段里那个键。键要整段相等,不能只比前缀——清单里同时有 browser_versionversion,按前缀找的话 kernel_version 也会被当成 version

读不出来或写坏的时候退回 Cargo 的版本号。版本号显示得不理想是小事,为此起不来是大事。

这个文件由人维护,不由工具生成,也不由自动化改写。

C ABI 接口版本

YSU_CAPI_VERSION 是另一回事。它只在函数签名变化时递增:改了参数、改了返回类型、加了新的导出函数。

当前值是 2。它定义在两个地方,必须同步:

  • ui/include/ysu_capi.hpp 里的 #define YSU_CAPI_VERSION 2
  • crates/ysu-capi/src/lib.rs 里的 const VERSION: u32 = 2

外壳启动时做一次比对,不一致就弹一个对话框,然后退出,退出码 1。这是故意的:参数错位的调用比退出危险得多——C 的调用约定下,被调方会把数据指针当成回调函数指针用,一调用就崩,而且崩得没有线索。

比对发生在 QApplication 建好之后、主窗口建出来之前。内核侧不做校验,只如实报告版本号。

产品版本变了不一定动这个号。加一个不改变任何签名的内部实现、改文案、改默认样式表,都是产品版本的事,与 ABI 无关。

三个组件各自的号

kernel_version 是 YSU 的号,jse_version 是 JSE 的号。它们各记各的进展,互不牵制。

目前只有 browser_version 会被显示和返回。另外两个先在清单里占着位置——哪天外壳要分行列出来,再单独开一个口子,而不是现在就搭一个用不上的接口。

什么算破坏性变更

会动接口版本的:

  • 导出函数的参数个数、类型、顺序变化
  • 导出函数的返回类型变化
  • 删掉导出函数
  • 枚举出参的取值含义变化(比如 ysu_resolve_addresskind

不会动的:

  • 新增导出函数(旧调用方不受影响)
  • 函数内部行为修正
  • 错误信息文案变化
  • 渲染结果的样式变化

新增函数不动版本号,但头文件与实现要一起改,签名一致性测试会盯着这件事。

三道闸

签名错位这个问题在 C 的调用约定下编译器看不见,所以有三道人工设的闸:

  1. 接口版本号。启动时对一次。它拦得住「外壳链的静态库是旧的」。
  2. 签名一致性测试。把 ui/include/ysu_capi.hppcrates/ysu-capi/src/lib.rs 逐个函数对参数个数。它拦得住「改了实现忘了改头文件」——这种情况版本号天然一致,反而看不出来。
  3. 版本号本身的测试。一条钉住头文件里的宏值等于库里的常量,一条钉住当前值是 2。

第 2 道闸只比对参数个数,不比对类型。类型对不上要靠人工看——这是它的边界,写在测试的注释里。

发布节奏

修订号(0.1.00.1.1)是普通修复,次版本号是成规模的功能迭代,主版本号留给不兼容的大改。内核自己的 kernel_version 用的是同样的口径。

版本号在哪里被读

位置读什么用途
ui/src/main.cppysu_engine_version()QApplication 的版本
ui/src/main.cppysu_capi_version()与头文件里的宏比对
外壳标题栏内核交回的页面标题与版本号无关

On this page