参加仓颉内测的时候,我经常要在几个 SDK 版本之间来回切换。
仓颉当时更新很快,小版本之间也可能有不少破坏性变更。新版本发布以后旧的通常还不能直接删,手上的项目包括一些第三方依赖可能还依赖旧版,新功能要用新版试,出了兼容性问题又得切回原来的版本复现。电脑里需要配置好几套 SDK。
SDK 多了,切换就有点麻烦。PATH 和 CANGJIE_HOME 要改,cjc、标准库和运行时库也得对上版本。只要有一处还指着旧目录,接下来看到的报错就未必是代码本身的问题。
因此我写了 cjv 来管这些 SDK。它把不同版本分开放,运行 cjc 或 cjpm 时再决定用哪一套。项目也可以在 cangjie-sdk.toml 里写明版本,换台电脑或者放进 CI,不用再重新配一遍。
安装当前 LTS 只需要:
cjv install lts
cjv default lts
cjc --version
前两行安装并选择 LTS。以后仍然运行普通的 cjc。
切换工具链
cjv 初始化时会把一组代理放进 PATH。以后切换默认版本只改 cjv 的设置,shell 配置不用跟着重写:
cjv install sts
cjv default sts
cjc --version
临时试一下 nightly,也不用动默认版本:
cjc +nightly --version
cjpm +nightly build
+nightly 只对当前命令有效。
项目使用的版本可以写在仓库根目录的 cangjie-sdk.toml 里:
[toolchain]
channel = "lts-1.0.0"
cjv 会从当前目录向上寻找这个文件。项目中的 cjc 和 cjpm 都使用这里声明的版本,CI 脚本不用再单独写一份版本号。
哪些情况不需要 cjv
机器上长期只有一个固定版本时,手动配置环境更直接。没有切换版本或固定项目版本的需求,cjv 只会多加一层代理。
cjv 也不代替 cjpm。依赖解析、构建和包管理还是 cjpm 的工作;cjv 只管 SDK 从哪里来、放在哪里,以及本次命令使用哪一版。
项目要求的工具链尚未安装时,cjv 默认会自动下载。禁止联网的构建环境可以关掉这个行为:
cjv set auto-install false
关掉以后,缺少工具链或组件会直接报错。
安装
Linux 和 macOS:
curl -sSf https://cjv.zxilly.dev/install.sh | sh
Windows PowerShell:
irm https://cjv.zxilly.dev/install.ps1 | iex
脚本根据操作系统和 CPU 架构下载 cjv,校验后完成初始化。它写入的 PATH 一般要等到下一个终端会话才会生效。Linux 和 macOS 可以在当前终端加载:
source ~/.cjv/env
GitHub 访问不稳定时,Linux 和 macOS 可以改用 GitCode 镜像:
curl -sSf https://cjv.zxilly.dev/install.sh | sh -s -- --mirror
PowerShell 的写法是:
$env:CJV_MIRROR = "1"; irm https://cjv.zxilly.dev/install.ps1 | iex
不想执行安装脚本的话,可以从 Releases 下载预编译文件,把 cjv(Windows 上是 cjv.exe)放进 PATH。第一次运行 cjv install 时,它会补做初始化。
常用命令
安装和选择版本
lts、sts 和 nightly 会解析到各自的当前版本。需要固定编译器时,直接安装带版本号的工具链:
cjv install lts-1.0.0
cjv default lts-1.0.0
查看本机状态:
cjv show
cjv show installed
cjv show active
show active 会显示当前工具链以及选择来源。发现版本与预期不同时,再用 cjv which cjc 查看实际执行的文件。
如果不喜欢把 +nightly 写在被代理的命令后面,也可以显式调用 cjv run:
cjv run nightly cjc --version
两种写法的结果一样,都不会修改默认设置。
在项目中声明组件和目标
cangjie-sdk.toml 不只能写 SDK 版本。项目需要 stdx、离线文档或交叉编译目标时,可以一并声明:
[toolchain]
channel = "sts"
components = ["stdx", "docs"]
targets = ["ohos", "android"]
targets 中写 ohos、android 这样的后缀,不写完整的平台名称。auto-install 开启时,cjv 会在第一次运行工具前补齐缺少的内容。
有时项目固定使用 LTS,但我想在本机拿 nightly 测一下。这种情况可以设置目录覆盖,不用修改已经提交的配置文件:
cjv override set nightly
cjv show active
用完删掉即可:
cjv override unset
运行编译产物
仓颉程序会动态链接 SDK 中的运行时库。直接执行编译产物时,系统可能找不到这些库。cjv exec 会先准备当前工具链的运行时环境,再启动程序:
cjv exec ./my_binary
cjv exec +nightly ./my_binary
它只影响启动的子进程。想让整个终端会话都使用当前工具链,可以加载 envsetup 的输出。
Bash 和 Zsh:
eval "$(cjv envsetup)"
PowerShell:
cjv envsetup | Invoke-Expression
代理如何选择工具链
初始化 cjv 后,PATH 中会多出一个固定目录,里面放着 cjc、cjpm、cjfmt 和 cjlint 等代理。代理先选出工具链,再启动对应 SDK 目录中的程序。
工具链的选择顺序是:
- 命令行中的
+toolchain CJV_TOOLCHAIN环境变量- 当前目录的本地覆盖
- 当前目录或父目录中的
cangjie-sdk.toml - 默认工具链
越靠前的设置优先级越高。项目用 cangjie-sdk.toml 固定版本,本机测试可以加目录覆盖,单条命令还能再指定一次 +nightly。cjv show active 会列出最终结果和它的来源。
工具链也可以指向一套已有的本地 SDK:
cjv toolchain link mysdk /path/to/local/sdk
cjv 保存的是目录引用。卸载 mysdk 不会删除原目录。由于自定义工具链没有对应的官方发布文件,缺少的组件也不能由 cjv 自动下载。
更新和卸载
检查并安装工具链更新:
cjv check
cjv update
更新 cjv 自身:
cjv self update
普通工具链用 cjv uninstall <toolchain> 删除。卸载 cjv 的命令是:
cjv self uninstall
这条命令会删除整个 cjv 主目录,已安装的工具链和设置也在里面。运行前最好先看一眼目录里有没有要保留的东西。
发表回复