MANUAL
RakuPM 手册
一份全面、权威的 raku-pm 参考文档——从首次安装到高级开发者工作流。
版本 0.77.0 | 快速上手 | 架构深度解析 | 命令大全
目录
1. 项目概述
RakuPM 是一个面向 Raku 编程语言的教学型包管理器。它是一个功能完整的 MVP(约 3,700 行 Raku 代码,40 个库模块 + 1 个 CLI 入口),以 zef 的分层可插拔架构为蓝本。
RakuPM 可以:
发现、解析、下载、构建、测试和安装 Raku 包(来自生态索引 zef/REA/CPAN 和本地目录)
直接从 git URL 安装,并递归解析依赖
管理多个仓库索引,支持优先级排序和离线缓存
执行原子安装,支持事务回滚(世代管理)
检测系统原生库(
:from<native>),覆盖 Windows、macOS 和 Linux作者发布工作流:完整的创建、检查、打包、发布流程
自我升级:通过两阶段引导机制升级自身
核心设计原则
| 原则 | 说明 |
|---|---|
| 分层可插拔架构 | 以 zef 为蓝本;每个模块职责单一、定义清晰 |
| zef 兼容 | 相同的 META6.json 格式、规格串语法、CUR::Installation 目标、上传协议 |
| 多版本共存 | 同一包的多个版本并存;use :ver<> 选择正确版本 |
| 原子安装 | 全有或全无:安装树中任何包失败,目标恢复到安装前状态 |
| 内容寻址存储 | 包以 MD5 摘要存储,支持篡改检测 |
| 零外部依赖(除 4 个外) | MD5 为纯 Raku 实现;UI 为纯 Raku 实现;构建系统仅需 META6.json |
2. 前置要求
| 要求 | 版本 | 说明 |
|---|---|---|
| Rakudo | 6.d 或更新 | Raku 编译器 |
| Git | 任意近期版本 | git 安装和自我升级需要 |
运行时依赖(由 zef 自动安装)
| 包 | 用途 |
|---|---|
JSON::Fast | JSON 解析/生成 |
HTTP::Tinyish | HTTP 客户端(系统 curl 后端) |
URI | URI 解析 |
MD5 由纯 Raku 实现(
RakuPM::MD5),因此不需要生态依赖来做内容摘要。
3. 安装
方式 A:从源码运行(推荐)
这是最稳妥的方式——当你有多个 Rakudo 安装时可以避免版本不匹配问题。
git clone https://gitee.com/skyter10086/raku-pm.git
cd raku-pm
raku bin/raku-pm.raku --help # 打印帮助信息即表示环境正常每次使用均通过 raku bin/raku-pm.raku [命令] 调用(如果 lib 目录未被自动识别,可使用 raku -Ilib bin/raku-pm.raku [命令])。
可选:创建 shell 包装器以便快捷使用:
# bash / zsh
cat > ~/.local/bin/raku-pm <<'EOF'
#!/usr/bin/env bash
exec raku "$HOME/raku-pm/bin/raku-pm.raku" "$@"
EOF
chmod +x ~/.local/bin/raku-pm方式 B:通过 zef 安装为全局命令
git clone https://gitee.com/skyter10086/raku-pm.git
cd raku-pm
zef install .安装后 raku-pm 命令会出现在 PATH 上。在 Windows 上,zef 会在 site/bin 下生成 raku-pm.exe。
注意: 遗留的 zef 包装器会把
raku-pm --help吞掉(其*%命名参数签名吃掉了--help);raku-pm 自己生成的包装器没这问题(详见 §17 末尾那节)。推荐一律用raku-pm help。默认输出是紧凑命令索引(一屏内);raku-pm help <命令>看单个命令的详细用法,raku-pm help <组名>看某一组(如raku-pm help author看发布/打包那组),raku-pm help all看完整手册。
设置环境变量
export RAKUPM_REPO=repo # 本地仓库根目录
export RAKUPM_TARGET=$HOME/.raku-pm # 安装根目录(store/ + site/ + 锁文件)两个目录在首次运行时自动创建。
安装包后,需要将 inst#<target>/site 加入模块搜索路径:
export RAKULIB="inst#$HOME/.raku-pm/site"
raku -e 'use File::Temp; say tempfile'运行
raku-pm env可以为当前前缀打印精确的 export 命令。
4. 配置
4.1 环境变量
环境变量控制运行时行为。完整列表见 第 19 节。最常用的:
| 变量 | 用途 | 默认值 |
|---|---|---|
RAKUPM_TARGET | 安装前缀(store/site/锁文件/git-cache) | ~/.raku-pm |
RAKUPM_REPO | 本地仓库根目录 | .(当前目录) |
RAKUPM_ECOSYSTEM | 生态索引 URL(逗号/分号分隔) | (无;未设置时用内置默认 zef+cpan+rea+本地仓库,设它会覆盖内置索引) |
RAKULIB | 模块搜索路径(必须包含 inst#<target>/site) | (无) |
RAKUPM_OFFLINE | 离线模式(1/true/yes/on) | (关闭) |
RAKUPM_NO_COLOR | 禁用颜色输出 | (关闭) |
NO_COLOR | 禁用颜色(行业标准) | (关闭) |
4.2 配置文件
| 文件 | 位置 | 用途 |
|---|---|---|
repositories.json | <target>/repositories.json | 持久化的仓库配置 |
installed.json | <target>/installed.json | 已安装包的账本 |
raku-pm.lock | $CWD/raku-pm.lock(默认) | 可复现安装的锁文件 |
credentials.json | <target>/credentials.json | 生态上传的 API 密钥存储 |
META6.json | 发行版根目录 | 包身份标识(名称、版本、提供、依赖) |
4.3 全局选项
所有命令均识别以下选项:
| 选项 | 说明 |
|---|---|
--target=<dir> | 本次调用覆盖 $RAKUPM_TARGET |
--no-lock | 跳过跨进程文件锁(另一进程正在写入时危险) |
--promote-bin / --no-promote-bin | 是否将 bin 包装器放到全局 PATH 上 |
--offline | RAKUPM_OFFLINE=1 的别名;不发起网络请求 |
5. 核心概念
5.1 目标目录布局
$RAKUPM_TARGET/ 默认 ~/.raku-pm
├── cache/ 生态索引 + 下载的 tarball 和解压目录
│ ├── index-360.zef.pm.json
│ └── dist/<dist>/<version>/
├── store/ 源码快照:多版本共存,只增不减
│ └── <dist>/<version>/
│ ├── META.json raku-pm 的标准化元数据
│ ├── META6.json Raku 标准元数据(CUR 读取的文件)
│ ├── lib/ 源码
│ ├── resources/ 资源(原生 .so/.dll 构建产物存放于此)
│ ├── build.json 构建追踪副文件
│ └── .digest 内容摘要(MD5,篡改检测)
├── site/ ★ 真正被 raku 搜索的目录(CompUnit::Repository::Installation)
│ ├── precomp/ 预编译,由 Rakudo 管理
│ └── dist/ 每个发行版的元数据(多版本并存)
├── installed.json raku-pm 的已安装账本
├── raku-pm.lock 锁文件
├── generations/ 世代快照(用于回滚)
├── git-cache/ git 克隆缓存
└── log/<dist>/<version>/ 测试输出日志关键区分:store/ 与 site/
store/是源码缓存——一次下载可多次部署;卸载后重新安装无需重新下载。site/是运行时——Rakudo 的use从中解析模块的CompUnit::Repository::Installation。
5.2 多版本共存
Raku 的 use Foo(不带版本约束)始终加载已安装的最高版本。raku-pm 的设计顺应了这一点:
install将每个版本加入site/而不清除旧版本。installed.json记录全部已安装版本。要使用特定版本,在代码中写
use Foo:ver<1.2.3>,或用--only移除更高版本。
降级注意: 安装旧版本不会自动切换
use的目标。它只是在旁边增加一个版本。要使旧版本真正生效,需要upgrade Foo --version=X --only,或在代码中使用:ver<>。
5.3 账本(installed.json)
账本是"什么已安装"的唯一真实来源。每个条目包含:
name、version、auth、api——身份信息provides——该发行版提供了哪些模块digest——用于篡改检测的内容哈希source-dir——store 中的源码目录(用于重装)reason——explicit(用户请求)或dependency(作为依赖被拉入)
reason 字段驱动 autoremove:只有 dependency 原因的包会在不再被依赖时被回收。用户显式安装的包永远不会被回收。
6. 命令参考
6.1 包操作
install
安装一个包及其所有依赖。
raku-pm install <模块|url|路径> [选项]支持的目标类型:
模块名:
install File::Temp规格串:
install 'Foo:ver<1.2.3>:auth<zef:x>'Git URL:
install https://github.com/user/repo.git本地目录:
install .或install /path/to/dist
选项:
| 标志 | 说明 |
|---|---|
--version=X | 锁定精确版本 |
--locked | 严格锁文件模式;锁不一致时中止 |
--no-test | 跳过测试阶段 |
--no-build | 跳过构建阶段 |
--no-test-deps | 不安装测试依赖 |
--no-native-check | 跳过原生库检测 |
--force | 重新执行所有步骤,即使已安装 |
--allow-test-failure | 即使测试失败也安装(慎用) |
--test-timeout=S | 每个测试文件的超时秒数(默认:300) |
--dry | 仅解析;不安装 |
--lock-file=<path> | 使用指定的锁文件 |
--pin=Mod@ver[,…] | 锁定特定依赖版本 |
规格串优先级: --version > 规格串中的 :ver<> > 位置参数约束。
示例:
raku-pm install File::Temp # 从生态装最新版
raku-pm install 'Concurrent::Stack:ver<1.1>' # 精确版本
raku-pm install 'Concurrent::[email protected]' # @ 简写(等价)
raku-pm install Concurrent::Stack --version=1.1 # 标志形式
raku-pm install https://github.com/user/repo.git # 从 git 安装
raku-pm install . # 安装当前目录
raku-pm install Web::App --dry # 仅解析
raku-pm install File::Temp --no-test # 跳过测试upgrade
升级已安装的包。
raku-pm upgrade # 升级【全部】已安装包
raku-pm upgrade <模块> # 升级单个到最新
raku-pm upgrade <模块> --version=X # 升/降到指定版本
raku-pm upgrade --only # 升级后移除其他所有版本
raku-pm upgrade --dry # 仅预览不带包名的
upgrade遵循apt upgrade/zef upgrade惯例。
uninstall
卸载已安装的包。
raku-pm uninstall <模块> # 移除【所有】版本
raku-pm uninstall <模块> --version=X # 仅移除指定版本
raku-pm uninstall <模块> --keep-store # 保留 store 中的源码快照
raku-pm uninstall <模块> --recursive # 同时移除反向依赖
raku-pm uninstall <模块> --force # 忽略反向依赖检查行为细节:
不带
--version:从site/、账本和锁文件中移除所有版本;删除关联的store/快照。带
--version=X:X既可以是精确版本号(只移除该版本),也可以是 semver 约束串 —— 一次移除所有满足的版本:区间<1.0/>=1.0, <2.0、通配*、at-least0.2+, 以及 npm 风格糖语法^1.0(兼容)/~1.2(近似)。 被移除版本的 store 快照一并删除(要保留快照供秒级重装,加--keep-store)。反向依赖检查:默认在其他包依赖它时拒绝卸载。用
--recursive一并移除依赖者,或用--force强制卸载。退出码(0.87.3 起):只有真的卸掉了东西才返回 0。以下四种情形都返回 1 并在 stderr 说明原因,便于脚本判断(
raku-pm uninstall X && …/set -e): 包根本没装、指定的版本不存在、范围没匹配到任何已装版本、因反向依赖被拒绝。 修复前这四种都会打印「已卸载 X」且返回 0 —— 谎报成功比不报更糟。
糖语法的上界按 npm 语义算(0.87.2 修正过一处算宽):
^1.2.3→<2.0.0、^0.2.3→<0.3.0、^0.0.3→<0.0.4;~1.2与~1.2.3→<1.3.0、~1→<2.0.0。 权威清单见t/version.t的糖语法矩阵(两侧都断言)。
预发布版的比较(0.87.3)按 semver 2.0.0 §11:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0。 拿不准时用installed <模块>看实际排序结果。
reinstall
卸载后重新安装同一版本。
raku-pm reinstall <模块> [--version=X] [--force] [--dry]如果该包从未安装过,退化为普通的 install。选项与 install 相同。
fetch
仅下载源码,不安装。
raku-pm fetch <模块> [--to=目录] [--version=X]用于填充本地目录仓库:先 fetch 到 ./my-dists,再 repos add ./my-dists。
test
仅运行测试(不安装)。
raku-pm test [路径] # 默认为当前目录build
仅运行构建阶段(不安装)。
raku-pm build [路径] # 默认为当前目录6.2 查询命令
所有查询命令都是只读的,不会修改磁盘上的任何内容。
| 命令 | 语法 | 说明 |
|---|---|---|
list | raku-pm list | 列出所有仓库中可安装的包 |
search | raku-pm search <关键词> | 搜索包(模糊匹配 + 相关性排序) |
installed | raku-pm installed [模块] | 列出已安装的包和版本 |
store | raku-pm store [模块] | 查看包的源码存储 |
version | raku-pm version [模块] | 显示 raku-pm 版本,或某模块的已安装版本 |
which | raku-pm which [模块] | 不带参数:跨仓库冲突概览;带参数:模块实际加载位置 |
info | raku-pm info <包> | 包的详细元数据 |
browse | raku-pm browse <包> | 在浏览器中打开主页 |
locate | raku-pm locate <模块> | 查找模块在磁盘上的文件路径 |
why | raku-pm why <模块> | 依赖解析诊断 |
depends | raku-pm depends <模块> | 正向依赖树 |
rdepends | raku-pm rdepends <包> | 反向依赖(谁依赖了这个包) |
outdated | raku-pm outdated | 列出有可用更新的包 |
lock | raku-pm lock | 显示锁文件内容 |
verify | raku-pm verify [--content] [--fix] | 验证内容完整性 + 安装一致性 |
native | raku-pm native <库名> [--paths] | 检查系统原生库是否存在 |
verify的内容校验:原生 MD5 + 清单缓存(0.86.0)。 逐文件算 MD5 本是它的耗时大头 (纯 Raku MD5 只有约 0.16 MB/s,store 里 296 个文件要 10.9 秒;并行 / 多进程实测都救不了)。 现在两级加速:① 通过 NativeCall 直接调系统libcrypto的MD5()(拿不到库的机器才回退 纯 Raku);② 每个 store 条目存一份.files.json清单,大小与修改时间都没变的文件复用 上次的 MD5。实测:强制全量哈希 11.7s → 2.9s,清单命中时约 1.9s。 代价明说:「改了内容、又把大小和修改时间都保持原样」的改动查不出来 —— 怀疑被篡改时 设RAKUPM_VERIFY_NOCACHE=1(要对照纯 Raku 实现用RAKUPM_MD5=raku)。 不需要额外安装 OpenSSL:原生路径用的是系统里的libcrypto(macOS 自带、 Linux 多数发行版随 curl/git 依赖装好、Windows 看机器 —— 在 Windows 上还会额外尝试 Git for Windows 自带的那份libcrypto-3-x64.dll),拿不到就自动回退纯 Raku, 结果一致、只是慢些。--content表示只做内容校验、跳过安装一致性检查。
规格串支持: search、installed、store、version 和 fetch 均支持规格串进行版本/作者过滤:
raku-pm search 'JSON::Fast:ver<1.0+>'
raku-pm installed 'Foo:ver<1.0>'
raku-pm installed 'Foo:auth<zef:x>'
raku-pm store '[email protected]'匹配规则:
search:模糊匹配 + 相关性排序(名称精确 > 前缀 > 子串 > 模块)store/installed:精确匹配(精确的发行版名或模块名)install:索引精确模块名查找
6.3 仓库管理
raku-pm repos # 列出已配置的仓库
raku-pm repos add <别名|URL|路径> # 添加仓库
raku-pm repos add <名称> --name=X # 添加并指定别名
raku-pm repos remove <名称|URL> # 移除仓库
raku-pm repos update [名称] # 强制刷新索引缓存
raku-pm update [名称] # repos update 的别名内置别名:zef、rea、cpan、p6c。
详细说明见 第 10 节。
6.4 版本与发布管理
bump
在 META6.json 中递增版本号,并在 Changes 中添加条目。
raku-pm bump # 默认:--patch(0.1.0 → 0.1.1)
raku-pm bump --minor # 0.1.0 → 0.2.0
raku-pm bump --major # 0.1.0 → 1.0.0
raku-pm bump 2.5.0 # 精确版本
raku-pm bump --to=2.5.0 # 同上,显式形式
raku-pm bump --date=2026-09-15 # 设置标题日期
raku-pm bump --dry # 仅预览generations
列出安装世代(原子安装的可回滚快照)。
raku-pm generationsrollback
回滚到之前的世代。
raku-pm rollback # 回退一个世代
raku-pm rollback <世代ID> # 回退到指定世代详见 第 13 节。
6.5 维护与清理
clean / gc
回收缓存和未引用的 store 条目。
raku-pm clean # 试算(默认)
raku-pm clean --yes # 实际删除
raku-pm clean --all --yes # 同时回收索引和 git 缓存
raku-pm clean --name=Foo # 仅清理 Foo 相关条目
raku-pm clean --older-than=30 # 仅清理 30 天未使用的条目安全边界: 只有没有被以下四处引用的版本才会被删除:
installed.json账本活跃的
site/目录锁文件
世代清单
clean 永远不会删除任何"仍在安装中"的版本——包括你想清掉的旧版本。使用 uninstall --version=X 来移除旧版本(同时也会清理其 store 快照)。
flush
清空整个前缀(从零开始)。
raku-pm flush # 试算
raku-pm flush --yes # 删除 raku-pm 在此前缀中安装的一切与 clean 不同,flush 没有任何保护——它删除一切。执行 flush 后,raku-pm 命令将不存在;需要通过 zef install . 重新安装或从源码运行。
autoremove
回收不再需要的包。
raku-pm autoremove --dry # 预览
raku-pm autoremove # 移除孤儿依赖循环直到收敛:移除一个包可能会使其自身的依赖变成孤儿,也会被一并回收。用户显式安装的包永远不会被回收。
6.6 自管理
self-upgrade
升级 raku-pm 自身。
raku-pm self-upgrade # 有新版则升级
raku-pm self-upgrade --force # 即使已是最新也重装
raku-pm self-upgrade --from=<git-url> # 从指定远端拉取
raku-pm self-upgrade --dry # 仅预览来源优先级(两条路):
--from-dir <目录>——直接用本地源码目录装(不 clone;也是两阶段自举第二阶段的入口)git 远端——
--from <url>指定,或默认上游https://gitee.com/skyter10086/raku-pm.git
升级完成后,旧版本会自动从 site/、store/ 和账本中修剪(仅保留最新版本)。
不带
--force的self-upgrade在版本已是最新时会跳过修剪。使用--force强制重装 + 修剪。
self-remove
卸载 raku-pm 自身。
raku-pm self-remove --dry # 预览
raku-pm self-remove # 完全移除 raku-pm比 uninstall RakuPM 做得更多:同时移除 store/、bin 包装器和账本条目。
6.7 实用命令
| 命令 | 说明 |
|---|---|
env | 打印当前前缀的 RAKULIB / PATH 设置说明 |
shell | 打印可供 eval 使用的 shell 环境导出命令 |
doctor | 运行全面健康检查(RAKULIB、PATH、残留暂存、一致性、版本) |
completions <shell> | 为 bash、zsh、fish 或 pwsh 生成 shell 补全脚本 |
preflight | 提交前门禁检查(与 CI 一致) |
smoke [包...] | 下载源码并运行测试;不带参数则对所有已安装发行版执行冒烟测试 |
6.8 作者侧命令
这些命令作用于你正在开发的发行版目录。
raku-pm new <模块> [--into=目录] [--auth=] [--version=] [--with-readme] [--bin=<x>] [--force] [--dry]
raku-pm refresh [路径] [--dry]
raku-pm check [路径] [--remote]
raku-pm dist [路径] [--to=目录] [--no-build] [--dry]
raku-pm bump [路径] [--to=X.Y.Z|--major|--minor|--date] [--dry]
raku-pm publish [路径] [--remote] [--from=tar.gz] [--to=仓库目录] [--no-build] [--force] [--quiet] [--dry]
raku-pm login [--username=] [--password=] [--api-key=] [--quiet]
raku-pm logout完整作者工作流详见 第 17 节。
6.9 命令别名
方便记忆的短形式:
| 别名 | 完整命令 | 别名 | 完整命令 |
|---|---|---|---|
i | install | un / rm | uninstall |
up | upgrade | upd | update |
ri | reinstall | ls | list |
li | installed | inf | info |
dep | depends | rdep / rdeps | rdepends |
s | search | t | test |
b | build | f | fetch |
doc | doctor | out | outdated |
gen / gens | generations | rb | rollback |
ar | autoremove | nat | native |
vfy | verify | st | store |
w | which | e | env |
sh | shell | loc | locate |
br | browse | wh | why |
su | self-upgrade | sr | self-remove |
repo | repos | lk | lock |
sm | smoke |
7. 安装流水线
每次 install 遵循以下七个阶段的流水线:
resolve → fetch → build → store → test → install(激活) → lock| 阶段 | 发生了什么 | 可跳过的方式 |
|---|---|---|
| resolve | 查找哪些发行版提供目标模块;选择满足约束的最高版本;递归展开依赖;拓扑排序为自底向上顺序 | — |
| fetch | 下载 tarball(生态仓库)或指向目录(本地/git);解压到 cache/dist/ | — |
| build | 如果存在 Build.pm 或 META6 builder,在子进程中运行 | --no-build |
| store | 将源码录入内容寻址存储,计算 MD5 摘要 | — |
| test | 对源码目录运行 t/*.t 和 t/*.rakutest | --no-test |
| activate | 将发行版交给 CompUnit::Repository::Installation 写入 site/;更新账本 | — |
| lock | 将精确版本和摘要写入锁文件 | — |
原子性范围: 阶段 3–7(store 到 lock)被包装在一个事务中。如果任何包失败,目标恢复到运行前的状态。只有全部成功才会记录世代。
Git 安装遵循相同的流水线。目标包的源码来自克隆目录而非仓库,但其他阶段完全相同。
本地目录安装(install .)也遵循相同的流水线。目录被附加到依赖链的末尾。
8. 依赖解析
解析器(RakuPM::Resolver)是包管理器的核心。它执行:
递归 DFS——从目标模块出发遍历依赖图。
逐版本 provides 检查——处理"provides 漂移"问题:模块在不同版本间可能从一个发行版迁移到另一个(例如
NativeLibs在DBIish0.6.2 之前内嵌,之后独立成包)。冲突检测——同一模块被不同约束要求时,错误信息展示完整的请求者链。
循环检测——相互依赖在生态数据中确实存在,因此循环不是致命的。记录、警告,并将拓扑排序无法排定的部分追加到末尾。
拓扑排序(Kahn 算法)——依赖总在被依赖者之前安装。
候选者排序
当多个发行版提供同一模块时:
名称匹配模块名的发行版优先(例如
NativeLibs模块 →NativeLibs发行版)。然后更高版本(真正的 semver,不是字符串比较)。
然后名称字典序(确保稳定、可复现的结果)。
约束语法
| 写法 | 含义 |
|---|---|
Foo:ver<1.2.3> | 恰好 1.2.3 |
[email protected] | @ 简写,同上 |
Foo --version=1.2.3 | 标志形式 |
Foo:ver<1.2+> | 至少 1.2(zef 的 + 后缀) |
Foo:ver<>=1.0> | 大于等于 1.0 |
Foo:ver<>1.0> | 大于 1.0 |
Foo:ver<<=1.0> | 小于等于 1.0 |
Foo:ver<<1.0> | 小于 1.0 |
Foo:ver<>=1.0, <2.0> | 范围:1.0 ≤ x < 2.0 |
Foo:ver<*> / Foo:ver<> | 任意版本 |
Foo:ver<1.2.3>:auth<zef:x> | 版本 + 作者过滤 |
:auth<>实际进行过滤——不是解析后忽略。不匹配的 auth 是硬错误。
诊断:why
raku-pm why <模块>输出考虑了哪些发行版、被淘汰的原因、导致最终选择的约束。使用 Resolver.explain-all() 进行非致命诊断。
9. 版本管理
多版本
raku-pm 安装到 CompUnit::Repository::Installation,该系统原生支持多版本共存。安装 2.0.0 不会移除 1.0.0。
$ raku-pm installed Foo
· Foo 1.0.0
* Foo 2.0.0 ← use Foo 会加载这个(最高版本)* 标记表示"活跃版本"——use Foo 加载的版本。
锁定版本
raku-pm install Foo --version=1.0.0 # 安装特定版本
raku-pm install Foo [email protected] # 锁定依赖版本降级
Raku 的 use Foo 总是选择最高版本。安装旧版本会在旁边添加它,但不会使其成为活跃版本。要实际切换:
raku-pm upgrade Foo --version=1.0.0 --only # 移除更高版本
# 或在代码中使用版本约束:use Foo:ver<1.0.0>锁文件
raku-pm lock # 显示锁文件内容
raku-pm install Foo --locked # 严格模式:锁不一致时中止锁文件默认写入 $CWD/raku-pm.lock(Cargo.lock 风格),可提交到版本控制以实现可复现安装。
10. 仓库系统
仓库如何工作
raku-pm 按有序列表查询仓库以获取包元数据。解析依赖时,它会询问所有仓库并按 第 8 节 中的排序规则合并结果。
仓库类型
| 类型 | 后端 | 说明 |
|---|---|---|
| Ecosystem | Repository::Ecosystem | 远程 JSON 索引(zef、REA、CPAN) |
| Local | Repository::Local | 包含发行版子目录的本地目录 |
内置仓库别名
| 别名 | 索引 URL | 记录数 | 说明 |
|---|---|---|---|
zef | https://360.zef.pm | ~8k | 当前版本 |
rea | …/Raku/REA/main/META.json | ~15k | 生态存档,含历史版本 |
cpan | …/ugexe/Perl6-ecosystems/master/cpan1.json | ~2k | CPAN 上的 Perl6 模块 |
p6c | …/ugexe/Perl6-ecosystems/master/p6c1.json | — | 遗留存档 |
有 922 个发行版仅存在于 REA 而不在 zef 索引中——单一索引无法安装所有东西。
配置优先级(高 → 低)
$RAKUPM_TARGET/repositories.json——由repos add/remove维护RAKUPM_ECOSYSTEM环境变量——逗号/分号分隔,每项为 URL 或别名默认:
zef索引 + 本地$RAKUPM_REPO目录
添加本地目录仓库
本地目录仓库持有每个发行版一个子目录,每个含 META6.json + lib/:
raku-pm repos add ~/my-dists # 添加本地目录
raku-pm fetch HTTP::Client --to=~/my-dists # 填充它弹性索引获取
当仓库索引无法获取时:
| 情况 | 行为 |
|---|---|
| 获取失败,有旧缓存 | 重用缓存,警告"可能过期" |
| 获取失败,从未缓存 | 视为空索引,警告"结果可能不完整" |
警告永远不会静默隐藏缺失的模块——它会说明该模块可能存在但当前不可达。
索引缓存
索引使用条件 GET(ETag / If-None-Match)缓存在本地。TTL 由 RAKUPM_INDEX_TTL 控制。过期后 raku-pm 会询问服务器一次(极可能收到 304 Not Modified)。
11. 锁文件与可复现安装
锁文件(raku-pm.lock)记录精确版本和内容摘要:
{
"version": 1,
"entries": [
{ "name": "JSON", "version": "2.0.0", "auth": "demo:raku",
"digest": "d47078e2...", "depends": [] }
]
}可提交的锁文件工作流
cd my-project
raku-pm install JSON # 将精确版本锁定到 ./raku-pm.lock
git add -f raku-pm.lock # 提交(-f 绕过 .gitignore)
# 同事,克隆后:
raku-pm install --locked # 严格重用锁定版本--lock-file=<path>可重定位锁文件(CI 中常用)。install/reinstall/self-upgrade/lock均接受--lock-file。锁文件与
--target无关:即使使用独立前缀,锁文件仍留在项目根目录。
12. 原生库检测
raku-pm 不打包系统库。安装前会检查所需原生库是否存在于系统中。
检查内容
META6.json 中的
:from<native>依赖扫描
lib/和bin/源码中的is native('...')运行时调用:from<bin>和:from<Perl5>外部依赖(仅报告,不阻止安装)
跨平台文件名映射
| 逻辑名 | Windows | macOS | Linux |
|---|---|---|---|
sqlite3 | sqlite3.dll | libsqlite3.dylib | libsqlite3.so |
ssl | libssl-3-x64.dll | libssl.dylib | libssl.so |
libffi | libffi-8.dll | libffi.dylib | libffi.so |
zlib | zlib1.dll | libz.dylib | libz.so |
未知 foo | foo.dll | libfoo.dylib | libfoo.so |
跨平台搜索路径
| 操作系统 | 搜索路径 |
|---|---|
| Windows | PATH → System32/SysWOW64 → vcpkg installed/*/bin |
| macOS | DYLD_LIBRARY_PATH → /opt/homebrew/lib(Apple Silicon)→ /usr/local/lib(Intel)→ /opt/local/lib(MacPorts)→ /usr/lib |
| Linux | LD_LIBRARY_PATH → /usr/local/lib、/usr/lib → Debian 多架构目录 → 版本化 .so.N |
各发行版的安装命令
当库缺失时,raku-pm 会根据你的 Linux 发行版建议安装命令:
| 发行版家族 | 包管理器 | 示例 |
|---|---|---|
| Debian/Ubuntu | apt | apt install libsqlite3-dev |
| Fedora/RHEL | dnf | dnf install sqlite-devel |
| Arch | pacman | pacman -S sqlite |
| Alpine | apk | apk add sqlite-dev |
| openSUSE | zypper | zypper install libsqlite3-devel |
查询原生库
raku-pm native sqlite3 # 检查是否存在
raku-pm native ssl curl --paths # 多个同时查询,显示搜索路径
raku-pm native --installed # 扫描锁文件中声明的库
raku-pm native sqlite3 --os=linux --distro=ubuntu # 模拟其他平台平台条件依赖
META6.json 可使用 by-distro.name 声明平台特定依赖:
{
"depends": [{
"name": {
"by-distro.name": {
"": "",
"mswin32": "Win32::Registry"
}
}
}]
}仅解析匹配 $*DISTRO.name 的分支(或 "" 回退)。在 Linux 上不会获取 Win32::Registry。
13. 原子安装与世代回滚
原子安装的工作原理
所有包暂存到共享的临时 CUR(
target/stage/pending)。测试对暂存 CUR 运行。
如果全部通过,包被提升到真实的
site/+ 账本 + bin 包装器。如果有任何失败,暂存区被丢弃——真实 CUR 永远不被触碰。
raku-pm install A # A 依赖 B → 顺序 [B, A]
B 安装到暂存 CUR ✓
A 的测试失败 ✗
→ 回滚:暂存区丢弃;B 也被移除;目标恢复到之前的状态世代
成功的原子安装后,一个世代被记录到 target/generations/:
target/generations/
├── current 当前世代 ID
├── 000001/
│ ├── before.json 事务前的账本快照
│ ├── manifest.json 事务后的账本
│ └── meta.json 时间戳、安装了什么
└── 000002/...保留最近 10 个世代。世代非常轻量——是几 KB 的 JSON,不是 site/ 的完整副本。
回滚
raku-pm generations # 列出可用世代
raku-pm rollback # 回退一个世代
raku-pm rollback 000003 # 回退到指定世代回滚会从 CUR 中移除目标世代安装的版本并恢复账本。之前版本中未被移除的版本保持不变。
限制: 如果目标世代的源码已被
clean从 store 中移除,回滚会警告并跳过。如果可能需要回滚,不要过度清理。
14. 缓存与清理
缓存内容
| 类别 | 位置 | 空间影响 |
|---|---|---|
| Store 快照 | store/<dist>/<version>/ | 随时间增长(每次升级留一个快照) |
| 下载缓存 | cache/dist/ | tarball + 解压的目录 |
| 索引缓存 | cache/index-*.json | 每个索引约 10 MB |
| Git 克隆缓存 | git-cache/ | 每个仓库一个克隆 |
| 预编译产物 | store 条目中的 .precomp | 按需重建 |
| 测试日志 | log/<dist>/<version>/ | 每个测试文件的输出 |
使用 clean 清理
clean 是保护性 GC——只删除不被引用的版本。引用来源:账本、site/、锁文件和世代清单。
raku-pm clean # 试算:查看将被回收的内容
raku-pm clean --yes # 实际删除
raku-pm clean --all --yes # 同时回收索引和 git 缓存
raku-pm clean --name=Foo # 仅清理 Foo 相关条目
raku-pm clean --older-than=30 # 仅清理 30 天未使用的条目核武器:flush
flush 清除当前前缀中 raku-pm 安装的一切,没有任何保护。仅在需要从零开始时使用。
回收孤儿依赖
raku-pm autoremove # 移除不再被依赖的包
raku-pm autoremove --dry # 预览循环直到收敛:移除一个包可能会使其自身的依赖变成孤儿,也会被一并回收。
15. 并发与文件锁
多个 raku-pm 进程同时运行可能损坏 store 和锁文件。为防止这种情况,每个写命令都会获取跨进程文件锁:
$ raku-pm install Foo # 同时另一个终端在安装别的包
⏳ 另一个 raku-pm 正在写入 /home/user/.raku-pm,等待它结束…锁的细节
| 方面 | 行为 |
|---|---|
| 机制 | flock(进程终止时 OS 回收;不会留下僵死锁) |
| 锁文件 | target/.raku-pm.run.lock(不持有状态) |
| 超时 | 默认 600 秒(RAKUPM_LOCK_TIMEOUT) |
| 可重入 | 进程内引用计数;嵌套调用不会自锁 |
| 子进程令牌 | 为子进程生成随机令牌;它们跳过锁 |
| 逃生舱 | --no-lock 或 RAKUPM_NO_LOCK=1(危险;仅在确认无其他进程运行时使用) |
安装时的测试是串行的
0.80.0 起,跑测试一律串行(每个测试文件一个独立子进程,进程隔离,但不是沙箱)。
此前曾默认并发跑测试,但为了让并发不误伤安装,得额外配「串行预热 precomp」+「失败用例
串行复验」两道安全网 —— 这份复杂度在单机教学场景下不划算,故整个并发机制已移除
(--test-jobs 开关随之取消)。
| 阶段 | 并发? |
|---|---|
| 依赖解析 / 下载 / 构建 / 入 store / 进暂存 / 晋升 site | 串行(自底向上逐个发行版) |
| 跑测试文件 | 串行(逐文件,进程隔离) |
跨进程(两个 raku-pm 同时跑) | 串行 —— 靠上面那道文件锁 |
唯一的自动重试:失败串行复验
首跑失败的用例会串行重跑一次,仍挂才算真失败。这是为上游 flaky 用例留的余地 ——
实测 Concurrent::Stack 1.3 的 stress 用例连跑 10 次挂 3 次(上游自身的竞态丢数据);
那是上游 bug,但用户不该为一个概率性失败而装不上包。复验日志写 <名字>.flake.log,
不覆盖首跑的失败日志(保留诊断线索)。
残余风险(如实列出)
| 风险 | 说明 |
|---|---|
| 复验也挂 → 判真失败,整链回滚 | 本次已构建成功的依赖会一并丢弃(store 里的版本仍保留) |
| 上游测试本身 flaky | 如 Concurrent::Stack 1.3 的 stress 用例;串行也挂,靠复验兜一次 |
| 单个测试文件很慢 | 串行意味着慢用例依次累加墙钟,可能撞 --test-timeout(默认 300 秒) |
旋钮
raku-pm install Foo --test-timeout=900 # 放宽单个测试文件的超时
raku-pm install Foo --allow-test-failure # 测试失败仍装上(软通过)
raku-pm install Foo --no-test # 完全不跑测试环境变量:RAKUPM_TEST_TIMEOUT。
还有两类自动软通过:显式
--allow-test-failure,以及被识别为「Windows 平台已知 上游 bug」的失败(判定很窄:具体包 + 具体测试文件 + 失败特征),后者不会掩盖真 bug。
16. HTTP 层
所有网络流量通过 RakuPM::HTTP 门面,由唯一后端执行实际请求:
| 后端 | 底层 | 说明 |
|---|---|---|
RakuPM::HTTP::Tinyish | HTTP::Tinyish(系统 curl) | 唯一后端;最佳 TLS/代理支持 |
0.82.0 起为单后端:原先还有一个纯 Raku 的
HTTP::Tiny兜底后端,以及RAKUPM_HTTP_BACKEND/register-backend那套可插拔机制,现已移除。
功能
指数退避重试加抖动,应对瞬时故障
代理支持,通过
HTTPS_PROXY/HTTP_PROXY环境变量条件 GET,使用
ETag/If-None-Match(304 重用缓存)调试可观测性,通过
RAKUPM_HTTP_DEBUG分块上传,用于生态发布
17. 作者工作流与发布
生产者端工作流遵循以下顺序:
new → refresh → check → dist → publish1. 搭建新发行版
raku-pm new Foo::Bar # 创建 Foo-Bar/
raku-pm new Foo::Bar --into=MyProj # 自定义目录名
raku-pm new Foo::Bar --with-readme --bin=foo --auth=github:me --version=0.2.0
raku-pm new Foo::Bar --dry # 仅预览创建:META6.json、lib/Foo/Bar.rakumod、t/01-basic.rakutest、Changes、.gitignore,可选 README.md 和 bin/<x>.raku。
2. 从磁盘重建 provides
raku-pm refresh # 为当前目录重建 provides
raku-pm refresh ./some-dist # 指定目录
raku-pm refresh --dry # 仅预览在 lib/ 下添加/重命名模块后,META6.json 中的 provides 可能与磁盘不一致。refresh 扫描 lib/ 并自动重建 provides。仅修改 provides——其他字段原样保留。
3. 发布前预检
raku-pm check # 仅本地,即时,离线
raku-pm check ./some-dist # 指定目录
raku-pm check --remote # 同时联网检查该版本是否已发布检查项:META6.json 有效、名称存在、版本 ≠ *、auth 匹配 RAKUPM_AUTHOR_AUTH、provides 与磁盘匹配、bin 脚本存在、依赖可解析。
任何硬错误([✗])导致非零退出——可直接作为 CI 发布门禁。
4. 打包 sdist
raku-pm dist # 打包当前目录
raku-pm dist ./some-dist # 指定目录
raku-pm dist --no-build # 跳过构建阶段
raku-pm dist --to=../out # 将 tarball 放到 ../out
raku-pm dist --dry # 仅预览先运行 check 门禁(任何硬错误被直接拒绝)。如果存在 Build.pm,则内联执行(原生扩展编译产物会进入 tarball)。输出为源码分发包(不含预编译字节码)。
5. 部署
本地目录仓库:
raku-pm publish --to=../my-dists # 部署到本地仓库
raku-pm publish --from=../out/Foo-1.0.0.tar.gz --to=../my-dists # 复用已有包
raku-pm publish --dry # 仅预览远程生态:
raku-pm login --api-key=<key> # 存储 API 密钥
raku-pm login --username=me --password=secret # 在线登录 42.zef.pm
raku-pm publish --remote --dry # 预览上传请求
raku-pm publish --remote # 实际上传发布后,其他机器可以 raku-pm repos add <仓库> 然后 raku-pm install <包名>。
18. 排障指南
模块找不到
在所有已配置的仓库里都找不到模块 'Foo'可能原因:
该模块不在默认的
zef索引中。尝试raku-pm repos add rea获取更广的覆盖。某个仓库索引无法获取。检查错误上方是否有
⚠警告行。模块名拼写错误。尝试
raku-pm search <部分名>。
"另一个 raku-pm 正在写入"超时
可能有一个之前的 raku-pm 进程挂起了。锁在进程退出(或被终止)时自动释放。如果锁文件已过期:
rm $RAKUPM_TARGET/.raku-pm.run.lock或等待最多 600 秒让锁超时。
原生库找不到
✗ sqlite3 未找到 —— 本系统需要 libsqlite3.so / libsqlite3.so.0
安装:apt install libsqlite3-dev使用建议的命令安装系统库,或使用 --no-native-check 跳过检查(库在运行时仍会加载失败)。
RAKULIB 遮蔽
如果 RAKULIB 被全局导出(从 shell 配置文件),raku 会加载 site/ 中 raku-pm 自身的旧副本。如果那个副本过期,测试可能表现为当前代码的失败。
安全习惯:
通过
raku tools/run-suite.raku运行测试(它传递-I<工作区>/lib并自检)。运行单个文件用
raku -Ilib t/<x>.t(-I在RAKULIB之前)。查看哪个副本加载:
raku-pm which RakuPM::Version
use Foo:ver<1.2.3> 加载了错误的版本
RAKULIB 中的 inst# 前缀是必须的。没有它,Rakudo 将 site/ 视为普通文件系统仓库并忽略版本元数据:
export RAKULIB="inst#$HOME/.raku-pm/site" # 正确
# 错误:export RAKULIB="$HOME/.raku-pm/site"在 PowerShell 中 raku-pm --help 可能被吞掉
先说结论:raku-pm 自己生成的包装器下 --help 是可用的 —— 实测 raku-pm --help、
raku-pm -h 都正常进入帮助、退出码 0(本机 wrapper 为 site/bin/raku-pm.ps1,
它把 @args 原样转发给入口)。会被吞掉的只有遗留的 zef 包装器(raku-pm.exe,
其 *% 命名参数签名把 --help 当自己的参数收走)—— 那个 .exe 会在安装/升级时被
重命名为 <name>.zef-old,见下一节。
推荐一律用 raku-pm help:它在任何包装器形态下都可靠,且能分级
(raku-pm help <命令> / raku-pm help <组名> / raku-pm help all)。
升级后 raku-pm version 仍显示旧版本
zef 时代的 raku-pm.exe(.EXE 在 PATHEXT 中优先于 .BAT)可能遮蔽了新包装器。安装/升级时的入口检查会将其重命名为 <name>.zef-old。如果问题持续,运行 raku-pm env 查看哪个入口点实际执行。
调试模式
RAKUPM_DEBUG=1 raku-pm <命令> # 失败时显示完整回溯
RAKUPM_HTTP_DEBUG=1 raku-pm <命令> # HTTP 请求/响应详情19. 环境变量参考
| 变量 | 用途 | 默认值 |
|---|---|---|
RAKUPM_TARGET | 安装前缀(store/site/锁文件) | ~/.raku-pm |
RAKUPM_REPO | 本地仓库根目录 | .(当前目录) |
RAKUPM_ECOSYSTEM | 生态索引 URL(逗号/分号分隔) | (无;未设置时用内置默认 zef+cpan+rea+本地仓库,设它会覆盖内置索引) |
RAKULIB | 模块搜索路径 | (无) |
RAKUPM_OFFLINE | 离线模式(1/true/yes/on) | (关闭) |
RAKUPM_NO_COLOR | 禁用颜色输出 | (关闭) |
NO_COLOR | 禁用颜色(行业标准) | (关闭) |
RAKUPM_HTTP_DEBUG | HTTP 调试日志 | (关闭) |
RAKUPM_INDEX_TTL | 索引缓存 TTL | (可配置) |
RAKUPM_LOCK_TIMEOUT | 跨进程锁超时(秒) | 600 |
RAKUPM_NO_LOCK | 跳过写锁(1) | (关闭) |
RAKUPM_LOCK_TOKEN | 内部:子进程锁令牌 | (自动) |
RAKUPM_TEST_TIMEOUT | 每个测试文件的超时(秒) | 300 |
RAKUPM_TEST_LOG_DIR | 测试日志根目录 | <target> |
RAKUPM_TEST_PROGRESS | 测试进度显示(0=关,1=开) | 自动(TTY 检测) |
RAKUPM_PUBLISH_REPO | 默认发布目标目录 | (无) |
RAKUPM_API_KEY | 生态上传 API 密钥 | (无) |
RAKUPM_AUTHOR_AUTH | 发布者身份 | zef:skyter10086 |
RAKUPM_CURL | 覆盖 curl 二进制路径 | 系统 curl |
RAKUPM_DEBUG | 失败时显示完整回溯 | (关闭) |
RAKUPM_CI_XT | CI:设为 0 跳过扩展测试 | (开启) |
HTTPS_PROXY / HTTP_PROXY | HTTP 请求代理设置 | (无) |
DYLD_LIBRARY_PATH | macOS 库搜索路径 | (无) |
LD_LIBRARY_PATH | Linux 库搜索路径 | (无) |
20. 配置文件参考
repositories.json
位于 $RAKUPM_TARGET/repositories.json。由 repos add/remove 维护。结构:
[
{ "name": "zef", "url": "https://360.zef.pm", "type": "ecosystem" },
{ "name": "local", "url": "/path/to/local-repo", "type": "local" }
]installed.json
位于 $RAKUPM_TARGET/installed.json。已安装包的账本。每个条目包含身份信息、provides、摘要、源码目录和安装原因。
raku-pm.lock
位于 $CWD/raku-pm.lock(默认)。记录精确版本和内容摘要,用于可复现安装。可提交到版本控制。
credentials.json
位于 $RAKUPM_TARGET/credentials.json。存储生态上传的 API 密钥。由 raku-pm login 创建。
META6.json
发行版清单。关键字段:
{
"name": "My-Dist",
"version": "1.0.0",
"auth": "github:me",
"api": "1",
"description": "简要描述",
"license": "Artistic-2.0",
"provides": { "My::Dist": "lib/My/Dist.rakumod" },
"depends": [ "JSON::Fast" ],
"test-depends": [ "Test" ],
"build-depends": [],
"bin": { "my-tool": "bin/my-tool.raku" },
"source-url": "https://github.com/me/my-dist"
}21. 贡献指南
运行测试
# 完整测试套件(与 CI 使用相同代码路径):
raku tools/run-suite.raku
# 不含扩展测试(无网络/重型测试):
raku tools/run-suite.raku --no-xt
# 仅文档检查:
raku tools/run-suite.raku --docs-only
# 按名称过滤:
raku tools/run-suite.raku --filter=native
# 不打印每用例耗时 / 最慢排行(CI 日志求干净时):
raku tools/run-suite.raku --no-timings
# 单个测试文件:
raku -Ilib t/version.t不要使用
prove6判定测试结果——它会误报无害的警告。唯一可靠的结果来源是raku tools/run-suite.raku。
想知道"门禁为什么慢"就跑一次全量,看结尾的耗时小结:脚本默认打印每个用例的墙钟、 最慢 15 个排行、
t/xt/实际墙钟、并行加速比,以及raku -e ''的启动基线及其占比。 判据要两头看:启动占比高 ⇒ 瓶颈是进程数(合并用例文件才有效);占比低 ⇒ 瓶颈是重测试 本身与其调度,先看排行前几名。实测(本机 8 并发,135 个用例):启动基线 0.22s 仅占 2%、 平均每用例 14.2s,所以本项目属于后者。另外,加速比明显低于--jobs值通常是"长任务 落在批次尾部、批尾只剩一两个在跑"的长尾,而不是并行没生效。
调度方式:套件把
t/与xt/放在同一个池里,按历史耗时降序调度(LPT)—— 长任务 先起,短任务填进它们留下的空槽;任务再分到若干条道上各跑各的(不设"每层等齐"的屏障, 谁先跑空谁接下一个)。所以日志里的用例是混着出现的(末尾有按目录的分组小结)。 早先"按目录分开跑 + 按文件名分批"会让长任务随机落在批次尾部、批尾只剩一两个还在跑, 实测 8 并发只拿到 2.83x。耗时记录在~/.raku-pm/suite-timings.json,纯排序提示。
Git 预提交钩子
raku tools/install-hooks.raku # 安装 git 预提交钩子钩子执行 tools/run-suite.raku --no-xt(与 CI 使用相同门禁)。
CI 流水线
Gitee Go 流水线(.workflow/master-pipeline.yml)在 push 到 master 时触发,在自托管代理上运行,执行 raku tools/run-suite.raku。
代码结构
bin/raku-pm.raku CLI 入口(参数解析、命令派发)
lib/RakuPM/Client.rakumod 编排器(协调所有其他模块)
lib/RakuPM/Resolver.rakumod 依赖解析器
lib/RakuPM/Installer.rakumod Store → CUR 激活
lib/RakuPM/Repository/ 可插拔仓库后端
lib/RakuPM/HTTP/ 单后端 HTTP 层
lib/RakuPM/UI.rakumod 终端显示(颜色、表格、CJK 对齐)
lib/RakuPM/Version.rakumod Semver 解析与比较
lib/RakuPM/Spec.rakumod 安装规格串解析器
lib/RakuPM/Author.rakumod 作者侧命令架构概览
bin/raku-pm.raku(CLI)
→ RakuPM::Client(编排器)
→ RakuPM::Resolver(依赖解析)
→ RakuPM::Repository(角色,可插拔后端)
→ Repository::Local
→ Repository::Ecosystem
→ RakuPM::Store(内容寻址存储)
→ RakuPM::Installer(激活到 CUR::Installation)
→ RakuPM::Builder(构建阶段)
→ RakuPM::Lock(锁文件)详见 architecture.md。
22. 常见问题
Q:raku-pm 与 zef 有什么区别?
raku-pm 以 zef 的架构为蓝本但从零实现,作为教学工具。关键区别:raku-pm 约 3,700 行(zef 大得多),具有原子安装和世代回滚,包含跨平台原生库检测器,实现了完整的作者发布工作流。zef 是生产级包管理器;raku-pm 旨在教学包管理器的工作原理。
Q:raku-pm 和 zef 能共存吗?
能。它们安装到不同位置(zef 装到 ~/.raku,raku-pm 装到 $RAKUPM_TARGET/site)。Raku 的多版本设计让同一模块的两个版本可以并存。
Q:如果我安装了 zef 已经安装的包会怎样?
它们共存。如果两者安装了同一版本,use 优先从 RAKULIB 选择(raku-pm 的 site)。raku-pm which 显示实际加载的是哪个副本。
Q:为什么 store/ 和 site/ 要有两份?
store/ 是下载缓存(一次下载多次部署;重装不需要重新下载)。site/ 是运行时(use 从中解析模块)。这与 Cargo 的 registry/cache + src 设计一致。
Q:查询命令能用规格串吗?
能。search、installed、store、version 和 fetch 均支持 Foo:ver<1.2+> 或 [email protected] 等规格串进行版本/作者过滤。
Q:upgrade 的 --only 是做什么的?
升级后移除所有更高版本。这是让降级真正生效的方式(Raku 的 use 总是选择最高版本)。
Q:如何贡献?
参见 第 21 节:贡献指南。项目使用 Gitee 托管;贡献遵循标准的 fork → 分支 → PR 工作流。所有 PR 必须通过 raku tools/run-suite.raku。
附录 A:退出码
| 码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 业务失败(测试未过、本地库缺失、用法错误等) |
业务失败打印干净的用户友好消息并以退出码 1 退出,不打印内部栈追踪。设置 RAKUPM_DEBUG=1 可查看完整回溯。
附录 B:Spec 字符串语法
Spec 字符串是 zef 兼容的方式,用于指定带版本和/或作者约束的模块。被 install、search、installed、store、version 和 fetch 接受。
支持的形式
| 形式 | 示例 | 含义 |
|---|---|---|
| 仅模块名 | Foo | 任意版本 |
带 :ver<> | Foo:ver<1.2.3> | 恰好 1.2.3 |
带 :ver<> + + | Foo:ver<1.2+> | 至少 1.2 |
带 @ 简写 | [email protected] | 恰好 1.2.3 |
带 :auth<> | Foo:auth<zef:x> | 指定作者 |
| 组合 | Foo:ver<1.2.3>:auth<zef:x>:api<1> | 版本 + 作者 + API |
Shell 引号
<> 中的 : 是 shell 重定向运算符。请引用整个字符串:
bash/zsh:
search 'Foo:ver<1.2+>'Windows cmd: 使用
@简写替代:search [email protected]+PowerShell: 同 bash——引用字符串即可
回退
如果 ver 部分不是有效约束(如 search Foo@bar),整个输入回退为普通关键词搜索。
附录 C:平台行为矩阵
| 功能 | Windows | macOS | Linux |
|---|---|---|---|
| 原生库后缀 | .dll | .dylib | .so |
| 原生库前缀 | (无) | lib | lib |
| Shell 包装器 | .bat + .ps1 | bash | bash |
| 默认搜索路径 | PATH、System32、vcpkg | DYLD_LIBRARY_PATH、Homebrew、MacPorts | LD_LIBRARY_PATH、多架构目录 |
sort -V 支持 | N/A(使用 bash) | 否(回退为字典序) | 是 |
site/bin 提升 | .exe → .bat 重命名检查 | 直接复制 | 直接复制 |
| 文件锁 | LockFileEx(强制锁) | flock | flock |
PATHEXT | .EXE 优先于 .BAT | N/A | N/A |
本手册覆盖 raku-pm 版本 0.77.0。最新变更请参见 Changes。 问题和反馈:https://gitee.com/skyter10086/raku-pm/issues