diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 122e9d3..c64ed4b 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -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) diff --git a/include/dpm/core.h b/include/dpm/core.h index be7d6c0..9d5eed1 100644 --- a/include/dpm/core.h +++ b/include/dpm/core.h @@ -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) */ /* ------------------------------------------------------------------ */ /* diff --git a/include/internal/context.hpp b/include/internal/context.hpp index bbb113b..082fa24 100644 --- a/include/internal/context.hpp +++ b/include/internal/context.hpp @@ -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 @@ -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 diff --git a/include/internal/modules.hpp b/include/internal/modules.hpp index 21590bd..7b0e18f 100644 --- a/include/internal/modules.hpp +++ b/include/internal/modules.hpp @@ -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 diff --git a/src/bundled-modules/info/include/commands.hpp b/src/bundled-modules/info/include/commands.hpp index 49e90a0..7091cc3 100644 --- a/src/bundled-modules/info/include/commands.hpp +++ b/src/bundled-modules/info/include/commands.hpp @@ -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 */ diff --git a/src/bundled-modules/info/info.cpp b/src/bundled-modules/info/info.cpp index 24db2a0..fc8ef5a 100644 --- a/src/bundled-modules/info/info.cpp +++ b/src/bundled-modules/info/info.cpp @@ -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) diff --git a/src/bundled-modules/info/src/commands.cpp b/src/bundled-modules/info/src/commands.cpp index 829d7d7..010f8e7 100644 --- a/src/bundled-modules/info/src/commands.cpp +++ b/src/bundled-modules/info/src/commands.cpp @@ -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()); diff --git a/src/cli/dpm.cpp b/src/cli/dpm.cpp index 01a77e8..4d96e12 100644 --- a/src/cli/dpm.cpp +++ b/src/cli/dpm.cpp @@ -32,6 +32,13 @@ #include #include +/** + * @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) { diff --git a/src/core/context.cpp b/src/core/context.cpp index 5b631e3..525eb37 100644 --- a/src/core/context.cpp +++ b/src/core/context.cpp @@ -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/"; diff --git a/src/core/modules.cpp b/src/core/modules.cpp index 98da1f4..438f4b6 100644 --- a/src/core/modules.cpp +++ b/src/core/modules.cpp @@ -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**); diff --git a/src/core/version.cpp b/src/core/version.cpp index 89beed7..c1e0101 100644 --- a/src/core/version.cpp +++ b/src/core/version.cpp @@ -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 @@ -28,7 +28,7 @@ #include /** - * @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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dc0d48d..3218d65 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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