Why I Rewrote Portage in Go: Introducing GRPM v0.1.0
Source: Dev.to
If you’ve ever used Gentoo Linux, you know Portage. It’s powerful, flexible, and… Python‑based. For years—literally years—I had this persistent idea: what if Portage were written in Go? Fast compilation, single binary, no runtime dependencies, native concurrency. The idea kept coming back.
Last spring I finally stopped thinking and started coding. What began as “let’s see how hard this is” turned into 8 months of intensive development, ~60 000 lines of Go, and a complete re‑implementation of a package manager.
Today I’m releasing GRPM v0.1.0 (Go Resource Package Manager)—a drop‑in replacement for Portage with modern architecture and guaranteed conflict‑free dependency resolution.
Why a SAT‑based resolver?
Traditional dependency resolution algorithms rely on heuristics. When they encounter conflicts they often give up or make sub‑optimal choices. Portage’s resolver is sophisticated, but it can still fail on complex graphs with circular dependencies and slot conflicts.
I wanted something mathematically guaranteed to find a solution if one exists.
Core idea
GRPM uses a Boolean Satisfiability (SAT) solver at its core.
- Every package version becomes a Boolean variable.
- Every dependency becomes a logical clause.
// 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)
}
This transforms dependency resolution into a well‑studied mathematical problem. If a valid installation exists, the SAT solver will find it.
| Property | Benefit |
|---|---|
| Completeness | If a solution exists, it finds it |
| Conflict handling | Multiple slot versions, blockers, USE flag constraints → SAT clauses |
| Speed | Modern SAT solvers handle millions of variables |
| Dependencies | Uses gophersat, a pure‑Go SAT solver (no CGO) |
Architecture (Domain‑Driven Design)
┌─────────────────────────────────────┐
│ CLI / Daemon Layer │
├─────────────────────────────────────┤
│ Application Layer ← Use cases │
├─────────────────────────────────────┤
│ Domain Layer ← Business │
│ (pkg, solver) │
├─────────────────────────────────────┤
│ Infrastructure Layer ← Repos, sync, install │
└─────────────────────────────────────┘
The domain layer knows nothing about Portage file formats or filesystem details, making the codebase testable and extensible.
Package formats
GRPM supports both modern GPKG (.gpkg.tar) and legacy TBZ2 (.tbz2) formats.
# Install from binary package
sudo grpm install --binpkg www-servers/nginx
# Build binary package from installed
sudo grpm build app-misc/hello-2.10
The binary‑package subsystem handles:
- Multiple compression formats (zstd, xz, gzip, bzip2)
- Package signing (GPG, SSH, RSA)
- Remote binhost support
Building from source
# 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
Output
*** 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 implements all PMS phases: pkg_setup, src_unpack, src_prepare, src_configure, src_compile, src_install.
Syncing repositories
No external rsync binary is required—GRPM uses gokrazy/rsync, a pure‑Go implementation.
# 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
Daemon mode (gRPC & REST)
# Start daemon
sudo grpm daemon
# CLI auto‑connects to daemon if running
grpm status
- gRPC on Unix socket (
/var/run/grpm.sock) - REST API on HTTP (
127.0.0.1:8080) - Job queue with conflict detection
- Parallel operation support
Snapshots on Btrfs/ZFS
GRPM automatically creates snapshots before package operations.
# Snapshot created automatically
sudo grpm install --snapshot-dir /.snapshots sys-libs/zlib
If installation fails, you can roll back to the snapshot.
Regex performance boost
GRPM uses coregex instead of Go’s standard regexp package. The performance difference is dramatic:
| Benchmark | stdlib regexp | coregex | Speedup |
|---|---|---|---|
| Compile | 5 100 ns | 23 ns | 221× |
| Match | 185 ns | 1.5 ns | 123× |
All regex patterns are pre‑compiled at package initialization:
// internal/repo/ebuild_parser.go
var (
ebuildVarRe = coregex.MustCompile(`(?m)^([A-Z_][A-Z0-9_]*)="([^"]*)"`)
ebuildAtomVersionRe = coregex.MustCompile(`^(.+?)-(\d.*)$`)
)
Command reference (Portage‑familiar)
| Command | Description |
|---|---|
grpm resolve | Resolve dependencies with SAT solver |
grpm install | Install packages (binary or source) |
grpm emerge | Build packages from source |
grpm remove | Remove installed packages |
grpm search | Search for packages |
grpm info | Display package information |
grpm sync | Synchronize repository |
grpm update | Update @world/@system packages |
grpm depclean | Remove orphaned packages |
grpm status | Show daemon status |
Dry‑run & confirmation
# Show what would happen (dry‑run)
grpm install --pretend app-misc/hello
# Ask for confirmation
sudo grpm install --ask app-misc/hello
Output with --ask
*** Installation plan:
[ebuild N ] sys-libs/zlib-1.2.13 to / USE="..."
[ebuild N ] app-misc/hello-2.10 to / USE="..."
Total: 2 package(s)
Would you like to merge these packages? [Yes/No]
Installation
# 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
Enjoy a faster, dependency‑conflict‑free Gentoo experience with GRPM!
Quick Commands
| Action | Command |
|---|---|
| Sync repository | sudo grpm sync |
| Search for packages | grpm search firefox |
| Show package info | grpm info dev-lang/go |
| Build from source (pretend) | sudo grpm emerge --pretend app-misc/hello |
| Build from source (real) | sudo grpm emerge app-misc/hello |
| Install from binary | sudo grpm install --binpkg app-misc/hello |
Project Overview
- Language: Go 1.25+
- License: Apache‑2.0
- Supported Platforms: Linux (x86_64, arm64, armv7, armv6, i386)
- Codebase Size: ~60 000 lines of Go
- Test Coverage: ~70 %
Core Dependencies
| Dependency | Purpose |
|---|---|
gophersat | SAT solver for dependency resolution |
cobra | CLI framework |
grpc | Daemon communication |
gokrazy/rsync | Native rsync implementation |
modernc.org/sqlite | Pure‑Go SQLite for caching |
coregex | High‑performance regex |
All dependencies are pure Go — no CGO required.
Current Limitations (v0.1.0)
- Ebuild execution limited to the autotools workflow (
./configure && make) - Limited eclass support (
toolchain-funcs,eutils,multilib) - No EAPI 8 features
- CMake/Meson build systems not supported
These issues will be addressed in future releases.
Roadmap – v1.0.0 (Q2 2026)
- Complete Portage command compatibility
- Full eclass support
- EAPI 8 features
- Support for CMake, Meson, and Cargo build systems
- Performance optimisation for large dependency graphs
- Production validation on real Gentoo systems
Why Build GRPM?
- Learning – Package managers are fascinating systems. Building one from scratch teaches you about dependency graphs, constraint solving, filesystem operations, and system integration.
- Performance – Go’s fast compilation, efficient memory usage, and excellent concurrency make it ideal for system tools.
- Community – Gentoo deserves modern tooling. If GRPM helps even a few users, the effort is worthwhile.
Try It Out
- Repository:
- Releases:
- Documentation: CLI Reference (link to be added)
Contributing
Contributions are welcome! Whether it’s bug reports, feature requests, or code contributions — every bit helps.
Have you tried building your own package manager? Or have experience with SAT solvers in other domains? I’d love to hear about it in the comments.