Skip to content

第 27 篇:收尾——双版本验证、文档补齐与 PR 提交

本系列第 27 篇,也是最后一篇。26 篇下来,170 个测试全绿,代码翻译终于完成。但"写完代码"离"提交 PR"还有一段路:双版本验证、文档补齐、打包制品、填写 PR 模板……这篇记录收尾阶段的全部过程和踩过的坑。

一、收尾阶段要做什么

代码写完并不意味着能直接提 PR。对照共建计划的要求,还需要:

  1. 双版本验证:LTS 1.0.5 和 STS 1.1.3 都要能编译、测试通过
  2. 文档补齐:LICENSE、CHANGELOG.md、设计文档、API 文档、README
  3. 制品打包:用 cjpm bundle 生成 .cjp 制品
  4. PR 提交:填写模板、推送到官方仓库

一个一个来。

二、双版本验证——LTS 1.0.5

问题:两个版本怎么共存?

我一开始装的是 STS 1.1.3(第 2 篇),开发全程用的也是 1.1.3。现在要验证 LTS 1.0.5 也能跑,但两个版本的 SDK 安装器会互相覆盖——不能同时装两个

解决方案:NSIS 免安装解压

查了一下,仓颉 SDK 安装包是 NSIS 格式。NSIS 安装包支持静默解压到指定目录,不写入注册表,不和已安装版本冲突:

powershell
# 假设安装包在 d:\2.Project\cj-awesome\仓颉工具\
# /S 表示静默,/D= 指定解压目录
.\cangjie-sdk-windows-x64-1.0.5.exe /S "/D=d:\2.Project\cj-awesome\仓颉工具\cangjie-sdk-1.0.5"

等几秒就解压完了。进去一看,结构跟正式安装一样,cjpm.exetools\bin\ 目录下。

💡 坑 1:1.0.5 的 cjpm.exetools\bin\ 而不是 bin\。不同版本的目录结构有细微差异,别想当然。

切换版本测试

不能直接把 1.0.5 加到系统 PATH(会和 1.1.3 冲突)。我的做法是在 PowerShell 里临时设置环境变量:

powershell
$env:CANGJIE_HOME = "d:\2.Project\cj-awesome\仓颉工具\cangjie-sdk-1.0.5"
$env:PATH = "$env:CANGJIE_HOME\tools\bin;$env:CANGJIE_HOME\bin;$env:CANGJIE_HOME\runtime\lib;$env:PATH"

然后验证版本:

powershell
cjpm --version
# Cangjie Project Manager: 1.0.5

坑 2:cjpm 缓存不兼容

第一次用 1.0.5 跑 cjpm test,直接报错:

DataModelException: This data is not DataModelString

一头雾水。后来想到:之前用 1.1.3 编译过,target/ 目录里缓存的是 1.1.3 格式的数据。两个版本的缓存格式不兼容。

解决办法很简单——清掉 target/ 目录:

powershell
Remove-Item -Recurse -Force target\
cjpm test

清完重跑,170/170 PASSED ✅。

💡 经验:切换 SDK 版本后,一定要先清 target/。两个版本的编译缓存格式不同,混用会报各种奇怪的错。

三、双版本验证——STS 1.1.3 分支

LTS 验证通过后,还需要为 STS 1.1.3 创建一个独立分支。原因是 cjpm.toml 里的 cjc-version 字段不同:

  • main 分支:cjc-version = "1.0.5"(兼容 LTS)
  • feat/plist4cj-1.1.3 分支:cjc-version = "1.1.3"(STS 专用)

创建分支

powershell
git checkout -b feat/plist4cj-1.1.3

然后修改 cjpm.toml 里的版本号:

toml
[package]
  cjc-version = "1.1.3"   # 改为 STS 版本

切回 STS 1.1.3 环境,清 target/,跑测试:

powershell
Remove-Item -Recurse -Force target\
cjpm test

170/170 PASSED ✅。提交。

至此,双版本验证全部完成:

分支SDK 版本测试结果
mainLTS 1.0.5170/170 PASSED
feat/plist4cj-1.1.3STS 1.1.3170/170 PASSED

四、制品打包——cjpm bundle

STS 1.1.3 打包

STS 1.1.3 的 cjpm 支持 bundle 命令,可以把库打包成 .cjp 文件(类似 Java 的 .jar):

powershell
cjpm bundle

成功生成了 plist4cj-1.0.0.cjp(49KB)。但紧接着报了个错:

Can not load openssl library or function SHA256_Init

坑 3:cjpm bundle 与 OpenSSL 4.0 不兼容

cjpm bundle 在打包后需要用 SHA256 生成校验和。它调用的是 OpenSSL 的 SHA256_Init 函数——但这个函数在 OpenSSL 4.0 中已经被移除了(属于旧版 API 废弃)。

我尝试安装了两个 OpenSSL 发行版:

powershell
winget install ShiningLight.OpenSSL.Light   # OpenSSL 4.0.1
winget install FireDaemon.OpenSSL            # 也是 4.0.1

都是 4.0.x,SHA256_Init 已经不存在了。

💡 :cjpm 目前只兼容 OpenSSL 3.x,不兼容 4.0。但 Windows 上通过 winget 能装到的基本都是 4.0 了。

解决办法:PowerShell 手动补校验和

既然 cjpm bundle 只是缺一个 SHA256 校验文件,那用 PowerShell 自己算一个就行:

powershell
$hash = (Get-FileHash -Algorithm SHA256 "target\bundle\plist4cj-1.0.0.cjp").Hash.ToLower()
"$hash  plist4cj-1.0.0.cjp" | Out-File -Encoding utf8 "target\bundle\plist4cj-1.0.0.cjp.sha256"

这样就有了 .cjp + .sha256 两个文件,效果等同于 cjpm bundle 完整的输出。

LTS 1.0.5 打包

LTS 1.0.5 的 cjpm 没有 bundle 命令(这是 1.1.3 新增的功能)。所以 LTS 分支无法生成 .cjp 制品,这是已知限制。

五、文档补齐

对照共建计划的目录结构要求,发现缺好几个文件:

文件要求状态
LICENSE必须❌ 缺失
CHANGELOG.md必须❌ 缺失
doc/design.md必须❌ 缺失
doc/feature_api.md建议❌ 缺失
README.md必须⚠️ 内容不完整

1. LICENSE

上游 dd-plist 用的是 MIT 协议,我们也保持一致。注意要保留上游版权声明

plist4cj - A Cangjie port of dd-plist to parse and generate property lists
Copyright (C) 2026 plist4cj contributors

Based on dd-plist:
Copyright (C) 2016 Daniel Dreibrodt

Permission is hereby granted, free of charge, ...
[MIT 协议全文]

2. CHANGELOG.md

记录 v1.0.0 的变更,包含所有已完成模块和已知限制:

markdown
## [1.0.0] - 2026-07-23

### Added
- 完整移植 dd-plist 1.30 全部公开 API,170/170 单元测试通过
- 数据模型:NSObject、NSString、NSNumber、NSData、NSDate、UID、NSNull、NSArray、NSDictionary、NSSet
- 解析器:Binary / XML / ASCII
- 写出器:XML / Binary / ASCII
- 双版本适配:LTS 1.0.5 与 STS 1.1.3

### Known Limitations
- toJavaObject / fromJavaObject 反射胶水不可移植
- UID BigInteger 用 Int64 替代
- XMLLocationFilter / ByteOrderMarkFilterInputStream 已跳过

3. doc/design.md

设计文档,包含架构分层图、核心设计决策、测试策略、平台差异说明。重点解释为什么这样做

决策原因
手写递归下降解析器仓颉标准库无 XML DOM、无 SAX
Array<Rune> 替代 char[]仓颉 Rune 是 Unicode 标量值
Int64 替代 BigInteger仓颉无 BigInteger
跳过 toJavaObject依赖 Java 反射,仓颉无等价能力

4. doc/feature_api.md

所有公开 API 的方法签名表格,方便使用者查阅。

5. README.md 重写

按共建模板重写,包含:项目信息、快速开始、环境要求、API 文档、使用示例、已知限制等。

💡 坑 4:README 更新后出现了内容重复——新版内容追加在旧版后面,变成了两份 README 拼在一起。提交前一定要检查文件内容。

双分支同步

以上文档都在 main 分支创建。别忘了用 cherry-pick 同步到 feat/plist4cj-1.1.3 分支:

powershell
git checkout feat/plist4cj-1.1.3
git cherry-pick <main上的commit>

💡 坑 5:cherry-pick 可能冲突(尤其是 README.md 两个分支都有改动时)。冲突了就手动解决,或者直接 git checkout main -- README.md 覆盖过去。

六、PR 模板填写与提交

确认提交流程

共建计划的 PR 提交流程:

  1. Fork 官方仓库(AtomGit 平台 cj-awesome 组织)
  2. 从 fork 的分支向官方仓库发起 PR
  3. PR 标题必须带 【共建】 前缀

PR 信息

按模板填写关键信息:

字段内容
仓颉版本LTS 1.0.5 / STS 1.1.3
三方库名称plist4cj
上游版本1.30
上游仓库https://github.com/3breadt/dd-plist
适配路径共建

测试用例对比

测试类型上游用例数仓颉版用例数
单元测试约 120+170
通过率100%

两个分支分别提 PR

因为有两个分支,需要分别提两个 PR

PR分支仓颉版本
PR 1main → 官方 mainLTS 1.0.5
PR 2feat/plist4cj-1.1.3 → 官方对应分支STS 1.1.3

推送代码

powershell
git push origin main
git push origin feat/plist4cj-1.1.3

💡 提示:PR 绑定的是分支而不是某次 commit。如果 PR 审核期间需要修改,直接往同一个分支 push 新 commit,PR 会自动包含最新变更。

七、收尾阶段的踩坑总结

现象解决
SDK 安装冲突两个版本不能同时正式安装NSIS 静默解压到独立目录
cjpm 缓存不兼容切换版本后报 DataModelExceptiontarget/ 目录
OpenSSL 4.0 不兼容SHA256_Init 函数已移除PowerShell Get-FileHash 手动补校验和
LTS 无 bundle 命令unknown command 'bundle'已知限制,LTS 不生成 .cjp
README 内容重复多次编辑后新旧内容拼接检查并删除旧版残留
cherry-pick 冲突两分支 README 不同直接 checkout 覆盖

八、项目成果回顾

从第 1 篇到第 27 篇,整个过程回顾:

阶段篇目做了什么
准备1-5读懂规则、搭环境、认识库、规划路线、搭工程
叶子类型6-11NSObject、NSString、NSNumber、NSData、NSDate、UID、NSNull
容器类型12-14NSArray、NSDictionary、NSSet
工具与写出器15-22位置信息、BOM、三个写出器
解析器23-26格式检测、Binary/XML/ASCII 解析器
收尾27双版本验证、文档补齐、PR 提交(本篇)

最终交付:

指标数据
移植模块13 个类 + 3 个解析器 + 3 个写出器
单元测试170/170 PASSED
LTS 1.0.5✅ 170/170
STS 1.1.3✅ 170/170
共建文章27 篇
文档LICENSE / CHANGELOG / design / feature_api / README
当前状态🟡 PR 已提交,等待审核

九、最后的话

回头看,这件事最难的部分不是翻译代码本身,而是从头到尾搞清楚"到底要做什么"

一开始连仓颉是什么都不知道,到能读懂 Java 源码、能写出仓颉代码、能跑通 170 个测试、能把一个完整的开源库移植过来——这个过程虽然慢,但每一步都是实的。

给后来者的建议:

  1. 别怕慢,怕的是方向错。先把规则读懂(第 1 篇),后面才不会返工。
  2. 小步验证。每翻译一个类就跑一遍测试,别攒一堆一起编译。
  3. 善用文档。仓颉的官方文档(CangjieCorpus)是最权威的参考。
  4. 平台差异不可怕。Java 有但仓颉没有的东西(反射、BigInteger、SAX),想办法用等价方式补上或标注清楚就行。

本系列到此完结。如果对你有帮助,欢迎分享给同样想入门仓颉的朋友。PR 审核通过后,plist4cj 就会正式进入仓颉三方库生态,成为仓颉开发者可以使用的工具库。