diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5676b29..b28fb2f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -164,7 +164,10 @@ option(DPM_DOCS_PDF "Generate the code reference as PDF (requires LaTeX)" ON)
find_package(Doxygen)
if(DOXYGEN_FOUND)
- set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
+ # Doxygen and LaTeX both work in scratch space under docs/tmp. The
+ # finished document is copied up into docs/, so the directory a user
+ # opens holds documentation and nothing else.
+ set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs/tmp)
set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_EXTRACT_STATIC YES)
set(DOXYGEN_QUIET YES)
@@ -185,24 +188,45 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_GENERATE_LATEX NO)
endif()
- doxygen_add_docs(docs
+ # OVERVIEW.md becomes the reference's front page, so the generated
+ # document opens on the project description instead of a bare index.
+ set(DOXYGEN_USE_MDFILE_AS_MAINPAGE ${CMAKE_CURRENT_SOURCE_DIR}/docs/OVERVIEW.md)
+
+ doxygen_add_docs(docs-generate
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_SOURCE_DIR}/docs
COMMENT "Generating code reference with Doxygen"
)
+ # The one target a user builds. Everything below hangs off it, so
+ # `cmake --build
--target docs` produces finished documents.
+ add_custom_target(docs COMMENT "Building the code reference")
+ add_dependencies(docs docs-generate)
+
if(DPM_DOCS_PDF)
find_program(PDFLATEX_EXECUTABLE pdflatex)
if(PDFLATEX_EXECUTABLE)
- add_custom_target(docs-pdf
- COMMAND make -C ${CMAKE_BINARY_DIR}/docs/latex
- COMMENT "Building PDF code reference"
+ add_custom_command(TARGET docs POST_BUILD
+ COMMAND make -C ${CMAKE_BINARY_DIR}/docs/tmp/latex
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_BINARY_DIR}/docs/tmp/latex/refman.pdf
+ ${CMAKE_BINARY_DIR}/docs/pdf/dpm-core-${PROJECT_VERSION}.pdf
+ COMMENT "Compiling the PDF reference"
)
- add_dependencies(docs-pdf docs)
else()
- message(WARNING "DPM_DOCS_PDF is ON but pdflatex was not found; the docs-pdf target is unavailable")
+ message(WARNING "DPM_DOCS_PDF is ON but pdflatex was not found; no PDF will be produced")
endif()
endif()
+
+ if(DPM_DOCS_HTML)
+ add_custom_command(TARGET docs POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+ ${CMAKE_BINARY_DIR}/docs/tmp/html
+ ${CMAKE_BINARY_DIR}/docs/html
+ COMMENT "Placing the HTML reference"
+ )
+ endif()
endif()
# ---------------------------------------------------------------------
diff --git a/docs/CONSUMERS.md b/docs/CONSUMERS.md
index ecec2a7..75693b3 100644
--- a/docs/CONSUMERS.md
+++ b/docs/CONSUMERS.md
@@ -34,14 +34,16 @@ 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 */
+ "/path/to/conf.d",
+ "/path/to/modules",
+ "/path/to/root",
+ -1
};
dpm_ctx* ctx = dpm_open(&ov);
```
+Leaving `config_dir` NULL selects `/etc/dpm/conf.d/`; leaving `module_path` NULL selects the configured value and then the built-in default; leaving `root` NULL selects `/`; a `log_level` of -1 takes the configured value.
+
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
@@ -58,9 +60,11 @@ libdpm-core.so validates the module completely at load; a handle is returned onl
```
dpm_module_info info;
-dpm_module_info_of(ctx, mod, &info); /* info.name, .version, .description */
+dpm_module_info_of(ctx, mod, &info);
```
+`info` carries `name`, `version`, and `description`.
+
Deciding whether that version is suitable is yours. libdpm-core.so applies no version criterion of its own — a handle means the module is valid, not that it suits you.
**`dpm_execute`** invokes the module — a command name and arguments:
@@ -77,7 +81,7 @@ This is the only path into module code, and it is the same path the `dpm` binary
dpm_cursor* cur = dpm_list_modules(ctx);
dpm_module_info info;
while (dpm_cursor_next(cur, &info) == 0) {
- /* info.name, info.version, info.description */
+ /* each iteration fills info */
}
dpm_cursor_free(cur);
```
diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md
index 2976da7..ca33641 100644
--- a/docs/DOCUMENTATION.md
+++ b/docs/DOCUMENTATION.md
@@ -1,20 +1,28 @@
# Generating the Code Reference
-When Doxygen is present, the build offers a `docs` target that generates the API and source reference from the documentation comments carried in the headers and sources. Two output formats are available as configure-time options:
+When Doxygen is present, the build offers a `docs` target that produces the reference from the documentation comments carried in the headers and sources, together with the markdown documents in `docs/`. OVERVIEW.md becomes its front page, and DESIGN.md, MODULES.md, CONSUMERS.md, BUILD.md, and this file follow as chapters ahead of the namespace, class, and file reference.
+
+One command generates it:
+
+```
+cmake --build --target docs
+```
+
+The finished documents land in `/docs/`:
+
+```
+/docs/pdf/dpm-core-.pdf the PDF reference
+/docs/html/index.html the HTML reference
+/docs/tmp/ scratch space for the generators
+```
+
+Doxygen and LaTeX both work under `docs/tmp/`, and the finished document is copied up into `docs/`. `cmake --build --target clean` removes the whole tree.
+
+Two output formats are available as configure-time options:
- **`-DDPM_DOCS_PDF`** (default ON) — PDF reference, via Doxygen's native LaTeX output; requires `pdflatex` and `makeindex`
- **`-DDPM_DOCS_HTML`** (default OFF) — HTML reference
-Generate and compile the PDF reference:
-
-```
-cmake --build --target docs-pdf
-```
-
-The PDF lands at `/docs/latex/refman.pdf`.
-
-With `DPM_DOCS_HTML` enabled at configure time, the `docs` target additionally produces the HTML reference in `/docs/html`:
-
```
cmake -B -DDPM_DOCS_HTML=ON
cmake --build --target docs
diff --git a/include/dpm/core.h b/include/dpm/core.h
index 633933d..296aa2a 100644
--- a/include/dpm/core.h
+++ b/include/dpm/core.h
@@ -40,9 +40,10 @@ extern "C" {
/* ------------------------------------------------------------------ */
/**
- * Marks the public API visible. The library is compiled with hidden
- * default symbol visibility; these functions are its entire exported
- * surface.
+ * @brief Marks the public API visible
+ *
+ * The library is compiled with hidden default symbol visibility; the
+ * functions carrying this are its entire exported surface.
*/
#ifndef DPM_API
#define DPM_API __attribute__((visibility("default")))
@@ -60,11 +61,25 @@ typedef struct dpm_cursor dpm_cursor;
/* Log levels */
/* ------------------------------------------------------------------ */
+/**
+ * @brief Severity levels accepted by dpm_log()
+ *
+ * A message at a level above the context's configured level is dropped.
+ */
enum {
+ /** Unrecoverable failure. */
DPM_LOG_FATAL = 0,
+
+ /** Operation failed. */
DPM_LOG_ERROR = 1,
- DPM_LOG_WARN = 2,
- DPM_LOG_INFO = 3,
+
+ /** Operation continued, something was wrong. */
+ DPM_LOG_WARN = 2,
+
+ /** Normal reporting; the default level. */
+ DPM_LOG_INFO = 3,
+
+ /** Detail for diagnosing behaviour. */
DPM_LOG_DEBUG = 4
};
@@ -73,25 +88,45 @@ enum {
/* ------------------------------------------------------------------ */
/**
- * Overrides for dpm_open(). Any field may be left NULL (or -1 for
- * log_level) to accept configuration-file values and built-in defaults.
- * Every field here is exposed as a dpm CLI flag.
+ * @brief Overrides for dpm_open()
+ *
+ * Any field may be left NULL (or -1 for log_level) to accept
+ * configuration-file values and built-in defaults. Every field here is
+ * exposed as a flag on the dpm binary.
*/
typedef struct dpm_open_overrides {
- const char* config_dir; /* NULL = /etc/dpm/conf.d/ */
- const char* module_path; /* NULL = config value, then built-in */
- const char* root; /* NULL = "/" (target root for pkg ops) */
- int log_level; /* -1 = config value; else DPM_LOG_* */
+ /** NULL selects /etc/dpm/conf.d/. */
+ const char* config_dir;
+
+ /** NULL selects the configured value, then the built-in default. */
+ const char* module_path;
+
+ /** NULL selects "/", the target root for package operations. */
+ const char* root;
+
+ /** -1 selects the configured value; otherwise a DPM_LOG_* level. */
+ int log_level;
} dpm_open_overrides;
/* ------------------------------------------------------------------ */
/* Module information (enumeration results) */
/* ------------------------------------------------------------------ */
+/**
+ * @brief What the library read from a module at load
+ *
+ * Filled by dpm_module_info_of() and by dpm_cursor_next(). The string
+ * pointers remain valid until the context is closed.
+ */
typedef struct dpm_module_info {
- const char* name; /* module name (filename minus .so) */
- const char* version; /* module's own X.Y.Z */
- const char* description; /* one-line description */
+ /** Module name, its filename minus .so. */
+ const char* name;
+
+ /** The module's own X.Y.Z. */
+ const char* version;
+
+ /** One-line description. */
+ const char* description;
} dpm_module_info;
/* ------------------------------------------------------------------ */
@@ -285,17 +320,28 @@ DPM_API const char* dpm_last_error(dpm_ctx* ctx);
/* Module contract (implemented by modules, called by libdpm-core) */
/* ------------------------------------------------------------------ */
-/*
- * Every module exports, as extern "C":
+/**
+ * @page module_contract The module contract
*
- * int dpm_module_execute(dpm_ctx* ctx, const char* command,
- * int argc, char** argv);
- * const char* dpm_module_version(void);
- * const char* dpm_module_description(void);
+ * A module is one .so in the module directory. It includes
+ * , links -ldpm-core, and exports three reserved symbols as
+ * extern "C":
+ *
+ * @code
+ * int dpm_module_execute(dpm_ctx* ctx, const char* command,
+ * int argc, char** argv);
+ * const char* dpm_module_version(void);
+ * const char* dpm_module_description(void);
+ * @endcode
+ *
+ * @section module_contract_surface The functional surface
*
* dpm_module_execute is the module's entire functional surface; its
* capabilities are addressed by command string, so a module publishes
* no headers, struct layouts, or symbols to anything that calls it.
+ * NULL or an empty command behaves as the module's help command.
+ *
+ * @section module_contract_version Version compatibility
*
* A module determines for itself whether it can work with the library
* it is running against: dpm_core_version() reports the running
@@ -303,8 +349,12 @@ DPM_API const char* dpm_last_error(dpm_ctx* ctx);
* module is built against the system-installed libdpm-core.so and is
* responsible for being correct against it.
*
+ * @section module_contract_validation Validation
+ *
* The library refuses to load any module that does not validate
- * completely (see the DPM specification: load-time enforcement).
+ * completely: every reserved symbol resolves, and the version and
+ * description probes return well-formed values. A module that loads is
+ * fully valid.
*/
#ifdef __cplusplus
diff --git a/include/internal/context.hpp b/include/internal/context.hpp
index 06328e2..f2d5359 100644
--- a/include/internal/context.hpp
+++ b/include/internal/context.hpp
@@ -32,22 +32,33 @@
/** @brief A libdpm-core context: configuration, logging, module registry */
struct dpm_ctx {
+ /** Directory the .conf files were read from. */
std::string config_dir;
+
+ /** Directory modules are loaded from. */
std::string module_path;
+
+ /** Target root for package operations. */
std::string root;
- int log_level = DPM_LOG_INFO;
- bool write_to_log = false;
+ /** Messages above this level are dropped. */
+ int log_level = DPM_LOG_INFO;
+
+ /** Whether to append messages to log_file. */
+ bool write_to_log = false;
+
+ /** Path of the log file. */
std::string log_file;
- /* config[module][section][key] = value */
+ /** Configuration store, addressed as config[module][section][key]. */
std::map>> config;
- /* validated modules, keyed by name; loaded at most once per ctx */
+ /** Validated modules keyed by name; each is loaded at most once. */
std::map> modules;
+ /** Reason for the most recent failure. */
std::string last_error;
};
diff --git a/include/internal/modules.hpp b/include/internal/modules.hpp
index c783176..f5f351a 100644
--- a/include/internal/modules.hpp
+++ b/include/internal/modules.hpp
@@ -30,16 +30,28 @@
/** @brief A loaded, fully validated module */
struct dpm_module {
+ /** Module name, its filename minus .so. */
std::string name;
- void* handle = nullptr;
+
+ /** The dlopen handle. */
+ void* handle = nullptr;
+
+ /** dpm_module_version() as read at load. */
std::string version;
+
+ /** dpm_module_description() as read at load. */
std::string description;
+
+ /** Resolved dpm_module_execute; the only path into module code. */
int (*execute)(dpm_ctx*, const char*, int, char**) = nullptr;
};
/** @brief Enumeration cursor over validated modules */
struct dpm_cursor {
+ /** One entry per valid module. */
std::vector infos;
+
+ /** Position of the next entry. */
size_t idx = 0;
};
diff --git a/src/bundled-modules/info/include/commands.hpp b/src/bundled-modules/info/include/commands.hpp
index 89cfd64..49e90a0 100644
--- a/src/bundled-modules/info/include/commands.hpp
+++ b/src/bundled-modules/info/include/commands.hpp
@@ -29,11 +29,20 @@
* @brief Commands supported by the info module
*/
enum Command {
- CMD_UNKNOWN, /**< Unknown or unsupported command */
- CMD_HELP, /**< Display help information */
- CMD_VERSION, /**< Display libdpm-core and module versions */
- CMD_SYSTEM, /**< Display system information */
- CMD_CONFIG, /**< Display configuration information */
+ /** Unknown or unsupported command. */
+ CMD_UNKNOWN,
+
+ /** Display help information. */
+ CMD_HELP,
+
+ /** Display library and module versions. */
+ CMD_VERSION,
+
+ /** Display system information. */
+ CMD_SYSTEM,
+
+ /** Display configuration information. */
+ CMD_CONFIG,
};
/**
diff --git a/src/libdpm-core.map b/src/libdpm-core.map
index ba97de6..afe70ea 100644
--- a/src/libdpm-core.map
+++ b/src/libdpm-core.map
@@ -1,65 +1,16 @@
/*
- * libdpm-core.map — the linker version script for libdpm-core.so
+ * libdpm-core.map — holds libdpm-core.so's exports to the public C API,
+ * so describes the binary exactly and the implementation
+ * behind it stays private.
*
- * Passed to the linker with --version-script; see CMakeLists.txt, which
- * also lists it as a link dependency so edits force a relink. It does
- * two separate jobs.
- *
- *
- * 1. It limits the export set.
- *
- * `global` names every function declared in . `local: *`
- * hides everything else, including the template instantiations that
- * libstdc++ headers emit with default visibility.
- *
- *
- * 2. It versions the exports.
- *
- * Every symbol here is stamped with the node name: dpm_open becomes
- * dpm_open@@DPM_CORE_1.0. The linker reads that node off the library a
- * consumer links against and records it in the consumer's own binary,
- * and at startup the dynamic linker verifies the library still provides
- * it.
- *
- * That keeps an already-installed module working after the library
- * changes underneath it, which is what makes updating libdpm-core.so
- * safe mid-bootstrap.
- *
- * A breaking change ships as a new node while the old node keeps its
- * original definitions, so binaries built against the old node keep
- * resolving them. Both live in one .so under one soname, preserving the
- * invariant the routing model depends on: exactly one instance of the
- * library mapped per process.
- *
- *
- * Changing this file:
- *
- * - A new public function goes into `global` in the same commit that
- * adds it to .
- * - A breaking change to an existing function adds a new node above
- * DPM_CORE_1.0, leaving this node and its definitions intact.
- *
- * Newest node first. DPM_CORE_0.1 was never released; it is here as the
- * shape a retired node takes.
+ * Passed to the linker with --version-script. `dpm_*` passes the
+ * functions marked DPM_API in the header, which is where membership is
+ * decided; `local: *` hides the rest, including the template
+ * instantiations libstdc++ headers emit with default visibility.
*/
-DPM_CORE_1.0 {
+{
global:
- dpm_open;
- dpm_close;
- dpm_require;
- dpm_module_info_of;
- dpm_execute;
- dpm_list_modules;
- dpm_cursor_next;
- dpm_cursor_free;
- dpm_core_version;
- dpm_config_get;
- dpm_log;
- dpm_module_path;
- dpm_last_error;
+ dpm_*;
local:
*;
};
-
-DPM_CORE_0.1 {
-};