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>
This commit is contained in:
2026-08-10 00:38:21 -04:00
parent da35fe4758
commit c41adc2498
22 changed files with 327 additions and 243 deletions

View File

@@ -31,7 +31,7 @@
enum Command {
CMD_UNKNOWN, /**< Unknown or unsupported command */
CMD_HELP, /**< Display help information */
CMD_VERSION, /**< Display core and module versions */
CMD_VERSION, /**< Display libdpm-core and module versions */
CMD_SYSTEM, /**< Display system information */
CMD_CONFIG, /**< Display configuration information */
};
@@ -47,15 +47,15 @@ Command parse_command(const char* cmd_str);
/**
* @brief Displays the info module's help text
*
* @param ctx Host core context
* @param ctx Host libdpm-core context
* @return 0 on success
*/
int cmd_help(dpm_ctx* ctx);
/**
* @brief Reports the running core version and the info module version
* @brief Reports the running libdpm-core version and the module version
*
* @param ctx Host core context
* @param ctx Host libdpm-core context
* @return 0 on success
*/
int cmd_version(dpm_ctx* ctx);
@@ -63,15 +63,15 @@ int cmd_version(dpm_ctx* ctx);
/**
* @brief Reports operating system and architecture information
*
* @param ctx Host core context
* @param ctx Host libdpm-core context
* @return 0 on success
*/
int cmd_system(dpm_ctx* ctx);
/**
* @brief Reports core configuration as resolved by the running context
* @brief Reports configuration as resolved by the running context
*
* @param ctx Host core context
* @param ctx Host libdpm-core context
* @return 0 on success
*/
int cmd_config(dpm_ctx* ctx);
@@ -79,7 +79,7 @@ int cmd_config(dpm_ctx* ctx);
/**
* @brief Reports an unrecognized command
*
* @param ctx Host core context
* @param ctx Host libdpm-core context
* @param command The unrecognized command string
* @return 1 to indicate failure
*/

View File

@@ -2,8 +2,9 @@
* @file info.cpp
* @brief The info module: contract symbols and command routing
*
* Bundles with core; used for testing and reporting functionality of
* core. Implements the full DPM module contract: the five reserved
* Bundles with the dpm binary and libdpm-core; used for testing and
* reporting functionality of libdpm-core. Implements the full DPM
* module contract: the five reserved
* symbols plus a manifest-declared API table.
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
@@ -36,9 +37,9 @@
/**
* @brief Version 1 API table for the info module
*
* Lets consumers exercise core's typed-access path (require → get_api
* Lets consumers exercise the typed-access path (require → get_api
* → call) end to end, which is part of this module's purpose of
* testing core functionality.
* testing libdpm-core functionality.
*/
struct info_api_v1_s {
dpm_api_table_header hdr;
@@ -53,7 +54,7 @@ static const char* api_version(void)
static const char* api_description(void)
{
return "Reports and tests DPM core functionality.";
return "Reports and tests libdpm-core functionality.";
}
extern "C" {
@@ -82,11 +83,11 @@ extern "C" const char* dpm_module_version(void)
*/
extern "C" const char* dpm_module_description(void)
{
return "Reports and tests DPM core functionality.";
return "Reports and tests libdpm-core functionality.";
}
/**
* @brief Returns the minimum core version this module supports
* @brief Returns the minimum libdpm-core version this module supports
*/
extern "C" const char* dpm_module_core_min(void)
{
@@ -111,7 +112,7 @@ extern "C" const dpm_manifest* dpm_module_manifest(void)
* Routes the command to the appropriate handler. NULL or empty
* command behaves as help.
*
* @param ctx Host core context (reaches core services)
* @param ctx Host libdpm-core context (reaches its services)
* @param command The command string to execute
* @param argc Number of arguments
* @param argv Array of argument strings (argv[0] is the command)

View File

@@ -2,7 +2,7 @@
* @file commands.cpp
* @brief Implementation of the info module command handlers
*
* Reports on, and thereby exercises, core functionality: version,
* Reports on, and thereby exercises, libdpm-core functionality: version,
* system details, and configuration as resolved by the host context.
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
@@ -118,13 +118,13 @@ Command parse_command(const char* cmd_str)
int cmd_help(dpm_ctx* ctx)
{
dpm_log(ctx, DPM_LOG_INFO, "DPM Info Module - Reports and tests DPM core functionality.");
dpm_log(ctx, DPM_LOG_INFO, "DPM Info Module - Reports and tests libdpm-core functionality.");
dpm_log(ctx, DPM_LOG_INFO, "");
dpm_log(ctx, DPM_LOG_INFO, "Available commands:");
dpm_log(ctx, DPM_LOG_INFO, "");
dpm_log(ctx, DPM_LOG_INFO, " version - Display core and module version information");
dpm_log(ctx, DPM_LOG_INFO, " version - Display libdpm-core and module version information");
dpm_log(ctx, DPM_LOG_INFO, " system - Display system information");
dpm_log(ctx, DPM_LOG_INFO, " config - Display configuration as resolved by core");
dpm_log(ctx, DPM_LOG_INFO, " config - Display configuration as resolved by libdpm-core");
dpm_log(ctx, DPM_LOG_INFO, " help - Display this help message");
dpm_log(ctx, DPM_LOG_INFO, "");
return 0;

View File

@@ -103,7 +103,7 @@ bool option_matches(const char* arg, const char* short_form,
/**
* @brief Prints the table of available modules
*
* @param ctx The core context
* @param ctx The libdpm-core context
* @return 0 on success, 1 on failure
*/
int list_modules(dpm_ctx* ctx)
@@ -248,7 +248,7 @@ int main(int argc, char** argv)
char** module_argv = &argv[i + 1];
int module_argc = argc - i - 1;
dpm_module* mod = dpm_require(ctx, module_name, nullptr);
dpm_module* mod = dpm_require(ctx, module_name);
if (!mod) {
const char* err = dpm_last_error(ctx);
std::fprintf(stderr, "dpm: %s\n",

View File

@@ -3,6 +3,7 @@ DPM_CORE_1.0 {
dpm_open;
dpm_close;
dpm_require;
dpm_module_info_of;
dpm_get_api;
dpm_execute;
dpm_list_modules;

View File

@@ -2,7 +2,7 @@
* @file modules.cpp
* @brief Module discovery, load-time validation, routing, enumeration
*
* Implements the load-time enforcement sequence: core is the sole
* Implements the load-time enforcement sequence: libdpm-core is the sole
* authority on module validity. A module is either fully valid or not
* loaded — no partial states.
*
@@ -119,7 +119,7 @@ std::unique_ptr<dpm_module> validate_and_load(dpm_ctx* ctx,
auto core_min_f = reinterpret_cast<string_fn>(resolve(handle, "dpm_module_core_min"));
auto manifest_f = reinterpret_cast<manifest_fn>(resolve(handle, "dpm_module_manifest"));
/* Step 2: core-minimum handshake. */
/* Step 2: minimum-version handshake. */
const char* core_min = core_min_f();
long parsed[3];
if (!core_min || !parse_version(core_min, parsed)) {
@@ -128,8 +128,9 @@ std::unique_ptr<dpm_module> validate_and_load(dpm_ctx* ctx,
return nullptr;
}
if (compare_versions(core_min, DPM_CORE_VERSION_STR) > 0) {
reason = std::string("requires core >= ") + core_min +
", running core is " DPM_CORE_VERSION_STR " — update core";
reason = std::string("requires libdpm-core >= ") + core_min +
", running libdpm-core is " DPM_CORE_VERSION_STR
" — update libdpm-core";
dlclose(handle);
return nullptr;
}
@@ -212,47 +213,43 @@ std::unique_ptr<dpm_module> validate_and_load(dpm_ctx* ctx,
extern "C" {
dpm_module* dpm_require(dpm_ctx* ctx, const char* name,
const char* min_version)
dpm_module* dpm_require(dpm_ctx* ctx, const char* name)
{
if (!ctx || !name || !*name) {
return nullptr;
}
long parsed[3];
if (min_version && !dpmcore::parse_version(min_version, parsed)) {
dpmcore::set_error(ctx, std::string("malformed minimum version: ") +
min_version);
return nullptr;
}
dpm_module* mod = nullptr;
auto it = ctx->modules.find(name);
if (it != ctx->modules.end()) {
mod = it->second.get();
} else {
std::string reason;
auto loaded = dpmcore::validate_and_load(ctx, name, reason);
if (!loaded) {
dpmcore::set_error(ctx, std::string("module '") + name +
"': " + reason);
return nullptr;
}
mod = loaded.get();
ctx->modules[name] = std::move(loaded);
return it->second.get();
}
if (min_version &&
dpmcore::compare_versions(mod->version.c_str(), min_version) < 0) {
std::string reason;
auto loaded = dpmcore::validate_and_load(ctx, name, reason);
if (!loaded) {
dpmcore::set_error(ctx, std::string("module '") + name +
"' is version " + mod->version +
", below required minimum " + min_version);
"': " + reason);
return nullptr;
}
dpm_module* mod = loaded.get();
ctx->modules[name] = std::move(loaded);
return mod;
}
int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod, dpm_module_info* out)
{
if (!ctx || !mod || !out) {
return 1;
}
out->name = mod->name.c_str();
out->version = mod->version.c_str();
out->description = mod->description.c_str();
out->core_min = mod->core_min.c_str();
return 0;
}
const void* dpm_get_api(dpm_ctx* ctx, dpm_module* mod,
const char* api_name, int table_version)
{