Files
dpm-core-ng/CMakeLists.txt
Christopher M. Punches c41adc2498 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>
2026-08-10 00:38:21 -04:00

238 lines
8.9 KiB
CMake

cmake_minimum_required(VERSION 3.22)
project(dpm-core VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# ---------------------------------------------------------------------
# libdpm-core.so — the library (C ABI)
# ---------------------------------------------------------------------
add_library(dpm-core SHARED
src/context.cpp
src/modules.cpp
src/version.cpp
)
target_include_directories(dpm-core PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_definitions(dpm-core PRIVATE
DPM_CORE_VERSION_STR="${PROJECT_VERSION}"
)
# The public C API is the library's entire exported surface; internals
# stay hidden. The version script pins the export list and versions the
# symbols.
target_compile_options(dpm-core PRIVATE
-fvisibility=hidden
-fvisibility-inlines-hidden
)
target_link_options(dpm-core PRIVATE
-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/libdpm-core.map
)
set_property(TARGET dpm-core APPEND PROPERTY
LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/libdpm-core.map
)
target_link_libraries(dpm-core PRIVATE ${CMAKE_DL_LIBS})
set_target_properties(dpm-core PROPERTIES
OUTPUT_NAME "dpm-core"
VERSION ${PROJECT_VERSION}
SOVERSION 1
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
# ---------------------------------------------------------------------
# dpm — the CLI (thin client of libdpm-core)
# ---------------------------------------------------------------------
add_executable(dpm src/cli/dpm.cpp)
target_link_libraries(dpm PRIVATE dpm-core)
set_target_properties(dpm PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
BUILD_RPATH "$ORIGIN/../lib"
)
# ---------------------------------------------------------------------
# info — the bundled module (tests and reports libdpm-core functionality)
# ---------------------------------------------------------------------
add_library(info MODULE
src/bundled-modules/info/info.cpp
src/bundled-modules/info/src/commands.cpp
)
target_include_directories(info PRIVATE src/bundled-modules/info/include)
# The one permitted link dependency: libdpm-core.
target_link_libraries(info PRIVATE dpm-core)
set_target_properties(info PROPERTIES
PREFIX ""
SUFFIX ".so"
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/modules
)
# The module bundles with libdpm-core, so building the CLI builds it too: after a
# clean, running dpm from the build tree finds a module directory that is
# populated. This orders the build only — the CLI does not link the module.
add_dependencies(dpm info)
# ---------------------------------------------------------------------
# Test fixture modules: one known-good stub, five deliberately broken
# ---------------------------------------------------------------------
enable_testing()
# CTest's scratch area is named by its BuildDirectory configuration option.
# Writing it into DartConfiguration.tcl puts it where ctest reads it on every
# invocation — the test target, a bare ctest, and the IDE's test discovery
# alike — rather than only the ones the build drives.
file(WRITE ${CMAKE_BINARY_DIR}/DartConfiguration.tcl
"BuildDirectory: ${CMAKE_BINARY_DIR}/test-results\n")
set(FIXTURE_MODULE_DIR ${CMAKE_BINARY_DIR}/tests/fixtures)
foreach(fixture good missing_symbols bad_magic lying_manifest core_too_new bad_version)
add_library(fixture_${fixture} MODULE tests/fixtures/src/${fixture}.cpp)
set_target_properties(fixture_${fixture} PROPERTIES
PREFIX ""
SUFFIX ".so"
OUTPUT_NAME ${fixture}
LIBRARY_OUTPUT_DIRECTORY ${FIXTURE_MODULE_DIR}
)
endforeach()
# ---------------------------------------------------------------------
# API test binary — drives libdpm-core through its public C API
# ---------------------------------------------------------------------
add_executable(test_core tests/test_core.cpp)
target_link_libraries(test_core PRIVATE dpm-core)
target_compile_definitions(test_core PRIVATE
TEST_FIXTURE_MODULES="${FIXTURE_MODULE_DIR}"
TEST_FIXTURE_CONF="${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/conf"
DPM_CORE_VERSION_EXPECTED="${PROJECT_VERSION}"
)
set_target_properties(test_core PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests
BUILD_RPATH "$ORIGIN/../lib"
)
foreach(fixture good missing_symbols bad_magic lying_manifest core_too_new bad_version)
add_dependencies(test_core fixture_${fixture})
endforeach()
add_test(NAME core_api COMMAND test_core)
# ---------------------------------------------------------------------
# CLI end-to-end tests (the dpm binary is the cheapest full-stack client)
# ---------------------------------------------------------------------
add_test(NAME cli_help COMMAND dpm --help)
set_tests_properties(cli_help PROPERTIES PASS_REGULAR_EXPRESSION "Usage: dpm")
add_test(NAME cli_list
COMMAND dpm -c ${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/conf
-m ${CMAKE_BINARY_DIR}/modules --list-modules)
set_tests_properties(cli_list PROPERTIES PASS_REGULAR_EXPRESSION "info")
add_test(NAME cli_info_version
COMMAND dpm -c ${CMAKE_CURRENT_SOURCE_DIR}/tests/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}")
add_test(NAME cli_module_not_found
COMMAND dpm -c ${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/conf
-m ${CMAKE_BINARY_DIR}/modules nonexistent)
set_tests_properties(cli_module_not_found PROPERTIES WILL_FAIL TRUE)
add_test(NAME cli_rejects_invalid_module
COMMAND dpm -c ${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/conf
-m ${FIXTURE_MODULE_DIR} missing_symbols)
set_tests_properties(cli_rejects_invalid_module PROPERTIES WILL_FAIL TRUE)
# ---------------------------------------------------------------------
# Code reference (Doxygen) — optional 'docs' target
# ---------------------------------------------------------------------
option(DPM_DOCS_HTML "Generate the code reference in HTML" OFF)
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)
set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_EXTRACT_STATIC YES)
set(DOXYGEN_QUIET YES)
set(DOXYGEN_WARN_IF_UNDOCUMENTED NO)
set(DOXYGEN_JAVADOC_AUTOBRIEF YES)
if(DPM_DOCS_HTML)
set(DOXYGEN_GENERATE_HTML YES)
else()
set(DOXYGEN_GENERATE_HTML NO)
endif()
if(DPM_DOCS_PDF)
set(DOXYGEN_GENERATE_LATEX YES)
set(DOXYGEN_USE_PDFLATEX YES)
set(DOXYGEN_PDF_HYPERLINKS YES)
else()
set(DOXYGEN_GENERATE_LATEX NO)
endif()
doxygen_add_docs(docs
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
COMMENT "Generating code reference with Doxygen"
)
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_dependencies(docs-pdf docs)
else()
message(WARNING "DPM_DOCS_PDF is ON but pdflatex was not found; the docs-pdf target is unavailable")
endif()
endif()
endif()
# ---------------------------------------------------------------------
# Clean
# ---------------------------------------------------------------------
# clean removes what the build produced: the artifact directories, the
# generated documentation, and the test scratch areas.
#
# The generated build system stays. CMakeCache.txt, the makefiles,
# CMakeFiles/, and the CMake file API directory under .cmake/ are what an
# IDE reads to know the project's targets exist; deleting them leaves the
# directory unconfigured, so every target in the IDE stops resolving until
# the project is reloaded. Removing the build system is what configuring a
# fresh directory is for.
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES
${CMAKE_BINARY_DIR}/test-results
${CMAKE_BINARY_DIR}/Testing
${CMAKE_BINARY_DIR}/bin
${CMAKE_BINARY_DIR}/lib
${CMAKE_BINARY_DIR}/modules
${CMAKE_BINARY_DIR}/tests
${CMAKE_BINARY_DIR}/docs
)
# ---------------------------------------------------------------------
# Installation
# ---------------------------------------------------------------------
install(TARGETS dpm RUNTIME DESTINATION bin)
install(TARGETS dpm-core LIBRARY DESTINATION lib)
install(TARGETS info LIBRARY DESTINATION lib/dpm/modules)
install(DIRECTORY include/dpm DESTINATION include)
install(FILES data/core.conf DESTINATION /etc/dpm/conf.d)