Installation records what a module reports about itself. dpm_install_module
opens a module once, writes its version and description to a .meta file in
/var/lib/dpm/metadata/, and records the alternate names it declared in
modules.aliases beside it. dpm_uninstall_module removes both, leaving the
module file in place.
dpm_list_modules reads those records and opens no module. A module with no
record lists with a version of <uninstalled> and still loads when a caller
names it. An unreadable or absent metadata directory costs the listing its
detail and costs nothing else.
Aliases give a module alternate names, declared through the new
dpm_module_aliases contract symbol or added with dpm_add_module_alias. A
name is recorded once: one already serving as an alias, or belonging to an
installed module, is refused rather than repointed. dpm_require matches a
name against the installed modules, then the alias table, then the module
path.
The metadata directory is a fifth override field and the -M flag, and
[modules] metadata in core.conf.
The test suite is four binaries covering context, modules, records, and
aliases, each a ctest case of its own, alongside the CLI cases.
dpm_module_info_of becomes dpm_get_module_info, joining the other two
readers on the context in stating that a call fetches a value.
Recording an error had two entry points doing the same write: the
exported dpm_set_last_error and an internal dpm_core::set_last_error
taking a std::string. The internal one is gone and the loader calls the
exported function, so a reason reaches the context by one path whether a
module or the library records it.
A module had one channel back to its caller: the int returned from
dpm_module_execute, handed through by dpm_execute. Any detail behind
that number could only reach a log, so a caller wanting the reason had
to read output rather than ask for it.
dpm_set_last_error joins the exported API. A module records its reason
on the context handed to its entry point, which belongs to the caller,
and the caller reads it back with dpm_get_last_error.
The two accessors on the context now say what they do to it:
dpm_module_path becomes dpm_get_resolved_module_path, naming the value
it reports rather than the setting it came from, and dpm_last_error
becomes dpm_get_last_error, pairing with the setter.
Path normalization moves to sanitizers.cpp, which holds the conversions
that put a value written by a person into the single form the library
stores it in.
dpm_core carries a namespace block saying what it holds and why its
members keep external linkage while staying off the export table. Each
unnamed namespace says its contents are private to that translation
unit, and the one in the info module says why it has to be unnamed
there: a module is built without hidden visibility or a version script,
so a helper with external linkage would reach that module's exports.
The three opaque handles in the public header were undocumented. What
documentation they had came from the struct definitions in
include/internal, which a consumer never sees.
EXTRACT_ANON_NSPACES was off, so the documentation on every file-private
helper was written and discarded. Doxygen also capitalizes the first
letter of a brief, which turned libdpm-core.so into Libdpm-core.so
wherever a brief opened with it; those are reworded.
Bare references to the library are replaced with the artifact name
throughout, including three in the info module's output. The
cli_info_version regex follows.
The fixture targets, the test binary, and the six test cases live beside
the sources they build, leaving the top-level file with enable_testing,
the ctest scratch-area write, and the add_subdirectory call.
The paths both files need are set once above that call, so the child's
output directories and the parent's clean rule refer to the same
variables rather than repeating the paths.
Namespace and extern "C" bodies are indented a level, so nesting is
visible from the indentation rather than only from the braces. Opening
braces stay on the line that opens the scope, including function
definitions, which previously carried theirs on a line of their own.
A module is built against the system-installed libdpm-core.so and is
responsible for being correct against it. Where it needs to act on the
version it is running under, dpm_core_version() reports that and the
module decides for itself.
dpm_module_core_min() is removed. It was a declaration handed to the
library to enforce on the module's behalf, and enforcement of that kind
belongs nowhere in a library that routes and hosts. The contract is now
three reserved symbols and load validation is two steps: the reserved
symbols resolve, and the version and description probes return
well-formed values.
compare_versions had no remaining caller and is removed; parse_version
stays for the well-formedness probe. The core_too_new fixture went with
the handshake it existed to exercise.
A module is addressed by name and command string, and nothing else.
Typed API access handed a caller a pointer into the callee's function
table, which meant compiling against that module's struct layout — a
build-time dependency between modules that the design does not permit.
Removing it also removes the manifest, the table magic constant, and
the table size field, which existed only to describe and validate
those tables.
Load validation is now three steps: reserved contract symbols resolve,
the minimum-version handshake passes, and the version and description
probes return well-formed values. The contract is four reserved
symbols, and a module's interface is the command vocabulary it
documents.
Documentation is brought in line, and artifacts are named exactly:
libdpm-core.so for the library, <dpm/core.h> for the header, the dpm
binary for the command-line tool.
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>
clean was deleting the generated build system along with the artifacts —
CMakeCache.txt, the makefiles, CMakeFiles/, the CMake file API directory,
and the CTest configuration. That left the build directory unconfigured
after every clean, so an IDE reading its target list from the file API
lost every target until the project was reloaded. clean now removes only
what the build produced.
Also writes CTest's scratch directory into DartConfiguration.tcl so a bare
ctest and IDE test discovery use it too, not just build-driven runs.
BUILD.md no longer lists libdl as a dependency; glibc 2.34 merged it into
libc, so nothing links against it. The code-reference section moves to its
own DOCUMENTATION.md.
The fixture config logged at ERROR while the info module emits its output
at INFO, so the CLI invocation BUILD.md documents exited 0 and printed
nothing. The fixture now logs at INFO and the documented command works as
written.
OVERVIEW.md describes what DPM is, how core routes and validates modules,
and why the architecture takes the shape it does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>