为什么我用 Go 重写了 Portage:介绍 GRPM v0.1.0

发布: (2026年1月9日 GMT+8 02:39)
9 min read
原文: Dev.to

Source: Dev.to

如果你曾经使用过 Gentoo Linux,你一定了解 Portage。它功能强大、灵活,而且……基于 Python。多年来——真的多年——我一直有一个念头:*如果 Portage 用 Go 来实现会怎样?*快速编译、单一二进制文件、无运行时依赖、原生并发。这个想法一直挥之不去。

去年春天,我终于不再只想而是开始编码。最初的“看看有多难”逐渐演变为 8 months 的高强度开发,约 60 000 行 Go 代码,以及对包管理器的完整重新实现。

今天,我发布 GRPM v0.1.0 (Go Resource Package Manager)——一个可直接替代 Portage 的工具,拥有现代化架构并保证冲突自由的依赖解析。

为什么使用基于 SAT 的解析器?

传统的依赖解析算法依赖于启发式方法。当它们遇到冲突时,往往会放弃或做出次优选择。Portage 的解析器虽然很复杂,但在面对包含循环依赖和槽冲突的复杂图时仍可能失败。

我想要一种 数学上有保证 的方式,只要存在解就能找到它。

核心思路

GRPM 在核心使用布尔可满足性(SAT)求解器。

  • 每个软件包版本对应一个布尔变量。
  • 每个依赖转化为一个逻辑子句。
// internal/solver/gophersat_adapter.go
type GophersatAdapter struct {
    clauses  [][]int
    vars     map[string]int            // name@version -> var ID
    packages map[string][]*pkg.Package // name -> []versions
}

func (g *GophersatAdapter) AddPackage(p *pkg.Package) {
    key := p.Name + "@" + p.Version
    g.packages[p.Name] = append(g.packages[p.Name], p)
    g.getVarID(key)
}

这将依赖解析转化为一个研究成熟的数学问题。如果存在有效的安装方案,SAT 求解器就会找到它。

属性好处
完整性如果存在解,它会找到
冲突处理多版本槽、阻塞器、USE 标志约束 → SAT 子句
速度现代 SAT 求解器可以处理数百万变量
依赖使用 gophersat,一个纯 Go 的 SAT 求解器(无 CGO)

架构(领域驱动设计)

┌─────────────────────────────────────┐
│  CLI / Daemon Layer                 │
├─────────────────────────────────────┤
│  Application Layer   ← Use cases    │
├─────────────────────────────────────┤
│  Domain Layer         ← Business   │
│   (pkg, solver)                    │
├─────────────────────────────────────┤
│  Infrastructure Layer ← Repos, sync, install │
└─────────────────────────────────────┘

领域层不关心 Portage 文件格式或文件系统细节,从而使代码库易于测试和扩展。

包格式

GRPM 支持现代 GPKG (.gpkg.tar) 和传统 TBZ2 (.tbz2) 两种格式。

# Install from binary package
sudo grpm install --binpkg www-servers/nginx

# Build binary package from installed
sudo grpm build app-misc/hello-2.10

二进制包子系统处理:

  • 多种压缩格式(zstd、xz、gzip、bzip2)
  • 包签名(GPG、SSH、RSA)
  • 远程 binhost 支持

从源码构建

# Build from source with 8 parallel jobs
sudo grpm emerge --jobs 8 dev-lang/go

# Show build plan first
grpm emerge --pretend app-misc/hello

输出

*** Dependency resolution (--pretend mode):
[ebuild  N    ] sys-libs/zlib-1.2.13 [0]
[ebuild  N    ] app-misc/hello-2.10 [0]

Total: 2 package(s)

GRPM 实现了所有 PMS 阶段:pkg_setupsrc_unpacksrc_preparesrc_configuresrc_compilesrc_install

同步仓库

不需要外部的 rsync 二进制文件——GRPM 使用 gokrazy/rsync,这是一个纯 Go 实现。

# Auto‑select best method
sudo grpm sync

# Use Git with GPG verification
sudo grpm sync --method git

# Native Go rsync (faster, no GPG)
sudo grpm sync --method rsync

守护进程模式(gRPC 与 REST)

# Start daemon
sudo grpm daemon

# CLI auto‑connects to daemon if running
grpm status
  • gRPC 使用 Unix 套接字 (/var/run/grpm.sock)
  • REST API 通过 HTTP (127.0.0.1:8080)
  • 具备冲突检测的作业队列
  • 支持并行操作

Btrfs/ZFS 上的快照

GRPM 会在执行软件包操作前自动创建快照。

# Snapshot created automatically
sudo grpm install --snapshot-dir /.snapshots sys-libs/zlib

如果安装失败,您可以回滚到该快照。

正则性能提升

GRPM 使用 coregex 替代 Go 的标准 regexp 包。性能差异非常显著:

基准标准库 regexpcoregex加速比
编译5 100 ns23 ns221×
匹配185 ns1.5 ns123×

所有正则表达式在包初始化时预编译:

// internal/repo/ebuild_parser.go
var (
    ebuildVarRe = coregex.MustCompile(`(?m)^([A-Z_][A-Z0-9_]*)="([^"]*)"`)

    ebuildAtomVersionRe = coregex.MustCompile(`^(.+?)-(\d.*)$`)
)

命令参考(Portage‑familiar)

CommandDescription
grpm resolve使用 SAT 求解器解析依赖
grpm install安装软件包(二进制或源码)
grpm emerge从源码构建软件包
grpm remove移除已安装的软件包
grpm search搜索软件包
grpm info显示软件包信息
grpm sync同步仓库
grpm update更新 @world/@system 软件包
grpm depclean删除孤立软件包
grpm status显示守护进程状态

干运行 & 确认

# 显示将会发生的操作(干运行)
grpm install --pretend app-misc/hello

# 请求确认
sudo grpm install --ask app-misc/hello

使用 --ask 的输出

*** 安装计划:
[ebuild  N    ] sys-libs/zlib-1.2.13 to / USE="..."
[ebuild  N    ] app-misc/hello-2.10 to / USE="..."

总计: 2 个软件包

您想合并这些软件包吗?[Yes/No]

安装

# Download
wget https://github.com/grpmsoft/grpm/releases/download/v0.1.0/grpm_0.1.0_linux_x86_64.tar.gz

# Extract and install
tar -xzf grpm_0.1.0_linux_x86_64.tar.gz
sudo install -m 0755 grpm /usr/bin/grpm

使用 GRPM,享受更快、无依赖冲突的 Gentoo 体验!

快速命令

操作命令
同步仓库sudo grpm sync
搜索软件包grpm search firefox
显示软件包信息grpm info dev-lang/go
从源码构建(模拟)sudo grpm emerge --pretend app-misc/hello
从源码构建(实际)sudo grpm emerge app-misc/hello
从二进制安装sudo grpm install --binpkg app-misc/hello

项目概览

  • 语言: Go 1.25+
  • 许可证: Apache‑2.0
  • 支持平台: Linux (x86_64, arm64, armv7, armv6, i386)
  • 代码库规模: ~60 000 行 Go 代码
  • 测试覆盖率: ~70 %

核心依赖

依赖用途
gophersat用于依赖解析的 SAT 求解器
cobraCLI 框架
grpc守护进程通信
gokrazy/rsync本地 rsync 实现
modernc.org/sqlite纯 Go SQLite 用于缓存
coregex高性能正则表达式

所有依赖均为纯 Go — 无需 CGO。

当前限制 (v0.1.0)

  • Ebuild 执行仅限于 autotools 工作流 (./configure && make)
  • eclass 支持受限 (toolchain-funcs, eutils, multilib)
  • 不支持 EAPI 8 功能
  • 不支持 CMake/Meson 构建系统

这些问题将在未来的版本中得到解决。

Roadmap – v1.0.0 (Q2 2026)

  • 完成 Portage 命令兼容性
  • 完整的 eclass 支持
  • EAPI 8 功能
  • 支持 CMake、Meson 和 Cargo 构建系统
  • 对大型依赖图的性能优化
  • 在真实 Gentoo 系统上进行生产验证

为什么要构建 GRPM?

  1. 学习 – 包管理器是非常有趣的系统。从零开始构建一个包管理器可以让你了解依赖图、约束求解、文件系统操作以及系统集成。
  2. 性能 – Go 的快速编译、高效的内存使用以及出色的并发特性,使其非常适合用于系统工具。
  3. 社区 – Gentoo 需要现代化的工具。如果 GRPM 能帮助哪怕少数用户,这份努力也是值得的。

试一试

  • 代码仓库:
  • 发布:
  • 文档: CLI 参考(链接待添加)

贡献

欢迎贡献!无论是错误报告、功能请求,还是代码贡献——每一点帮助都很重要。

您尝试过构建自己的包管理器吗?或者在其他领域有使用 SAT 求解器的经验吗?欢迎在评论中分享。

Back to Blog

相关文章

阅读更多 »

轻量级跨平台 Hosts 管理工具

简介 Go Hosts 是一款使用 Go + Fyne 开发的轻量级跨平台 Hosts 管理工具,支持 Windows 与 macOS(Intel)。相较于体积庞大的 Electron 应用(如 SwitchHosts),Go Hosts 体积更小、编译打包灵活,适合对小工具有执念的用户。 - GitHub 开源地址...