它和别的包管理器差在哪
一句话:pip 里最难的是「挑版本」,Lompi 里最难的是「把实例身份算准」。前者是一整个回溯求解器的工程量;后者是一条公式,但那条公式必须对得起一件反直觉的事。
依赖从源码来
依赖不用另行声明——它就是源码里的 use。于是「清单说依赖 A、代码其实不用 A」这类谎言连存在的地方都没有。
身份是内容哈希
锁文件里钉的是哈希,不是版本号。所以「可复现」是算出来的,不是靠版本区间推出来的。
多版本共存
同一个库的两个版本可以同时存在于一次构建里。选版只剩一条规则——版本最大;要钉死就写 名字@版本 或用锁。没有区间,没有冲突消解策略这一整类复杂度。
那条反直觉的事
Lompi 算的不是「一个包的哈希」,是「一个实例的哈希」:
id(P) = sha256( 自身源码 ⊕ 每条 name 边: 边名 0x00 子实例身份 0x00 )
边也要进哈希。因为只看自身源码是错的——同一份源码在两个构建里绑到不同的依赖上,语义不同,却会被算出同一个身份、被错误地合并成一个实例。这不是理论担忧,它长这样:
geom 的源码逐字节相同, 但落在两个仓库里 仓库 A 有 mathutil 0.1.0 / 0.2.0 → geom id = 06b4cc0d3b1f… (绑 0.2.0) 仓库 B 只有 mathutil 0.1.0 → geom id = 0f4bb3784d09… (绑 0.1.0)
两个 id 不同是对的。把它们合并才是有害的——那会让「geom 用的是哪个 mathutil」这件事在构建里消失。
它跑起来是什么样
下面是真实输出,不是示意。
$ lompi index fixture/store 5 package(s) in store geom 1.0.0 06b4cc0d3b1f136f mathutil 0.1.0 53ce217a387ed58a mathutil 0.2.0 c5f88a0519c41888 multi 2.0.0 8326d0125e3b111d util 0.3.0 e0e90317698ff804
mathutil 出现两次不是重复——那是两个实例。第一行那个 5 package(s) 数的是实例数,不是名字数。
$ lompi tree fixture/store multi multi@2.0.0 8326d0125e3b util@0.3.0 e0e90317698f $ lompi resolve fixture/store multi # lompi lock v1 multi 2.0.0 8326d0125e3b111d51d050e49395400524a18ddc31eb730f7afbc9cb6a4955fb util 0.3.0 e0e90317698ff804db92a01bce6b7a965e9e679eb6bb80b25f02c2d05207b041 $ lompi verify fixture/store multi lompi.lock [OK] lompi: 锁与仓库一致 (169 bytes)
验锁是把整棵树重算一遍再逐字节比对。拿错一份锁,得到的是 [DIFF] 锁与仓库不一致 并以 1 退出——不是「差不多能用」。
取件那一半
前面那些命令只读。这一半会动你的磁盘,而且它有一条不太一样的边界——先说这条。
- 网址库是一个 git 仓库 —— 布局和 store 一模一样(
<名字>/<版本>/*.lomt),没有服务端要运维。地址写在执行文件旁边的lompi.conf里,而那份配置本身就是 Loment 源码。 - fetch 不联网,它打印计划 —— Lompi 自己做不到联网:PE 上那 8 个 syscall 里没有 socket,垫片也没有
ws2_32。所以它把git clone打出来交给 shell 或 CI——和「plan不建目录」是同一条边界。 - install 才动盘,而且要明说 —— 默认装进全局 store,
--into DIR装进项目;不带--apply只出计划。--from-lock F只装 F 钉死的那些实例,对不上就拒。 - 装之前先校验 ——
lompi check逐条报:有没有*.lomt、包名与目录名对不对、每个模块名与文件名对不对、pkg.lomp的两个标签对不对。过不了的写[BAD],而且说清是哪一条。
$ lompi fetch --registry https://example.invalid/loment-registry.git # lompi fetch plan (lompi cannot do network on PE -- no socket in the 8 syscalls) # registry: https://example.invalid/loment-registry.git (from --registry) mkdir -p <global>\cache git clone --depth 1 https://example.invalid/loment-registry.git <global>\cache\loment-registry # to update later: git -C <global>\cache\loment-registry pull
它现在到哪一步
- 覆盖解析与身份,不做物化 —— 读依赖、算身份、发锁、验锁、给落盘计划。编译器那一侧的物化改名是库系统里
materialize的活,不是它的。 -
plan不建目录 —— 工具链的 Windows PE 目标没有mkdir,所以它只打计划,deps/下的目录要自己先建好。这也是「它就是 install」这句话的完整含义:它算,落盘交给上层。 - 单文件上限 256 KiB —— 超了它拒算这个包的身份,而不是截断。静默截断会算出一个错的身份,那比报错坏得多。
- 容量护栏会静默截断 —— 仓库 512 个实例、单包
use边 2048 条、目录项 4096 条、依赖树深度 256。超出这些没有报错,属于已知上限,不是设计。 - 没有注册中心服务端,Lompi 也不自己联网 —— 网址库就是一个 git 仓库(布局与 store 相同),没有服务端要运维。而
fetch给出的是计划:PE 上那 8 个 syscall 里没有 socket,垫片也没有ws2_32,所以网络那一步交给 git、由 shell 或 CI 执行。也不做二进制分发:泛型是类型定向单态化的,使用方必须看得到源码。
use 边的区别、退出码、报错对照,以及和 pip 的一张对照表,都在 Lompi · 包管理器。写库本身怎么开始,看 写一个 Loment 库。