Document the namespaces and the opaque handles

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.
This commit is contained in:
2026-08-16 01:42:04 -04:00
parent 5230ac8748
commit 50b95fb729
12 changed files with 129 additions and 36 deletions

View File

@@ -15,6 +15,11 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_OUTPUT_DIRECTORY ${DPM_DOCS_TMP_DIR})
set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_EXTRACT_STATIC YES)
# Helpers private to a translation unit live in unnamed namespaces,
# whose contents Doxygen drops unless this is on. Without it their
# documentation is written and never reaches the output.
set(DOXYGEN_EXTRACT_ANON_NSPACES YES)
set(DOXYGEN_QUIET YES)
set(DOXYGEN_WARN_IF_UNDOCUMENTED NO)
set(DOXYGEN_JAVADOC_AUTOBRIEF YES)

View File

@@ -55,8 +55,45 @@ extern "C" {
/* Opaque handles */
/* ------------------------------------------------------------------ */
/**
* @brief A libdpm-core.so context
*
* Everything a consumer does happens through one of these. It carries
* the resolved configuration, the module path, the target root, the log
* targets, and the modules loaded so far.
*
* Obtained from dpm_open() and released by dpm_close(). The context owns
* everything it hands out: every string and handle a caller receives
* from it stays valid until it is closed, and the caller frees none of
* them.
*
* Several contexts may be open at once, each with its own target root.
*/
typedef struct dpm_ctx dpm_ctx;
/**
* @brief A loaded, fully validated module
*
* Obtained from dpm_require(). A handle is issued only for a module that
* passed validation completely, so holding one means the contract is
* satisfied.
*
* Owned by the context that loaded it: a module is loaded at most once
* per context, repeated requests return the same handle, and closing the
* context unloads it.
*/
typedef struct dpm_module dpm_module;
/**
* @brief A cursor over the valid modules in the module path
*
* Obtained from dpm_list_modules(), advanced with dpm_cursor_next(), and
* released with dpm_cursor_free().
*
* The cursor holds its own copy of what it reports, so releasing it
* leaves the modules loaded and the strings it produced valid until the
* context closes.
*/
typedef struct dpm_cursor dpm_cursor;
/* ------------------------------------------------------------------ */
@@ -136,7 +173,7 @@ typedef struct dpm_module_info {
/* ------------------------------------------------------------------ */
/**
* @brief Creates a libdpm-core context
* @brief Creates a libdpm-core.so context
*
* Reads configuration from /etc/dpm/conf.d/ (or the overridden config
* directory), resolves the module path (override > config > built-in
@@ -154,7 +191,7 @@ DPM_PUBLIC_ABI_EXPORT
dpm_ctx* dpm_open(const dpm_open_overrides* overrides);
/**
* @brief Releases a libdpm-core context
* @brief Releases a libdpm-core.so context
*
* Unloads every module handle the context issued, closes log targets,
* and frees all memory owned by the context. All handles and strings
@@ -181,7 +218,7 @@ void dpm_close(dpm_ctx* ctx);
* module's version with dpm_module_info_of() and decide whether it is
* acceptable.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @param name The module name (its filename minus .so)
* @return A module handle owned by the context, or NULL on failure
* with the precise reason retrievable via dpm_last_error()
@@ -196,7 +233,7 @@ dpm_module* dpm_require(dpm_ctx* ctx, const char* name);
* exactly as they were read at load. The caller decides whether the
* version it is looking at suits its purposes.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @param mod A module handle from dpm_require()
* @param out Receives the module's information; the string pointers
* remain valid until context close
@@ -218,7 +255,7 @@ int dpm_module_info_of(dpm_ctx* ctx, dpm_module* mod,
* compile-time knowledge of the module it is calling — the same call
* a module makes to reach a peer.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @param mod A module handle from dpm_require()
* @param command The command name; NULL or empty behaves as the
* module's help command
@@ -240,7 +277,7 @@ int dpm_execute(dpm_ctx* ctx, dpm_module* mod, const char* command,
* Scans the module path and validates each candidate .so; failures
* are logged and excluded from the results.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @return A cursor over all valid modules, or NULL on an unreadable
* module path
*/
@@ -273,9 +310,9 @@ void dpm_cursor_free(dpm_cursor* cur);
/* ------------------------------------------------------------------ */
/**
* @brief Returns libdpm-core's own version
* @brief Returns the version of libdpm-core.so
*
* @return The libdpm-core version as a static X.Y.Z string; callable
* @return The libdpm-core.so version as a static X.Y.Z string; callable
* without a context
*/
DPM_PUBLIC_ABI_EXPORT
@@ -288,7 +325,7 @@ const char* dpm_core_version(void);
* context's configuration directory; the namespace named "core", from
* core.conf, is the library's own.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @param module The configuration namespace to read
* @param section The section name within the file
* @param key The key within the section
@@ -305,7 +342,7 @@ const char* dpm_config_get(dpm_ctx* ctx, const char* module,
* Targets are the console and, when configured, the log file.
* Messages above the configured level are dropped.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @param level The severity (DPM_LOG_FATAL through DPM_LOG_DEBUG)
* @param message The message to log; NULL is a no-op
*/
@@ -315,7 +352,7 @@ void dpm_log(dpm_ctx* ctx, int level, const char* message);
/**
* @brief Returns the resolved module directory path
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @return The module directory path this context resolved
*/
DPM_PUBLIC_ABI_EXPORT
@@ -324,7 +361,7 @@ const char* dpm_module_path(dpm_ctx* ctx);
/**
* @brief Returns the most recent failure recorded on the context
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @return A human-readable description of the most recent failure, or
* NULL if none; overwritten by the next failing call
*/
@@ -332,7 +369,7 @@ DPM_PUBLIC_ABI_EXPORT
const char* dpm_last_error(dpm_ctx* ctx);
/* ------------------------------------------------------------------ */
/* Module contract (implemented by modules, called by libdpm-core) */
/* Module contract (implemented by modules, called by libdpm-core.so) */
/* ------------------------------------------------------------------ */
/*

View File

@@ -1,6 +1,6 @@
/**
* @file context.hpp
* @brief The libdpm-core context: configuration, logging, module registry
* @brief The libdpm-core.so context: configuration, logging, module registry
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
* @author Chris Punches <chris.punches@silogroup.org>
@@ -30,7 +30,7 @@
#include "internal/modules.hpp"
/** @brief A libdpm-core context: configuration, logging, module registry */
/** @brief A libdpm-core.so context: configuration, logging, module registry */
struct dpm_ctx {
/** Directory the .conf files were read from. */
std::string config_dir;
@@ -62,11 +62,31 @@ struct dpm_ctx {
std::string last_error;
};
/**
* @namespace dpm_core
* @brief Internal implementation of libdpm-core.so
*
* Holds what the library's translation units share with each other and
* with nothing else: the loader, the configuration reader, the error
* recorder, and the version parser. It is declared in include/internal/,
* which is never installed, so a consumer or a module cannot name any of
* it.
*
* Nothing in here reaches the export table. The library is compiled with
* hidden default symbol visibility and linked against a version script
* whose `local: *` covers everything the public declarations do not
* claim, so these keep external linkage across the library's own files
* while staying invisible outside it.
*
* Helpers used by a single translation unit belong in that file's
* unnamed namespace instead, which gives them internal linkage and keeps
* them from colliding across files.
*/
namespace dpm_core {
/**
* @brief Records a failure reason on the context
*
* @param ctx The libdpm-core context; NULL is a no-op
* @param ctx The libdpm-core.so context; NULL is a no-op
* @param msg The failure description
*/
void set_error(dpm_ctx* ctx, const std::string& msg);
@@ -77,7 +97,7 @@ namespace dpm_core {
* Parses every .conf file in the context's configuration directory
* into the context's configuration store.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
*/
void load_config_dir(dpm_ctx* ctx);
} // namespace dpm_core

View File

@@ -71,7 +71,7 @@ namespace dpm_core {
* Loads the named module's .so from the context's module path and
* verifies the complete contract.
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @param name The module name
* @param reason Receives the refusal reason on failure
* @return The validated module (caller owns), or nullptr on failure

View File

@@ -56,15 +56,15 @@ Command parse_command(const char* cmd_str);
/**
* @brief Displays the info module's help text
*
* @param ctx Host libdpm-core context
* @param ctx Host libdpm-core.so context
* @return 0 on success
*/
int cmd_help(dpm_ctx* ctx);
/**
* @brief Reports the running libdpm-core version and the module version
* @brief Reports the running libdpm-core.so version and the module version
*
* @param ctx Host libdpm-core context
* @param ctx Host libdpm-core.so context
* @return 0 on success
*/
int cmd_version(dpm_ctx* ctx);
@@ -72,7 +72,7 @@ int cmd_version(dpm_ctx* ctx);
/**
* @brief Reports operating system and architecture information
*
* @param ctx Host libdpm-core context
* @param ctx Host libdpm-core.so context
* @return 0 on success
*/
int cmd_system(dpm_ctx* ctx);
@@ -80,7 +80,7 @@ int cmd_system(dpm_ctx* ctx);
/**
* @brief Reports configuration as resolved by the running context
*
* @param ctx Host libdpm-core context
* @param ctx Host libdpm-core.so context
* @return 0 on success
*/
int cmd_config(dpm_ctx* ctx);
@@ -88,7 +88,7 @@ int cmd_config(dpm_ctx* ctx);
/**
* @brief Reports an unrecognized command
*
* @param ctx Host libdpm-core context
* @param ctx Host libdpm-core.so context
* @param command The unrecognized command string
* @return 1 to indicate failure
*/

View File

@@ -44,7 +44,7 @@ extern "C" const char* dpm_module_version(void) {
* @brief Returns the module's one-line description
*/
extern "C" const char* dpm_module_description(void) {
return "Reports and tests libdpm-core functionality.";
return "Reports and tests libdpm-core.so functionality.";
}
/**
@@ -53,7 +53,7 @@ extern "C" const char* dpm_module_description(void) {
* Routes the command to the appropriate handler. NULL or empty
* command behaves as help.
*
* @param ctx Host libdpm-core context (reaches its services)
* @param ctx Host libdpm-core.so 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, libdpm-core functionality: version,
* Reports on, and thereby exercises, libdpm-core.so functionality: version,
* system details, and configuration as resolved by the host context.
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
@@ -32,6 +32,14 @@
#define INFO_VERSION "0.1.0"
/**
* @brief Helpers private to this translation unit
*
* System detection, used by the command handlers below. An unnamed
* namespace gives them internal linkage, which matters here: a module is
* built without hidden visibility and without a version script, so a
* helper with external linkage would land on the module's export table.
*/
namespace {
std::string detect_architecture() {
struct utsname system_info;
@@ -112,20 +120,20 @@ 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 libdpm-core functionality.");
dpm_log(ctx, DPM_LOG_INFO, "DPM Info Module - Reports and tests libdpm-core.so 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 libdpm-core and module version information");
dpm_log(ctx, DPM_LOG_INFO, " version - Display libdpm-core.so 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 libdpm-core");
dpm_log(ctx, DPM_LOG_INFO, " config - Display configuration as resolved by libdpm-core.so");
dpm_log(ctx, DPM_LOG_INFO, " help - Display this help message");
dpm_log(ctx, DPM_LOG_INFO, "");
return 0;
}
int cmd_version(dpm_ctx* ctx) {
std::string core_msg = "libdpm-core Version: ";
std::string core_msg = "libdpm-core.so Version: ";
core_msg += dpm_core_version();
dpm_log(ctx, DPM_LOG_INFO, core_msg.c_str());

View File

@@ -32,6 +32,13 @@
#include <strings.h>
#include <vector>
/**
* @brief Helpers private to this translation unit
*
* Argument matching and printing, used by main() alone. An unnamed
* namespace gives them internal linkage, which keeps them out of the
* binary's symbol table and out of reach of anything linked with it.
*/
namespace {
/**
* @brief Prints the CLI usage message
@@ -99,7 +106,7 @@ namespace {
/**
* @brief Prints the table of available modules
*
* @param ctx The libdpm-core context
* @param ctx The libdpm-core.so context
* @return 0 on success, 1 on failure
*/
int list_modules(dpm_ctx* ctx) {

View File

@@ -32,6 +32,14 @@
namespace fs = std::filesystem;
/**
* @brief Helpers private to this translation unit
*
* Configuration reading and the value conversions it needs. An unnamed
* namespace gives them internal linkage, so they are unreachable from
* the library's other files and cannot collide with a same-named helper
* in one of them.
*/
namespace {
/** Configuration directory used when no override is supplied. */
const char* DEFAULT_CONFIG_DIR = "/etc/dpm/conf.d/";

View File

@@ -52,6 +52,14 @@ void dpm_internal_unload(void* handle) {
}
}
/**
* @brief Helpers private to this translation unit
*
* The module contract's function signatures and the symbol resolver
* built on them. An unnamed namespace gives them internal linkage, so
* they are unreachable from the library's other files and cannot
* collide with a same-named helper in one of them.
*/
namespace {
/** Signature of a module's command entry point. */
using execute_fn = int (*)(dpm_ctx*, const char*, int, char**);

View File

@@ -1,6 +1,6 @@
/**
* @file version.cpp
* @brief libdpm-core.so's version, and X.Y.Z version parsing
* @brief Version of libdpm-core.so, and X.Y.Z version parsing
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
* @author Chris Punches <chris.punches@silogroup.org>
@@ -28,7 +28,7 @@
#include <cstdlib>
/**
* @brief libdpm-core.so's own version
* @brief Version of libdpm-core.so
*
* The single place the library's version is written. dpm_core_version()
* reports it, and the build reads it for the artifact naming.
@@ -36,7 +36,7 @@
#define DPM_CORE_VERSION_STR "1.0.0"
/**
* @brief Returns libdpm-core.so's own version
* @brief Returns the version of libdpm-core.so
*
* Answers from a compiled-in constant, so it needs no context and is
* callable before one is opened. A module reads this to determine for

View File

@@ -53,7 +53,7 @@ add_test(NAME cli_info_version
COMMAND dpm -c ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/conf
-m ${CMAKE_BINARY_DIR}/modules -L INFO info version)
set_tests_properties(cli_info_version PROPERTIES
PASS_REGULAR_EXPRESSION "libdpm-core Version: ${PROJECT_VERSION}")
PASS_REGULAR_EXPRESSION "libdpm-core\\.so Version: ${PROJECT_VERSION}")
add_test(NAME cli_module_not_found
COMMAND dpm -c ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/conf