DEV Community

Cover image for Why I Rewrote Portage in Go: Introducing GRPM v0.1.0
Andrey Kolkov
Andrey Kolkov

Posted on • Originally published at github.com

Why I Rewrote Portage in Go: Introducing GRPM v0.1.0

October 2019, Gentoo Forums:

Me: "I'd rather rewrite Portage on Golang."

Reply: "We look forward to you having some work to show another 5 years from now."

forums.gentoo.org

6 years and ~60,000 lines of code later — here it is.


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 was written in Go? Fast compilation, single binary, no runtime dependencies, native concurrency. The idea kept coming back.

Last spring, I finally decided to stop thinking and start coding. What began as "let's see how hard this is" turned into 8 months of intensive development and a complete reimplementation 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.

The Problem with Traditional Package Managers

Traditional dependency resolution algorithms work with heuristics. When they encounter conflicts, they often give up or make suboptimal choices. Portage's resolver is sophisticated, but it can still fail on complex dependency graphs with circular dependencies and slot conflicts.

I wanted something mathematically guaranteed to find a solution if one exists.

The Solution: SAT-Based Dependency Resolution

GRPM uses a Boolean Satisfiability (SAT) solver at its core. Every package version becomes a Boolean variable, and 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)
}
Enter fullscreen mode Exit fullscreen mode

This approach transforms dependency resolution into a well-studied mathematical problem. If a valid installation exists, the SAT solver will find it.

Why SAT Solvers?

  • Completeness: If a solution exists, it finds it
  • Conflict handling: Multiple slot versions, blockers, USE flag constraints — all become SAT clauses
  • Speed: Modern SAT solvers handle millions of variables

GRPM uses gophersat, a pure Go SAT solver. No external dependencies, no CGO.

Architecture: Clean Design with DDD

GRPM follows Domain-Driven Design with a layered architecture:

┌─────────────────────────────────────┐
│  CLI / Daemon Layer                 │
├─────────────────────────────────────┤
│  Application Layer                  │  ← Use cases
├─────────────────────────────────────┤
│  Domain Layer                       │  ← Business logic
│  (pkg, solver)                      │
├─────────────────────────────────────┤
│  Infrastructure Layer               │  ← Repository, sync, install
└─────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The domain layer knows nothing about Portage file formats or filesystem details. This makes the codebase testable and the architecture extensible.

Key Features in v0.1.0

1. Full Binary Package Support

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
Enter fullscreen mode Exit fullscreen mode

The binary package subsystem handles:

  • Multiple compression formats (zstd, xz, gzip, bzip2)
  • Package signing (GPG, SSH, RSA)
  • Remote binhost support

2. Source Building (Emerge Command)

The emerge command executes full ebuild phases:

# 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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

GRPM implements all PMS phases: pkg_setup, src_unpack, src_prepare, src_configure, src_compile, src_install.

3. Native Repository Sync

No external rsync binary required. GRPM uses gokrazy/rsync, a pure Go rsync 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
Enter fullscreen mode Exit fullscreen mode

4. Daemon Architecture

GRPM can run as a background service with gRPC and REST APIs:

# Start daemon
sudo grpm daemon

# CLI auto-connects to daemon if running
grpm status
Enter fullscreen mode Exit fullscreen mode

The daemon provides:

  • 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

5. Transactional Updates

On Btrfs or ZFS, GRPM automatically creates snapshots before package operations:

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

If installation fails, you can roll back to the snapshot.

Performance Optimization: 120x Faster Regex

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 221x
Match 185 ns 1.5 ns 123x

All regex patterns are precompiled at package initialization:

// internal/repo/ebuild_parser.go
var (
    ebuildVarRe = coregex.MustCompile(`(?m)^([A-Z_][A-Z0-9_]*)="([^"]*)"`)
    ebuildAtomVersionRe = coregex.MustCompile(`^(.+?)-(\d.*)$`)
)
Enter fullscreen mode Exit fullscreen mode

For a package manager parsing thousands of ebuilds, this optimization matters.

CLI Reference

GRPM provides a familiar interface for Portage users:

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

Pretend and Ask Modes

Like Portage, GRPM supports dry-run and confirmation modes:

# Show what would happen (dry-run)
grpm install --pretend app-misc/hello

# Ask for confirmation
sudo grpm install --ask app-misc/hello
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

Quick Start

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

# Verify
grpm -V
Enter fullscreen mode Exit fullscreen mode

Basic Workflow

# Sync repository
sudo grpm sync

# Search for packages
grpm search firefox

# Show package info
grpm info dev-lang/go

# Build from source
sudo grpm emerge --pretend app-misc/hello
sudo grpm emerge app-misc/hello

# Install from binary
sudo grpm install --binpkg app-misc/hello
Enter fullscreen mode Exit fullscreen mode

Technical Details

  • Language: Go 1.25+
  • License: Apache-2.0
  • Platforms: Linux (x86_64, arm64, armv7, armv6, i386)
  • Codebase: ~60,000 lines of Go
  • Test Coverage: ~70%

Dependencies

GRPM uses carefully selected 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.

Known Limitations

GRPM v0.1.0 is a foundation release. Current limitations:

  • Ebuild execution limited to autotools workflow (./configure && make)
  • Limited eclass support (toolchain-funcs, eutils, multilib)
  • No EAPI 8 features
  • CMake/Meson build systems not supported

These will be addressed in future releases.

What's Next

Development continues with iterative v0.x.x releases:

  • Full EAPI 8 support
  • CMake/Meson build system support
  • Extended eclass support
  • Performance optimization for large dependency graphs
  • Community testing on real Gentoo systems

v1.0.0 will come after community validation and API stabilization — no fixed timeline, quality over deadlines.

Why Build This?

Three reasons:

  1. Learning: Package managers are fascinating systems. Building one from scratch teaches you about dependency graphs, constraint solving, filesystem operations, and system integration.

  2. Performance: Go's performance characteristics — fast compilation, efficient memory usage, excellent concurrency — make it ideal for system tools.

  3. Community: Gentoo deserves modern tooling. If GRPM helps even a few users, the effort is worthwhile.

Try It Out

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.

Top comments (0)