왜 나는 Portage를 Go로 다시 작성했는가: GRPM v0.1.0 소개
Source: Dev.to
Gentoo Linux를 사용해 본 적이 있다면 Portage를 알고 있을 겁니다. 강력하고 유연하며… 파이썬 기반이죠. 수년간—말 그대로 수년 동안—나는 이 지속적인 생각을 했습니다: Portage가 Go로 작성된다면 어떨까? 빠른 컴파일, 단일 바이너리, 런타임 의존성 없음, 네이티브 동시성. 그 생각은 계속 떠올랐습니다.
지난 봄에 나는 결국 생각을 멈추고 코딩을 시작했습니다. “얼마나 어려운지 한번 해보자”는 생각으로 시작했지만 8 months에 걸친 집중 개발, ~60 000줄의 Go 코드, 그리고 패키지 관리자의 완전한 재‑구현으로 이어졌습니다.
오늘 나는 **GRPM v0.1.0 (Go Resource Package Manager)**를 공개합니다—현대적인 아키텍처와 충돌 없는 의존성 해결을 보장하는 Portage의 드롭‑인 대체제입니다.
Source:
왜 SAT‑기반 해결자를 사용할까?
전통적인 의존성 해결 알고리즘은 휴리스틱에 의존합니다. 충돌이 발생하면 포기하거나 최적이 아닌 선택을 하는 경우가 많습니다. Portage의 해결자는 정교하지만, 순환 의존성과 슬롯 충돌이 있는 복잡한 그래프에서는 여전히 실패할 수 있습니다.
나는 수학적으로 보장된 해결책을 원했습니다. 즉, 해가 존재한다면 반드시 찾을 수 있는 방법 말이죠.
핵심 아이디어
GRPM은 핵심에 Boolean Satisfiability (SAT) 솔버를 사용합니다.
- 각 패키지 버전은 Boolean 변수가 됩니다.
- 각 의존성은 논리 절(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)
}
이렇게 하면 의존성 해결을 잘 연구된 수학 문제로 바꿀 수 있습니다. 유효한 설치가 존재한다면 SAT 솔버가 이를 찾아냅니다.
| Property | Benefit |
|---|---|
| Completeness | 해가 존재하면 찾아냄 |
| Conflict handling | 여러 슬롯 버전, 차단자, USE 플래그 제약 → SAT 절 |
| Speed | 최신 SAT 솔버는 수백만 변수도 처리 |
| Dependencies | gophersat 사용, 순수 Go SAT 솔버 (CGO 없음) |
아키텍처 (Domain‑Driven Design)
┌─────────────────────────────────────┐
│ 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
The binary‑package subsystem handles:
- 다양한 압축 형식 (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
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은 모든 PMS 단계(pkg_setup, src_unpack, src_prepare, src_configure, src_compile, src_install)를 구현합니다.
리포지토리 동기화
외부 rsync 바이너리가 필요하지 않습니다—GRPM은 순수 Go 구현인 gokrazy/rsync를 사용합니다.
# 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은 Go의 표준 regexp 패키지 대신 coregex를 사용합니다. 성능 차이가 극적입니다:
| 벤치마크 | 표준 라이브러리 regexp | coregex | 속도 향상 |
|---|---|---|---|
| Compile | 5 100 ns | 23 ns | 221× |
| Match | 185 ns | 1.5 ns | 123× |
모든 정규식 패턴은 패키지 초기화 시 미리 컴파일됩니다:
// internal/repo/ebuild_parser.go
var (
ebuildVarRe = coregex.MustCompile(`(?m)^([A-Z_][A-Z0-9_]*)="([^"]*)"`)
ebuildAtomVersionRe = coregex.MustCompile(`^(.+?)-(\d.*)$`)
)
명령어 참조 (Portage‑친화적)
| Command | Description |
|---|---|
grpm resolve | SAT 솔버를 사용하여 의존성 해결 |
grpm install | 패키지 설치 (바이너리 또는 소스) |
grpm emerge | 소스에서 패키지 빌드 |
grpm remove | 설치된 패키지 제거 |
grpm search | 패키지 검색 |
grpm info | 패키지 정보 표시 |
grpm sync | 저장소 동기화 |
grpm update | @world/@system 패키지 업데이트 |
grpm depclean | 고아 패키지 제거 |
grpm status | 데몬 상태 표시 |
드라이‑런 및 확인
# Show what would happen (dry‑run)
grpm install --pretend app-misc/hello
# Ask for confirmation
sudo grpm install --ask app-misc/hello
--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]
설치
# 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 %
Core Dependencies
| Dependency | Purpose |
|---|---|
gophersat | 의존성 해결을 위한 SAT 솔버 |
cobra | CLI 프레임워크 |
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 빌드 시스템 미지원
이러한 문제들은 향후 릴리스에서 해결될 예정입니다.
로드맵 – v1.0.0 (2026년 2분기)
- Portage 명령 호환성 완전 구현
- 전체 eclass 지원
- EAPI 8 기능
- CMake, Meson 및 Cargo 빌드 시스템 지원
- 대규모 의존성 그래프에 대한 성능 최적화
- 실제 Gentoo 시스템에서의 프로덕션 검증
왜 GRPM을 만들까요?
- 학습 – 패키지 관리자는 매력적인 시스템입니다. 처음부터 하나를 구축하면 의존성 그래프, 제약 해결, 파일 시스템 작업 및 시스템 통합에 대해 배울 수 있습니다.
- 성능 – Go의 빠른 컴파일, 효율적인 메모리 사용, 뛰어난 동시성은 시스템 도구에 이상적입니다.
- 커뮤니티 – Gentoo는 현대적인 도구를 필요로 합니다. GRPM이 몇 명이라도 사용자에게 도움이 된다면 그 노력은 가치가 있습니다.
직접 사용해 보기
- 저장소:
- 릴리스:
- 문서: CLI Reference (link to be added)
기여
기여를 환영합니다! 버그 보고, 기능 요청, 혹은 코드 기여 등 어떤 형태든 도움이 됩니다.
직접 패키지 매니저를 만들어 보셨나요? 아니면 다른 분야에서 SAT 솔버를 사용한 경험이 있으신가요? 댓글로 이야기를 들려주세요.