Drop the append-only versioning doctrine; document the version script
Module APIs and the library's exported ABI are versioned and retired deliberately: a break ships as a new table version or a new symbol version node, and consumers judge the versions they are handed. Document src/libdpm-core.map — what it pins, what belongs in it, and how a generation is retired — and declare DPM_CORE_0.1 as the shape the next retired node takes.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
DPM is the package manager for Dark Horse Linux. At its center is **libdpm-core**, a shared library that discovers modules, validates them, routes calls to them, reports their versions, and provides configuration and logging. Modules — shared objects in the module directory — implement package functionality.
|
||||
|
||||
**The dpm binary** is the command-line tool, and it is one consumer of that library. Build systems, image builders, distribution tooling, and programs in any language with C FFI are equal consumers of the same library through the same interface.
|
||||
**The `dpm` binary** is the command-line tool, and it is one consumer of that library. Build systems, image builders, distribution tooling, and programs in any language with C FFI are equal consumers of the same library through the same interface.
|
||||
|
||||
libdpm-core implements no package operations. It routes and hosts.
|
||||
|
||||
@@ -27,7 +27,7 @@ Everything passes through libdpm-core: binary-to-module, module-to-module, progr
|
||||
|
||||
## Running the dpm binary
|
||||
|
||||
The dpm binary's subcommand surface is exactly the set of loadable modules. `dpm <module> <command> [args...]` loads that module and hands the command to it; `dpm <module> help` asks the module to describe itself. There is no fixed list of operations baked into the tool, because the tool contains no capability logic — it parses arguments and prints.
|
||||
The `dpm` binary's subcommand surface is exactly the set of loadable modules. `dpm <module> <command> [args...]` loads that module and hands the command to it; `dpm <module> help` asks the module to describe itself. There is no fixed list of operations baked into the tool, because the tool contains no capability logic — it parses arguments and prints.
|
||||
|
||||
`dpm --list-modules` shows every module that passes validation, with its version and description. Candidates that fail are excluded from the listing and logged, so what is listed is what can actually be run.
|
||||
|
||||
@@ -44,7 +44,7 @@ These four exist because of a rule the design imposes on itself: every field a l
|
||||
|
||||
A program links libdpm-core and includes `<dpm/core.h>`. Everything it does happens through a **context**, an opaque handle obtained from `dpm_open`.
|
||||
|
||||
Opening a context reads the configuration files, resolves which directory modules will be loaded from, and initializes logging. It loads no modules. The same four things the dpm binary exposes as flags are the fields of the overrides struct passed to `dpm_open`, and passing NULL accepts the system defaults — which is what makes a default context equivalent to invoking the installed dpm binary, since that binary is just another caller doing the same thing.
|
||||
Opening a context reads the configuration files, resolves which directory modules will be loaded from, and initializes logging. It loads no modules. The same four things the `dpm` binary exposes as flags are the fields of the overrides struct passed to `dpm_open`, and passing NULL accepts the system defaults — which is what makes a default context equivalent to invoking the installed `dpm` binary, since that binary is just another caller doing the same thing.
|
||||
|
||||
The target root override is what makes chroot builds, image assembly, and sysroot management work: package operations act on the given tree instead of the running system. Multiple contexts with different roots may be open at once.
|
||||
|
||||
@@ -56,10 +56,10 @@ The context owns everything it hands out. Every string a caller receives stays v
|
||||
|
||||
**Calling into a module.** Two paths:
|
||||
|
||||
- `dpm_execute` passes a command name and an argument vector to the module's entry point. This is the path the dpm binary takes.
|
||||
- `dpm_execute` passes a command name and an argument vector to the module's entry point. This is the path the `dpm` binary takes.
|
||||
- `dpm_get_api` returns a plain C struct of function pointers for a named API at a stated version. This is the path modules and external programs take to call functions directly.
|
||||
|
||||
**Enumerating.** `dpm_list_modules` yields a cursor over every valid module, which is what backs the listing the dpm binary prints.
|
||||
**Enumerating.** `dpm_list_modules` yields a cursor over every valid module, which is what backs the listing the `dpm` binary prints.
|
||||
|
||||
**Services.** A module reaches libdpm-core through the context that dispatched the call: `dpm_log` to write a message, `dpm_config_get` to read a value from its own configuration namespace, `dpm_module_path` to learn where modules live, `dpm_core_version` to learn the library's version. A module needs no file handling and no logging machinery of its own.
|
||||
|
||||
@@ -77,7 +77,7 @@ The context owns everything it hands out. Every string a caller receives stays v
|
||||
|
||||
| Artifact | Location |
|
||||
|---|---|
|
||||
| the dpm binary | `/usr/bin/dpm` |
|
||||
| the `dpm` binary | `/usr/bin/dpm` |
|
||||
| `libdpm-core.so` | `/usr/lib/libdpm-core.so` |
|
||||
| public header | `/usr/include/dpm/core.h` |
|
||||
| modules | `/usr/lib/dpm/modules/<name>.so` |
|
||||
@@ -106,11 +106,11 @@ From the command line this shows up as a module missing from `--list-modules` wi
|
||||
|
||||
## Versioning
|
||||
|
||||
libdpm-core enforces exactly one version rule, the one where it is the host: each module states the oldest libdpm-core it supports, and a library older than that refuses to load the module, with a message naming the remedy. A module is never rejected for being old, because the library's contract grows append-only — a newer libdpm-core supports everything an older one did.
|
||||
libdpm-core enforces exactly one version rule, the one where it is the host: each module states the oldest libdpm-core it supports, and a library older than that refuses to load the module, with a message naming the remedy.
|
||||
|
||||
Every other version question belongs to the consumer. A module that depends on another requires it, reads the version reported back, and decides for itself whether that version is too new or too old for the calls it intends to make. libdpm-core reports what it saw and draws no conclusion from it, so a handle means the module is valid rather than that it suits any particular caller.
|
||||
|
||||
Module APIs evolve append-only: a breaking change means exporting a new table beside the old one rather than mutating the existing one. A newer module therefore still carries everything an older consumer called, so updating libdpm-core or any module preserves every combination that previously worked.
|
||||
A breaking change to a module's API means a new table version under its own symbol, and the module that owns it decides when the old version is retired. Because a consumer asks for an API by name and version, a retired table surfaces as a refused request at load, before the consumer has done any work.
|
||||
|
||||
## Why it works this way
|
||||
|
||||
@@ -124,7 +124,7 @@ Each layer of the system installs the dependencies of the next using only what a
|
||||
|
||||
### Because one implementation must serve every caller
|
||||
|
||||
The requirement that every implementation exist exactly once, and be consumable by the dpm binary, by other layers, and by external programs, is what makes libdpm-core a C ABI library rather than an application with a library carved out of it. It is also why the command line and the C API stay in step: the flags are the override fields, one for one, so a program and a person redirect the same things by the same names.
|
||||
The requirement that every implementation exist exactly once, and be consumable by the `dpm` binary, by other layers, and by external programs, is what makes libdpm-core a C ABI library rather than an application with a library carved out of it. It is also why the command line and the C API stay in step: the flags are the override fields, one for one, so a program and a person redirect the same things by the same names.
|
||||
|
||||
### Because nothing may be trusted that has not been verified
|
||||
|
||||
@@ -132,9 +132,9 @@ libdpm-core is the sole authority on module validity, and the contract is enforc
|
||||
|
||||
The residual limit is that dynamic symbol lookup cannot verify a function signature. The magic constant, the size field, the minimum-version handshake, and the probes cover this in practice; defeating them takes deliberate lying, which is a package-signing concern upstream of the loader.
|
||||
|
||||
### Because updates must never strand a working system
|
||||
### Because coordination happens at load, not at build
|
||||
|
||||
libdpm-core holds no maximums and no exact-match constraints, and evolution is append-only in both directions, so an update can only add satisfiable states. This is what allows modules to be released independently: coordination happens through the versioning model instead of through lockstep builds.
|
||||
A consumer states the module and API version it needs and is told at load whether it is there. That is what allows modules to be released independently: agreement is reached when the pieces meet, through declared versions each consumer judges for itself, instead of through lockstep builds.
|
||||
|
||||
### Because modules are developed independently
|
||||
|
||||
@@ -149,9 +149,9 @@ This repository's own fixtures are deliberately broken stub modules — missing
|
||||
|
||||
## What this repository produces
|
||||
|
||||
- **libdpm-core.so** — the library
|
||||
- **the dpm binary** — the command-line tool
|
||||
- **info** — a module bundled with the dpm binary and libdpm-core, used to test and demonstrate full DPM system functionality where appropriate
|
||||
- **`libdpm-core.so`** — the library
|
||||
- **the `dpm` binary** — the command-line tool
|
||||
- **info** — a module bundled with the `dpm` binary and libdpm-core, used to test and demonstrate full DPM system functionality where appropriate
|
||||
|
||||
Every other module is developed against libdpm-core and lives outside this repository.
|
||||
|
||||
@@ -167,5 +167,5 @@ Every other module is developed against libdpm-core and lives outside this repos
|
||||
- **DESIGN.md** — the full design specification
|
||||
- **CONSUMERS.md** — linking libdpm-core and driving it from a program
|
||||
- **MODULES.md** — writing, building, testing, and installing a module
|
||||
- **BUILD.md** — building, testing, and installing libdpm-core and the dpm binary
|
||||
- **BUILD.md** — building, testing, and installing libdpm-core and the `dpm` binary
|
||||
- **DOCUMENTATION.md** — generating the code reference
|
||||
|
||||
Reference in New Issue
Block a user