Lesson 17 / 20
The Package Manager Concept
A package's structure as files plus metadata, the function of the installed-package database, dependency resolution as a constraint problem, and version comparison rules.
Contents
Every tool used throughout the course was already present on the system. How it got there, how it is updated, and how it is removed was never asked.
The question is harder than it looks. Installing a program is not just copying files: the
executable goes into one directory, its libraries into another, its configuration under
/etc, its documentation into the manual directory — exactly the distribution the layout
standard from the directory hierarchy lesson defined. Done by hand, no record exists of which
file belongs to which program, and removal becomes impossible.
What a Package Is
A package is an archive carrying the files to be installed together with metadata about those files. Its structure resembles the format from the archiving lesson: a file tree plus header information. The difference is that the header is read for installation decisions.
The information metadata carries shows naming differences across package manager families, but the fields are common:
| Field | Function |
|---|---|
| Name | The package’s identity |
| Version | Which release it is |
| Architecture | Which processor family it was built for |
| Dependencies | Other packages required for it to run |
| Conflicts | Packages it cannot be installed alongside |
| Provides | The abstract capability names it satisfies |
| Checksums | A content digest of every file |
| Install scripts | Steps to run before and after installation |
The last two fields are often skipped over but are decisive. Checksums make it possible to test whether installed files have since been modified — the system-scale counterpart of the verification method from the file transfer lesson. Install scripts cover the cases file copying alone cannot: opening a user account, registering a service, adjusting a database — and because they run with root privilege, they sit at the center of package trust.
The Installed-Package Database
A package manager’s second component is a local database holding a record of every package installed on the system. The record contains the package’s metadata and the full list of files it installed.
Without this list, three operations cannot be done.
Removal. Which files to delete cannot be known; software installed by hand leaves remnants behind when removed.
Upgrading. Which files of the old version are absent from the new one cannot be known; remnant files accumulate.
Ownership query. Which package a file on the system belongs to cannot be asked; this query is used to determine which package’s responsibility a fault should be charged to.
Another consequence of the database is that manually modified files can be distinguished. Configuration files ship with the package but are edited by the administrator; on upgrade, these changes need to be preserved. Package managers compare a file’s checksum against the recorded one to tell whether it has changed, and if it has, they ask the user instead of overwriting it on upgrade.
Why Dependencies Exist
A dependency is not an abstract concept; it can be seen in the executables themselves:
$ ldd /usr/bin/ls linux-vdso.so.1 (0x0000ffff88cce000) libselinux.so.1 => /lib/aarch64-linux-gnu/libselinux.so.1 (0x0000ffff88be0000) libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff88a20000) /lib/ld-linux-aarch64.so.1 (0x0000ffff88c80000) libpcre2-8.so.0 => /lib/aarch64-linux-gnu/libpcre2-8.so.0 (0x0000ffff88970000)
The listing command does not run on its own: it depends on the standard C library, the security-labeling library, and the regular expression library. The numbers in parentheses are the libraries’ load addresses for that particular run and change on every invocation; the library names do not change. These libraries come in separate packages. The linking and loading lesson in the How Computers Work course defined this relationship; package dependencies are that relationship’s distribution-level counterpart.
The reason for using a shared library is avoiding duplication: hundreds of programs do not each carry a separate copy of the same library. The cost is that installation turns into a dependency graph.
The provides concept loosens this graph. A package can supply an abstract capability name alongside its
concrete one; other packages depend not on the concrete name but on the capability, so
different implementations doing the same job can substitute for one another. This is the
packaging-level counterpart of the /bin/sh example from the shell lesson: a dependency is
built on a role, and which program fills that role is the system’s decision.
Resolution Is a Constraint Problem
When a package’s installation is requested, the question that must be solved is: which packages, added to the currently installed set, satisfy every dependency and produce no conflict?
This is a search problem. A small example shows its shape:
| Package | Dependency | Conflict |
|---|---|---|
plot 2.0 |
graphics ≥ 3 |
— |
report 1.4 |
graphics ≤ 2 |
— |
graphics 2.9 |
— | — |
graphics 3.1 |
— | — |
plot and report cannot be installed on the same system: one wants the third release of
the graphics library, the other wants at most the second. The resolver reports this and
waits for the user to choose. On real systems this graph carries tens of thousands of nodes,
and constraints grow complex with intersections, alternatives, and version ranges.
Resolution ends in one of three states: a solution is found and a list of actions is presented; no solution exists and a conflict is reported; or multiple solutions exist and the manager’s preference rules — which repository takes priority, which version is preferred — take over.
For this reason, installation commands list what they will do and ask for confirmation before acting. The list carries not just the requested package but every dependency it pulls in and everything that needs removing; it should not be approved without being read.
Version Comparison
Since dependencies set constraints of the form “at least this version,” versions must be comparable. Comparison cannot be done lexicographically:
- Version
1.10is greater than1.9, but compared as text it looks smaller. 2.0and2.0.0are treated as equal under most rules.- Version
1.0-betais less than1.0; letter-bearing suffixes signal pre-release.
For this reason every package manager family has a defined comparison algorithm: the version string is split into numeric and alphabetic parts, numeric parts are compared as numbers, alphabetic parts are compared in order.
There is also a need to override ordering by hand. When a piece of software’s version numbering scheme changes, the new version can look smaller than the old one. To handle this, a number consulted before anything else during comparison is prepended to the version string; when this number increases, every subsequent comparison favors the new scheme.
Package Manager Classes
Tools operate at two levels, and the split exists in every family.
The low-level tool deals with a single package file: it unpacks it, places the files, records it in the database. It does not resolve dependencies; it errors out if one is missing. Querying installed packages is also at this level.
The high-level tool knows about repositories, resolves dependencies, downloads, and calls the low-level tool. This is the tool a user works with day to day.
The split has a practical consequence: if a package file is in hand and installed with the low-level tool, dependencies can be left unsatisfied. The correct approach is handing the file to the high-level tool, so missing dependencies are filled in from the repository.
There is also a domain outside the system package manager: programming languages’ own package managers. The two keep separate databases and are unaware of each other. If the same library is installed through both, which copy gets used becomes ambiguous. Common practice is installing language-level packages not into system directories but into a project- or user-specific area.
Summary
- A package is an archive carrying the files to install and metadata; the metadata includes dependency, conflict, checksum, and install-script information.
- The installed-package database is the record that makes removal, upgrading, and file ownership queries possible.
- Dependencies are the concrete result of shared libraries; abstract capability names let different implementations substitute for one another.
- Installation is a constraint problem; a solution may not exist or may not be unique, so tools present the action list for approval.
- Version comparison cannot be done lexicographically; a version string is split into numeric and alphabetic parts for comparison.
- The low-level tool deals with a single package and does not resolve dependencies; the high-level tool knows about repositories and performs resolution.
Next Step
With the concepts established, operations can be covered. The next lesson introduces the set of operations every package manager has a counterpart for, whatever its family: updating the index, installing, removing, purging along with configuration, querying, and verifying — and which of these operations can be undone.
To keep your progress and take notes, Log in
My notes
Log in to take notes.