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. 前置要求

要求版本说明
Rakudo6.d 或更新Raku 编译器
Git任意近期版本git 安装和自我升级需要

运行时依赖(由 zef 自动安装)

包用途
JSON::FastJSON 解析/生成
HTTP::TinyishHTTP 客户端(系统 curl 后端)
URIURI 解析

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 上
--offlineRAKUPM_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-least 0.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 查询命令

所有查询命令都是只读的,不会修改磁盘上的任何内容。

命令语法说明
listraku-pm list列出所有仓库中可安装的包
searchraku-pm search <关键词>搜索包(模糊匹配 + 相关性排序)
installedraku-pm installed [模块]列出已安装的包和版本
storeraku-pm store [模块]查看包的源码存储
versionraku-pm version [模块]显示 raku-pm 版本,或某模块的已安装版本
whichraku-pm which [模块]不带参数:跨仓库冲突概览;带参数:模块实际加载位置
inforaku-pm info <包>包的详细元数据
browseraku-pm browse <包>在浏览器中打开主页
locateraku-pm locate <模块>查找模块在磁盘上的文件路径
whyraku-pm why <模块>依赖解析诊断
dependsraku-pm depends <模块>正向依赖树
rdependsraku-pm rdepends <包>反向依赖(谁依赖了这个包)
outdatedraku-pm outdated列出有可用更新的包
lockraku-pm lock显示锁文件内容
verifyraku-pm verify [--content] [--fix]验证内容完整性 + 安装一致性
nativeraku-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 generations

rollback

回滚到之前的世代。

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 天未使用的条目

安全边界: 只有没有被以下四处引用的版本才会被删除:

  1. installed.json 账本

  2. 活跃的 site/ 目录

  3. 锁文件

  4. 世代清单

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             # 仅预览

来源优先级(两条路):

  1. --from-dir <目录>——直接用本地源码目录装(不 clone;也是两阶段自举第二阶段的入口)

  2. 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 命令别名

方便记忆的短形式:

别名完整命令别名完整命令
iinstallun / rmuninstall
upupgradeupdupdate
rireinstalllslist
liinstalledinfinfo
depdependsrdep / rdepsrdepends
ssearchttest
bbuildffetch
docdoctoroutoutdated
gen / gensgenerationsrbrollback
arautoremovenatnative
vfyverifyststore
wwhicheenv
shshellloclocate
brbrowsewhwhy
suself-upgradesrself-remove
reporeposlklock
smsmoke

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)是包管理器的核心。它执行:

  1. 递归 DFS——从目标模块出发遍历依赖图。

  2. 逐版本 provides 检查——处理"provides 漂移"问题:模块在不同版本间可能从一个发行版迁移到另一个(例如 NativeLibs 在 DBIish 0.6.2 之前内嵌,之后独立成包)。

  3. 冲突检测——同一模块被不同约束要求时,错误信息展示完整的请求者链。

  4. 循环检测——相互依赖在生态数据中确实存在,因此循环不是致命的。记录、警告,并将拓扑排序无法排定的部分追加到末尾。

  5. 拓扑排序(Kahn 算法)——依赖总在被依赖者之前安装。

候选者排序

当多个发行版提供同一模块时:

  1. 名称匹配模块名的发行版优先(例如 NativeLibs 模块 → NativeLibs 发行版)。

  2. 然后更高版本(真正的 semver,不是字符串比较)。

  3. 然后名称字典序(确保稳定、可复现的结果)。

约束语法

写法含义
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 节 中的排序规则合并结果。

仓库类型

类型后端说明
EcosystemRepository::Ecosystem远程 JSON 索引(zef、REA、CPAN)
LocalRepository::Local包含发行版子目录的本地目录

内置仓库别名

别名索引 URL记录数说明
zefhttps://360.zef.pm~8k当前版本
rea…/Raku/REA/main/META.json~15k生态存档,含历史版本
cpan…/ugexe/Perl6-ecosystems/master/cpan1.json~2kCPAN 上的 Perl6 模块
p6c…/ugexe/Perl6-ecosystems/master/p6c1.json—遗留存档

有 922 个发行版仅存在于 REA 而不在 zef 索引中——单一索引无法安装所有东西。

配置优先级(高 → 低)

  1. $RAKUPM_TARGET/repositories.json——由 repos add/remove 维护

  2. RAKUPM_ECOSYSTEM 环境变量——逗号/分号分隔,每项为 URL 或别名

  3. 默认: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> 外部依赖(仅报告,不阻止安装)

跨平台文件名映射

逻辑名WindowsmacOSLinux
sqlite3sqlite3.dlllibsqlite3.dyliblibsqlite3.so
ssllibssl-3-x64.dlllibssl.dyliblibssl.so
libffilibffi-8.dlllibffi.dyliblibffi.so
zlibzlib1.dlllibz.dyliblibz.so
未知 foofoo.dlllibfoo.dyliblibfoo.so

跨平台搜索路径

操作系统搜索路径
WindowsPATH → System32/SysWOW64 → vcpkg installed/*/bin
macOSDYLD_LIBRARY_PATH → /opt/homebrew/lib(Apple Silicon)→ /usr/local/lib(Intel)→ /opt/local/lib(MacPorts)→ /usr/lib
LinuxLD_LIBRARY_PATH → /usr/local/lib、/usr/lib → Debian 多架构目录 → 版本化 .so.N

各发行版的安装命令

当库缺失时,raku-pm 会根据你的 Linux 发行版建议安装命令:

发行版家族包管理器示例
Debian/Ubuntuaptapt install libsqlite3-dev
Fedora/RHELdnfdnf install sqlite-devel
Archpacmanpacman -S sqlite
Alpineapkapk add sqlite-dev
openSUSEzypperzypper 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. 原子安装与世代回滚

原子安装的工作原理

  1. 所有包暂存到共享的临时 CUR(target/stage/pending)。

  2. 测试对暂存 CUR 运行。

  3. 如果全部通过,包被提升到真实的 site/ + 账本 + bin 包装器。

  4. 如果有任何失败,暂存区被丢弃——真实 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::TinyishHTTP::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 → publish

1. 搭建新发行版

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_DEBUGHTTP 调试日志(关闭)
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_XTCI:设为 0 跳过扩展测试(开启)
HTTPS_PROXY / HTTP_PROXYHTTP 请求代理设置(无)
DYLD_LIBRARY_PATHmacOS 库搜索路径(无)
LD_LIBRARY_PATHLinux 库搜索路径(无)

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:平台行为矩阵

功能WindowsmacOSLinux
原生库后缀.dll.dylib.so
原生库前缀(无)liblib
Shell 包装器.bat + .ps1bashbash
默认搜索路径PATH、System32、vcpkgDYLD_LIBRARY_PATH、Homebrew、MacPortsLD_LIBRARY_PATH、多架构目录
sort -V 支持N/A(使用 bash)否(回退为字典序)是
site/bin 提升.exe → .bat 重命名检查直接复制直接复制
文件锁LockFileEx(强制锁)flockflock
PATHEXT.EXE 优先于 .BATN/AN/A

本手册覆盖 raku-pm 版本 0.77.0。最新变更请参见 Changes。 问题和反馈:https://gitee.com/skyter10086/raku-pm/issues

RakuPM v1.0.3

一个教学用的 Raku 包管理器:多索引仓库、依赖解析、跨系统本地库(:from<native>)探测、内容寻址存储、锁文件、单版本激活、单后端 HTTP 客户端(curl)、索引 TTL 与网络重试

Authors

  • skyter10086

License

Apache-2.0

Dependencies

Test Dependencies

Provides

  • RakuPM::Author
  • RakuPM::Builder
  • RakuPM::CLI
  • RakuPM::Cleaner
  • RakuPM::CliCheck
  • RakuPM::CliSpec
  • RakuPM::Client
  • RakuPM::Client::Flusher
  • RakuPM::Client::Git
  • RakuPM::Client::Query
  • RakuPM::Client::SelfManager
  • RakuPM::Client::Tester
  • RakuPM::Distribution
  • RakuPM::FileLock
  • RakuPM::Fs
  • RakuPM::HTTP
  • RakuPM::HTTP::Backend
  • RakuPM::HTTP::Tinyish
  • RakuPM::Help
  • RakuPM::InstallOptions
  • RakuPM::Installer
  • RakuPM::Installer::Generations
  • RakuPM::Installer::ShellTemplates
  • RakuPM::Ledger
  • RakuPM::Lock
  • RakuPM::MD5
  • RakuPM::Message
  • RakuPM::NativeLib
  • RakuPM::Net
  • RakuPM::Platform
  • RakuPM::Prefix
  • RakuPM::Repositories
  • RakuPM::Repository
  • RakuPM::Repository::Ecosystem
  • RakuPM::Repository::Local
  • RakuPM::Repository::Matching
  • RakuPM::Resolver
  • RakuPM::Spec
  • RakuPM::Store
  • RakuPM::UI
  • RakuPM::Version

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite — the markup and publishing tools behind this site.