Files
dpm-core-ng/docs/CONSUMERS.md
Christopher M. Punches c41adc2498 Move version compatibility to the consumer; name libdpm-core explicitly
dpm_require no longer takes a minimum version and applies no version
criterion of its own. A handle now means the module is valid, not that
it suits the caller. dpm_module_info_of is added alongside it, reporting
the name, version, description, and minimum-libdpm-core version read at
load, so a consuming module can judge a dependency's version for itself.
The one rule still enforced is the minimum-version handshake, where
libdpm-core is the host and refuses a module that demands a newer library
than the one running.

dpm_module_info_of joins the version script, so the exported surface is
now fourteen symbols under DPM_CORE_1.0.

Separately, the bare word "core" is gone from prose everywhere. It named
both the command-line tool and the library, so every use forced the
reader to guess which. Text now says "the dpm binary" or "libdpm-core".
Identifiers keep their spelling: libdpm-core, core.h, core.conf, the
"core" configuration namespace, dpm_core_version, core_min, DPM_CORE_1.0,
the dpmcore namespace, test_core, core_api.

Three user-visible strings changed with it: the load-refusal message now
reads "requires libdpm-core >= X, running libdpm-core is Y — update
libdpm-core", and the info module's description and help text name the
library. The test asserting on the refusal text was updated to match.

DESIGN.md's terminology line no longer defines "DPM Core" as the CLI,
which was the source of the ambiguity. OVERVIEW.md is restructured around
the three layers a reader meets DPM at — user, developer, filesystem —
so a code-level symbol never appears without saying whose layer it is.
MODULES.md describes the bundled info module as testing and demonstrating
full DPM system functionality rather than as a reference implementation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 00:38:21 -04:00

130 lines
4.9 KiB
Markdown

# Consuming libdpm-core
Programs link libdpm-core to operate the package manager directly: build systems, installers, image builders, system tooling, and foreign-language bindings all use the same library the dpm CLI is built on. A program holding a default context is operating the installed package manager itself — system configuration, system module path, system tree, system locking — identically to invoking the installed dpm command.
## Compiling and linking
With libdpm-core installed, include the public header and link the library:
```
#include <dpm/core.h>
```
```
g++ myprog.cpp -ldpm-core
```
The header installs to the standard include path and the library to the standard lib path, so no additional flags are required. The interface is a C ABI: every function is extern "C", every type crossing the boundary is a C type, and state passes through opaque handles — callable from C, C++, or any language with C FFI.
## The context
All work happens through a context handle:
```
dpm_ctx* ctx = dpm_open(NULL);
...
dpm_close(ctx);
```
dpm_open(NULL) reads the system configuration (/etc/dpm/conf.d/), resolves the system module path, and targets the root filesystem. dpm_close releases every handle the context issued; all pointers obtained through the context are invalid after it.
To point a context elsewhere, pass overrides — every field is optional:
```
dpm_open_overrides ov = {
"/path/to/conf.d", /* config_dir: NULL = /etc/dpm/conf.d/ */
"/path/to/modules", /* module_path: NULL = config, then default */
"/path/to/root", /* root: target root for package operations */
-1 /* log_level: -1 = from config */
};
dpm_ctx* ctx = dpm_open(&ov);
```
The 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 simultaneous contexts with different roots are legal.
## Acquiring and using modules
**dpm_require** loads a module by name, on demand:
```
dpm_module* mod = dpm_require(ctx, "mymodule");
```
libdpm-core validates the module completely at load; a handle is returned only for a fully valid module. NULL means the module is absent or invalid — dpm_last_error(ctx) carries the precise reason. Modules load at most once per context; repeated calls return the same handle.
**dpm_module_info_of** reports what libdpm-core saw in the loaded module:
```
dpm_module_info info;
dpm_module_info_of(ctx, mod, &info); /* info.name, .version, .description, .core_min */
```
Deciding whether that version is too new or too old is yours. libdpm-core applies no version criterion of its own — a handle means the module is valid, not that it suits you.
**dpm_execute** drives a module the way the CLI does — a command name and arguments:
```
int rc = dpm_execute(ctx, mod, "command", argc, argv);
```
**dpm_get_api** returns a module's typed function table for direct calls:
```
const mymodule_api_v1_s* api = (const mymodule_api_v1_s*)dpm_get_api(ctx, mod, "mymodule", 1);
```
The returned table was validated at load and is usable for the life of the context. NULL means the module does not provide that API at that version.
## Enumerating modules
```
dpm_cursor* cur = dpm_list_modules(ctx);
dpm_module_info info;
while (dpm_cursor_next(cur, &info) == 0) {
/* info.name, info.version, info.description, info.core_min */
}
dpm_cursor_free(cur);
```
The cursor covers every valid module in the module path; invalid candidates are excluded and logged.
## Services
- **dpm_core_version()** — the libdpm-core version; callable without a context.
- **dpm_module_info_of(ctx, mod, out)** — the name, version, description, and minimum-libdpm-core version read from a loaded module.
- **dpm_config_get(ctx, module, section, key)** — a value from a module's configuration namespace, or NULL if unset.
- **dpm_log(ctx, level, message)** — writes to the context's configured log targets; levels are DPM_LOG_FATAL through DPM_LOG_DEBUG.
- **dpm_module_path(ctx)** — the resolved module directory.
- **dpm_last_error(ctx)** — a human-readable description of the most recent failure on the context, or NULL.
## Ownership and errors
Strings returned by the library are owned by the context (or by the module that produced them) and remain valid until dpm_close; callers never free them. Functions returning int use 0 for success. Functions returning pointers use NULL for failure, with detail available from dpm_last_error.
## Complete example
```
#include <dpm/core.h>
#include <stdio.h>
int main(void) {
dpm_ctx* ctx = dpm_open(NULL);
if (!ctx) {
fprintf(stderr, "failed to initialize\n");
return 1;
}
dpm_module* mod = dpm_require(ctx, "info");
if (!mod) {
fprintf(stderr, "%s\n", dpm_last_error(ctx));
dpm_close(ctx);
return 1;
}
int rc = dpm_execute(ctx, mod, "version", 0, NULL);
dpm_close(ctx);
return rc;
}
```