aptcache

package
v2.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: GPL-3.0 Imports: 25 Imported by: 0

Documentation

Overview

Package aptcache is an in-memory index of apt + dpkg metadata.

It parses /var/lib/apt/lists/*_Packages and /var/lib/dpkg/status (deb822 format) once, giving callers O(1) Lookup, transitive ResolveDeps with virtual-package handling, and a concurrent Download backed by grab.

Typical use during cross-compile dep partitioning:

cache := aptcache.Load()
info, ok := cache.Lookup("libssl-dev")
if ok && info.ArchitectureAll() { ... }

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompareDebVersion added in v2.1.9

func CompareDebVersion(a, b string) int

CompareDebVersion returns -1, 0, or +1 comparing two Debian version strings per the deb-version(7) algorithm. Empty strings sort lowest.

func GoarchToDebArch added in v2.1.2

func GoarchToDebArch() string

GoarchToDebArch returns the Debian architecture name for the current runtime.GOARCH. Used by Lookup and ResolveDeps to default to the host architecture during multi-arch resolution.

func StoreGlobal added in v2.1.0

func StoreGlobal(c *Cache)

StoreGlobal replaces the process-global cache returned by Load. Intended for cross-package test injection; call with a cache built via NewEmptyCache + AddEntry before the code under test calls Load.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache is an in-memory index of package metadata keyed by package name. It merges data from the apt package index and the dpkg status database. The zero value is not usable; call Load.

func Load

func Load() *Cache

Load returns the process-global Cache, loading it on the first call. Subsequent calls return the cached result immediately. The cache is always non-nil; on non-Debian hosts it is empty.

func NewEmptyCache added in v2.1.0

func NewEmptyCache() *Cache

NewEmptyCache creates an empty Cache suitable for injection in tests. Use StoreGlobal to make it the active singleton returned by Load.

func Reload

func Reload() *Cache

Reload discards the cached result and re-reads the apt/dpkg metadata from disk. Call this after running apt-get update so that packages from newly added repositories are visible to subsequent Lookup calls.

The new cache is built before the old one is replaced, so concurrent readers always see a consistent snapshot (the old one until the swap, the new one afterwards).

func (*Cache) AddEntry added in v2.1.0

func (c *Cache) AddEntry(info *PackageInfo)

AddEntry inserts or replaces a PackageInfo entry in the cache. Intended for test setup; not safe for concurrent use during population.

func (*Cache) CapabilityCount added in v2.1.0

func (c *Cache) CapabilityCount() int

CapabilityCount returns the number of virtual package names (Provides) in the providers index.

func (*Cache) Download added in v2.1.0

func (c *Cache) Download(ctx context.Context, destDir string, pkgs []string) error

Download fetches the named packages into destDir using the apt package index metadata (Filename, SHA256, Size, BaseURL fields).

Implementation: uses cavaliergopher/grab (the same library yap's pkg/download uses for source downloads). grab gives us for free:

  • Concurrent batched downloads (DoBatch with a fixed worker pool).
  • HTTP Range / resume on partially-downloaded files (so an interrupted `yap build` doesn't re-fetch hundreds of MB).
  • In-stream SHA-256 verification via Request.SetChecksum, with delete-on-error so a corrupt .deb never lingers at destDir.

Performance: a 100-package closure that took ~30s with sequential net/http drops to ~5-8s against archive.ubuntu.com.

Returns an error if any package is not found in the index or any download fails. All downloads continue until completion (or context cancel); errors are aggregated and the first one returned. Partial files left by failed downloads are removed by grab itself.

Most callers should prefer DownloadClosure, which performs transitive resolution before downloading. Use Download directly only when you already have an explicit, pre-resolved list of package names.

func (*Cache) DownloadClosure added in v2.1.0

func (c *Cache) DownloadClosure(
	ctx context.Context, destDir string, seeds []string,
) (resolved []*PackageInfo, unresolved []string, err error)

DownloadClosure resolves the transitive closure of the supplied seed package names, downloads every resulting .deb into destDir, and returns the resolved PackageInfo slice in dependency order (deps before dependents) plus the list of names that could not be resolved.

Behaviour:

  • Packages already marked Installed are skipped (their dependencies are still walked so transitive runtime deps reachable only through an installed library still get pulled in).
  • Virtual packages are resolved to their first concrete provider via the reverse-Provides index.
  • "foo | bar" alternatives resolve to the first option.
  • Architecture / version qualifiers on seed names are stripped before lookup; see Lookup for the bare-name contract.
  • Cycles are short-circuited by ResolveDeps' internal `seen` map.

This is the helper that callers should reach for when they need "everything required to actually use these packages on the target filesystem" — most importantly, cross-build runtime dep extraction in pkg/builders/common.DownloadAndExtractCrossDeps, where missing transitive deps cause cross-link failures like:

ld: warning: libvpx.so.12, needed by libavcodec.so, not found

because PKGBUILDs only declare the direct dep (vendor-ffmpeg) while the transitive arch-specific libs (vendor-libvpx, vendor-x264) are not surfaced unless we walk the dep graph ourselves.

func (*Cache) InstalledNames added in v2.1.10

func (c *Cache) InstalledNames() map[string]bool

InstalledNames returns the set of package names (bare, without arch qualifier) currently installed on the host according to the merged dpkg status overlay. A name is considered installed if at least one arch variant is marked Installed.

Used by cross-build extractors to avoid overlaying foreign-arch payloads onto host binaries whose path is shared across architectures (e.g. /usr/bin/sudo): even though dpkg multi-arch annotates packages with separate entries per arch, the extractor writes to a single root tree where /usr/bin/sudo exists once and is held open by the running sudo process.

func (*Cache) Lookup

func (c *Cache) Lookup(name string) (PackageInfo, bool)

Lookup returns the PackageInfo for the named package and whether it was found. The name may include an arch qualifier (e.g., "gcc-11:arm64"). Without a qualifier, it tries the host architecture, "all", then any arch.

func (*Cache) PackageCount added in v2.1.0

func (c *Cache) PackageCount() int

PackageCount returns the number of packages indexed in the cache. Useful for diagnostic logging after a Reload to surface how many entries were parsed.

func (*Cache) ResolveDeps added in v2.1.0

func (c *Cache) ResolveDeps(seeds []string) ([]*PackageInfo, []string, error)

ResolveDeps performs transitive dependency resolution starting from the given seed packages. It returns the topologically-ordered list of packages that must be downloaded (deps before dependents), and a list of unresolvable deps (not in the index, possibly virtual).

Packages already marked as Installed are skipped (their Pre-Depends are still traversed for completeness, but the package itself is not added to the install list).

Seeds may include an arch qualifier ("libc6-dev:arm64"). The arch is preserved and inherited by all transitive dependencies, ensuring correct multi-arch resolution when both amd64 and arm64 indexes are loaded.

Implementation: post-order DFS keyed by package name:arch. Cycles are short-circuited via the `visited` map (set before recursion). Go's growable goroutine stack handles real-world Debian dep depths (typically <20) comfortably; an iterative variant would only be worth the complexity for a pathologically deep graph.

func (*Cache) ResolveVirtual

func (c *Cache) ResolveVirtual(name string) string

ResolveVirtual returns the first concrete provider of a virtual package, or the original name if the package is real (has a candidate) or unknown. This replaces the `apt-cache policy` + `apt-cache showpkg` two-step.

type PackageInfo

type PackageInfo struct {
	// Name is the package name (e.g. "gcc", "libssl-dev").
	Name string
	// Architecture is the value of the Architecture field (e.g. "amd64", "all", "arm64").
	Architecture string
	// Essential is true when the package carries "Essential: yes".
	Essential bool
	// MultiArch is the value of the Multi-Arch field ("same", "foreign", "allowed", or "").
	MultiArch string
	// Installed is true when the dpkg status database reports the package as
	// "install ok installed".
	Installed bool
	// HasCandidate is true when the package has at least one version in the apt index
	// (i.e. it is a real, installable package — not a pure virtual).
	HasCandidate bool
	// Filename is the relative path of the .deb in the apt repository
	// (e.g. "pool/main/g/gcc/gcc_12.2.0-14_amd64.deb").
	Filename string
	// SHA256 is the expected SHA-256 checksum of the .deb file.
	SHA256 string
	// Size is the expected size of the .deb file in bytes.
	Size int64
	// BaseURL is the repo base URL (scheme + host + path) where this package's
	// Filename is relative to. E.g. "https://ports.ubuntu.com/ubuntu-ports/".
	// Empty for packages from the dpkg status DB (not downloadable).
	BaseURL string
	// Depends is the parsed Depends field (list of package names without version constraints).
	Depends []string
	// PreDepends is the parsed Pre-Depends field (list of package names without version constraints).
	PreDepends []string
	// Version is the raw deb822 Version field. Used to resolve same name:arch
	// collisions across repositories (highest version wins).
	Version string
}

PackageInfo holds the subset of deb822 fields needed for cross-compilation partition decisions and virtual package resolution.

func (PackageInfo) ArchitectureAll

func (p PackageInfo) ArchitectureAll() bool

ArchitectureAll reports whether the package is architecture-independent.

func (PackageInfo) MultiArchForeign

func (p PackageInfo) MultiArchForeign() bool

MultiArchForeign reports whether a single host-arch copy of this package satisfies dependencies from any architecture (Multi-Arch: foreign or allowed). These are tools and daemons that run on the build host — they must NOT be qualified with a target arch during cross-compilation.

Multi-Arch: same (dev libraries) is intentionally excluded: those packages must be installed separately for each architecture.

func (PackageInfo) MultiArchSame

func (p PackageInfo) MultiArchSame() bool

MultiArchSame reports whether this package must be installed separately for each architecture (Multi-Arch: same). These are typically -dev libraries that need to be qualified with the target arch during cross-compilation.

type SourceEntry added in v2.1.0

type SourceEntry struct {
	URL           string   // e.g. "https://archive.ubuntu.com/ubuntu/"
	Suite         string   // e.g. "jammy"
	Components    []string // e.g. ["main", "universe"]
	Architectures []string // e.g. ["amd64"] — empty means default
	SignedBy      string   // path to GPG keyring file, or ""
}

SourceEntry represents a single apt source with its URL, suite, components, and architectures. Exported for use by pkg/aptrepo.

func LoadSources added in v2.1.0

func LoadSources() []SourceEntry

LoadSources parses /etc/apt/sources.list and /etc/apt/sources.list.d/*.{list,sources} and returns a slice of SourceEntry for each configured source. This is exported for use by pkg/aptrepo to fetch repository metadata.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL