diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0eedee..f2a346b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,9 @@ jobs: - name: Run target type tests run: cmake -P tests/setup/test_target_type.cmake + - name: Run custom executable runtime deployment tests + run: cmake -P tests/setup/test_runtime_dlls.cmake + - name: Run setup.cmake version resolution test run: cmake -P tests/setup/test_setup_version_resolution.cmake @@ -44,6 +47,9 @@ jobs: - name: Run target type tests run: cmake -P tests/setup/test_target_type.cmake + - name: Run custom executable runtime deployment tests + run: cmake -P tests/setup/test_runtime_dlls.cmake + - name: Run nested static and shared installation round trips run: cmake -P tests/install/test_nested_install.cmake @@ -165,6 +171,13 @@ jobs: # [DEPENDENCY] https://github.com/actions/checkout/releases - uses: actions/checkout@v7 + - name: Install Doxygen + # [DEPENDENCY] https://github.com/ssciwr/doxygen-install/releases + uses: ssciwr/doxygen-install@fa3e94d34131d3990b6b8ba939ab982964aa3758 + + - name: Test documentation configuration and rendered output + run: cmake -P tests/docs/test_docs.cmake + - name: Check README examples run: | # Extract and validate code blocks from README diff --git a/.scout/scout.config.yaml b/.scout/scout.config.yaml index cdb25bb..7155365 100644 --- a/.scout/scout.config.yaml +++ b/.scout/scout.config.yaml @@ -5,12 +5,13 @@ # ignore / dampen / boost / tune overrides; defaults for # repository/markdown/ranking/index/projects are filled in at load time. ignore: - - '**/CHANGELOG.md' - - '**/CHANGELOG*' - - '**/CHANGES.md' - - '**/HISTORY.md' + - "**/CHANGELOG.md" + - "**/CHANGELOG*" + - "**/CHANGES.md" + - "**/HISTORY.md" + - .claude/worktrees/ dampen: - factor: 0.3 patterns: - - '**/package.json' + - "**/package.json" diff --git a/CLAUDE.md b/CLAUDE.md index 5b533f6..00565fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,15 +12,20 @@ Two ways consumers use this repo: ## Commands -Run cpp-library's own test suite (pure CMake scripts, no C++ compilation involved): +Run cpp-library's tests through CMake scripts. Integration fixtures require a +C++ compiler and Ninja; documentation rendering also requires Doxygen: ```bash cmake -P tests/install/CMakeLists.txt # dependency mapping/merging unit tests cmake -P tests/install/test_provider_merge.cmake # dependency provider merge tests cmake -P tests/setup/test_setup_version_resolution.cmake # setup.cmake version-resolution integration test +cmake -P tests/setup/test_runtime_dlls.cmake # custom target DLL deployment; requires compiler and Ninja +cmake -P tests/docs/test_docs.cmake # docs extensions and rendering; requires Doxygen and Ninja ``` -These are the same commands CI (`.github/workflows/ci.yml`, job `unit-tests`) runs. There is no build step for cpp-library itself — `cmake -P` executes the scripts directly. +CI (`.github/workflows/ci.yml`) runs these scripts in its unit-test, Windows +package, and documentation jobs. There is no library build step for cpp-library +itself; integration scripts configure and build their own consumer fixtures. CI also runs an `integration-tests` job that generates a throwaway consumer project, configures/builds/installs it against this repo via `CPMAddPackage(... SOURCE_DIR ...)`, then verifies `find_package()` works against the installed package — this is the best reference for the full round-trip a downstream project goes through. @@ -45,7 +50,7 @@ or pin to a specific commit: `CPMAddPackage("gh:stlab/cpp-library#") - **`cpp-library-dependency-provider.cmake`** — implements the CMake dependency-provider callback (`SET_DEPENDENCY_PROVIDER`) that intercepts every `find_package()`/`FetchContent_MakeAvailable()` call to record the exact package/version/components used, so `Config.cmake` can regenerate accurate `find_dependency()` calls at install time. State is stored in `GLOBAL` properties (`_CPP_LIBRARY_TRACKED_DEP_*`, `_CPP_LIBRARY_PKG_KEYS`, etc.) — there's no other persistence mechanism, so tests reset these properties between cases (see `tests/install/CMakeLists.txt`). - **`cpp-library-docs.cmake`** — Doxygen + doxygen-awesome-css target (`docs`). - **`cpp-library-ci.cmake`** — generates `.github/workflows/ci.yml` for the *consumer* project from `templates/.github/workflows/ci.yml.in`, substituting the package name. -- **`cpp-library-testing.cmake`** — thin backward-compat wrapper; real test/example executable logic lives in `_cpp_library_setup_executables()` in `cpp-library.cmake`. +- **`cpp-library-testing.cmake`** — public `cpp_library_copy_runtime_dlls()` helper and backward-compatibility `_cpp_library_setup_testing()` wrapper; test/example executable creation lives in `_cpp_library_setup_executables()` in `cpp-library.cmake`. ### Key control flow diff --git a/README.md b/README.md index c354a42..d72e1bf 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,8 @@ cpp_library_setup( [EXAMPLES example_list] # Example source files to build (e.g., "example.cpp example_fail.cpp") [TESTS test_list] # Test source files to build (e.g., "tests.cpp") [DOCS_EXCLUDE_SYMBOLS symbols] # Symbols to exclude from docs + [DOCS_INPUTS input_list] # Additional documentation files/directories + [DOCS_OPTIONS setting_list] # Additive single-line Doxygen assignments [REQUIRES_CPP_VERSION 17|20|23] # C++ version (default: 17) [INSTALL_OPTION option_name] # Independent installation control (default: uppercase NAMESPACE_INSTALL) ) @@ -374,6 +376,57 @@ cpp_library_setup( - Installation is controlled by `INSTALL_OPTION`, or the uppercase `${NAMESPACE}_INSTALL` option when omitted. Options default to `PROJECT_IS_TOP_LEVEL`. +### Documentation configuration + +Keep the toolkit's Doxyfile template instead of copying it into your project. +`DOCS_INPUTS` adds files or directories to the default `include` input. Relative +paths are resolved against the directory calling `cpp_library_setup`; absolute +paths and paths containing spaces are supported. Missing inputs fail configuration. + +`DOCS_OPTIONS` applies project-specific Doxygen assignments after the shared +template. Each quoted CMake argument must be a single `TAG = value` or +`TAG += value` assignment, with Doxygen quoting inside the value when needed. +Use spaces, not CMake semicolons, to separate Doxygen list values. The toolkit +validates assignment syntax and rejects line-continuation backslashes, including +those followed by whitespace; Doxygen validates tag names and values. Settings +are literal, not `configure_file` templates: use `${...}` for CMake variables. + +```cmake +cpp_library_setup( + DESCRIPTION "My library" + NAMESPACE mylib + HEADERS api.hpp + DOCS_INPUTS docs/doxygen "${PROJECT_BINARY_DIR}/generated docs" + DOCS_EXCLUDE_SYMBOLS "mylib::detail" + DOCS_OPTIONS + "WARN_IF_UNDOCUMENTED = NO" + "INCLUDE_PATH = \"${PROJECT_BINARY_DIR}/include\"" + "MACRO_EXPANSION = YES" + "PREDEFINED = MYLIB_API= MYLIB_NAMESPACE_BEGIN()= MYLIB_NAMESPACE_END()=" +) +``` + +Existing `docs/Doxyfile` overrides remain supported. Additional inputs and options +are applied after that configuration too, but a full override does not inherit +future toolkit-template changes. Prefer the shared template with only the +project-specific deltas above. Documentation is configured only for top-level +projects with `BUILD_DOCS` enabled. + +### `cpp_library_copy_runtime_dlls` + +```cmake +add_executable(custom-test main.cpp support.cpp) +target_link_libraries(custom-test PRIVATE mylib::my-library) +cpp_library_copy_runtime_dlls(custom-test) +``` + +Call this helper in the directory that created the target. On Windows, it deploys +`TARGET_RUNTIME_DLLS` beside the target after building, including transitive +runtime DLLs. Static targets with no runtime DLLs require no copy; on other +platforms the helper does nothing. It uses CMake 3.24-compatible copy operations +and handles DLLs already in the destination. Toolkit-created test/example +executables use the same helper automatically. + ### Target Naming Use the component name as your project name, and specify the organizational namespace separately: diff --git a/cmake/cpp-library-docs.cmake b/cmake/cpp-library-docs.cmake index 72cc1bc..3372d8f 100644 --- a/cmake/cpp-library-docs.cmake +++ b/cmake/cpp-library-docs.cmake @@ -13,10 +13,30 @@ function(_cpp_library_setup_docs) ) set(multiValueArgs DOCS_EXCLUDE_SYMBOLS + DOCS_INPUTS + DOCS_OPTIONS ) cmake_parse_arguments(ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(docs_extensions "") + foreach(input IN LISTS ARG_DOCS_INPUTS) + get_filename_component(input "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + if(NOT EXISTS "${input}") + message(FATAL_ERROR "cpp-library: Documentation input does not exist: ${input}") + endif() + string(REPLACE "\"" "\\\"" input "${input}") + string(APPEND docs_extensions "INPUT += \"${input}\"\n") + endforeach() + foreach(option IN LISTS ARG_DOCS_OPTIONS) + if(NOT option MATCHES "^[A-Z][A-Z0-9_]*[ \t]*\\+?=[^\r\n]*$" + OR option MATCHES "\\\\[ \t]*$") + message(FATAL_ERROR + "cpp-library: DOCS_OPTIONS requires single-line Doxygen assignments: ${option}") + endif() + string(APPEND docs_extensions "${option}\n") + endforeach() + find_package(Doxygen REQUIRED) # Download doxygen-awesome-css theme via CPM @@ -60,6 +80,10 @@ function(_cpp_library_setup_docs) else() configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) endif() + if(NOT docs_extensions STREQUAL "") + file(APPEND "${DOXYFILE_OUT}" + "\n# Project documentation settings supplied through cpp_library_setup.\n${docs_extensions}") + endif() # Add custom target for documentation with proper stderr capture if(WIN32) diff --git a/cmake/cpp-library-testing.cmake b/cmake/cpp-library-testing.cmake index 8072871..fdc015f 100644 --- a/cmake/cpp-library-testing.cmake +++ b/cmake/cpp-library-testing.cmake @@ -1,10 +1,25 @@ # SPDX-License-Identifier: BSL-1.0 # -# cpp-library-testing.cmake - Testing setup with doctest +# cpp-library-testing.cmake - Runtime DLL deployment and testing compatibility # -# Note: Testing logic has been consolidated into the main cpp-library.cmake file -# This file is kept for backward compatibility but the actual implementation -# is now in the _cpp_library_setup_executables function. +# Provides cpp_library_copy_runtime_dlls for custom and toolkit-created executables. +# _cpp_library_setup_testing delegates to _cpp_library_setup_executables for compatibility. + +# Deploys runtime DLLs beside a custom target on Windows; empty lists need no copy. +# Call in the directory that created the target, as required by POST_BUILD commands. +function(cpp_library_copy_runtime_dlls target) + if(NOT TARGET "${target}") + message(FATAL_ERROR "cpp_library_copy_runtime_dlls: Target does not exist: ${target}") + endif() + if(WIN32) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_COMMAND}" + "-DCPP_LIBRARY_RUNTIME_DLLS=$" + "-DCPP_LIBRARY_RUNTIME_DESTINATION=$" + -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/cpp-library-copy-runtime-dlls.cmake" + VERBATIM) + endif() +endfunction() # Delegates to _cpp_library_setup_executables for backward compatibility. # - Postcondition: test executables configured via _cpp_library_setup_executables diff --git a/cpp-library.cmake b/cpp-library.cmake index ef397ec..88ab48d 100644 --- a/cpp-library.cmake +++ b/cpp-library.cmake @@ -134,15 +134,7 @@ function(_cpp_library_setup_executables) add_executable(${executable_base} "${source_dir}/${executable}") target_link_libraries(${executable_base} PRIVATE ${ARG_NAMESPACE}::${CLEAN_NAME} doctest::doctest) - if(WIN32) - # A script handles empty DLL lists without requiring newer copy commands. - add_custom_command(TARGET ${executable_base} POST_BUILD - COMMAND "${CMAKE_COMMAND}" - "-DCPP_LIBRARY_RUNTIME_DLLS=$" - "-DCPP_LIBRARY_RUNTIME_DESTINATION=$" - -P "${CPP_LIBRARY_ROOT}/cmake/cpp-library-copy-runtime-dlls.cmake" - VERBATIM) - endif() + cpp_library_copy_runtime_dlls(${executable_base}) # Register as CTest test add_test(NAME ${executable_base} COMMAND ${executable_base}) @@ -182,6 +174,8 @@ function(cpp_library_setup) EXAMPLES # Example source files to build (e.g., "example.cpp example_fail.cpp") TESTS # Test source files to build (e.g., "tests.cpp") DOCS_EXCLUDE_SYMBOLS # Symbols to exclude from docs + DOCS_INPUTS # Additional documentation files or directories + DOCS_OPTIONS # Single-line Doxygen assignments applied after the template ) cmake_parse_arguments(ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -189,6 +183,11 @@ function(cpp_library_setup) if("INSTALL_OPTION" IN_LIST ARG_KEYWORDS_MISSING_VALUES) message(FATAL_ERROR "cpp_library_setup: INSTALL_OPTION requires a value") endif() + foreach(keyword DOCS_INPUTS DOCS_OPTIONS) + if(keyword IN_LIST ARG_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "cpp_library_setup: ${keyword} requires a value") + endif() + endforeach() set(install_args) if(DEFINED ARG_INSTALL_OPTION) list(APPEND install_args INSTALL_OPTION "${ARG_INSTALL_OPTION}") @@ -333,6 +332,8 @@ function(cpp_library_setup) VERSION "${ARG_VERSION}" DESCRIPTION "${ARG_DESCRIPTION}" DOCS_EXCLUDE_SYMBOLS "${ARG_DOCS_EXCLUDE_SYMBOLS}" + DOCS_INPUTS "${ARG_DOCS_INPUTS}" + DOCS_OPTIONS "${ARG_DOCS_OPTIONS}" ) endif() diff --git a/templates/Doxyfile.in b/templates/Doxyfile.in index 7d786e7..99a2b0d 100644 --- a/templates/Doxyfile.in +++ b/templates/Doxyfile.in @@ -4,18 +4,18 @@ PROJECT_NAME = "@PROJECT_NAME@" PROJECT_BRIEF = "@PROJECT_BRIEF@" PROJECT_NUMBER = @PROJECT_VERSION@ -OUTPUT_DIRECTORY = @OUTPUT_DIR@ +OUTPUT_DIRECTORY = "@OUTPUT_DIR@" GENERATE_LATEX = NO QUIET = YES WARN_IF_UNDOCUMENTED = YES -INPUT = @INPUT_DIR@ +INPUT = "@INPUT_DIR@" RECURSIVE = YES EXCLUDE_SYMBOLS = @EXCLUDE_SYMBOLS@ -EXAMPLE_PATH = @EXAMPLES_PATH@ +EXAMPLE_PATH = "@EXAMPLES_PATH@" HIDE_UNDOC_NAMESPACES = NO -HTML_EXTRA_STYLESHEET = @AWESOME_CSS_PATH@/doxygen-awesome.css \ - @AWESOME_CSS_PATH@/doxygen-awesome-sidebar-only.css \ - @CPP_LIBRARY_ROOT@/templates/custom.css +HTML_EXTRA_STYLESHEET = "@AWESOME_CSS_PATH@/doxygen-awesome.css" \ + "@AWESOME_CSS_PATH@/doxygen-awesome-sidebar-only.css" \ + "@CPP_LIBRARY_ROOT@/templates/custom.css" HTML_COLORSTYLE = LIGHT GENERATE_TREEVIEW = YES DISABLE_INDEX = NO diff --git a/tests/docs/fixtures/CMakeLists.txt b/tests/docs/fixtures/CMakeLists.txt new file mode 100644 index 0000000..dfe58c8 --- /dev/null +++ b/tests/docs/fixtures/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.24) +project(docs-fixture LANGUAGES CXX) +include("${TOOLKIT_SOURCE}/cpp-library.cmake") +set(CPP_LIBRARY_VERSION 1.2.3) +set(FIXTURE_INSTALL OFF CACHE BOOL "") +set(BUILD_DOCS ON) + +# Replace only the network theme download; exercise real configuration and Doxygen. +function(CPMAddPackage) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/theme") + foreach(stylesheet doxygen-awesome.css doxygen-awesome-sidebar-only.css) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/theme/${stylesheet}" "") + endforeach() + set(doxygen-awesome-css_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/theme" PARENT_SCOPE) +endfunction() + +set(docs_args) +if(CASE STREQUAL "extended") + list(APPEND docs_args + DOCS_INPUTS "extra docs" "${CMAKE_CURRENT_SOURCE_DIR}/absolute docs/group.dox" + DOCS_OPTIONS + "PROJECT_BRIEF = \"Customized documentation\"" + "WARN_IF_UNDOCUMENTED = NO" + [=[PREDEFINED = "FIXTURE_PATH=C:\\docs\\"]=] + "EXCLUDE_SYMBOLS += fixture::extra" + DOCS_EXCLUDE_SYMBOLS "fixture::hidden") +elseif(CASE STREQUAL "missing-input") + list(APPEND docs_args DOCS_INPUTS "missing docs") +elseif(CASE STREQUAL "malformed-option") + list(APPEND docs_args DOCS_OPTIONS "not a Doxygen assignment") +elseif(CASE STREQUAL "malformed-terminal-continuation-option") + list(APPEND docs_args DOCS_OPTIONS [=[PREDEFINED = A= \]=]) +elseif(CASE STREQUAL "malformed-continuation-option") + list(APPEND docs_args DOCS_OPTIONS + [=[PREDEFINED = A= \ ]=] + "WARN_IF_UNDOCUMENTED = NO") +elseif(CASE STREQUAL "multiline-option") + list(APPEND docs_args DOCS_OPTIONS "QUIET = YES\nGENERATE_XML = YES") +elseif(CASE STREQUAL "missing-input-value") + list(APPEND docs_args DOCS_INPUTS) +elseif(CASE STREQUAL "missing-option-value") + list(APPEND docs_args DOCS_OPTIONS) +elseif(CASE STREQUAL "custom") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/docs") + file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile" + "PROJECT_NAME = \"Legacy configuration\"\n" + "INPUT = \"@INPUT_DIR@\"\n" + "OUTPUT_DIRECTORY = \"@OUTPUT_DIR@\"\n" + "GENERATE_LATEX = NO\nQUIET = YES\nWARN_AS_ERROR = YES\n") + list(APPEND docs_args DOCS_INPUTS "extra docs" + DOCS_OPTIONS "PROJECT_BRIEF = \"Customized documentation\"") +endif() + +cpp_library_setup( + DESCRIPTION "Documentation fixture" + NAMESPACE fixture + INSTALL_OPTION FIXTURE_INSTALL + HEADERS api.hpp + ${docs_args}) diff --git a/tests/docs/fixtures/absolute docs/group.dox b/tests/docs/fixtures/absolute docs/group.dox new file mode 100644 index 0000000..f0d0960 --- /dev/null +++ b/tests/docs/fixtures/absolute docs/group.dox @@ -0,0 +1,3 @@ +/** @defgroup extra_group Absolute fixture group + * @brief This group comes from an absolute input file. + */ diff --git a/tests/docs/fixtures/extra docs/mainpage.dox b/tests/docs/fixtures/extra docs/mainpage.dox new file mode 100644 index 0000000..2f209d0 --- /dev/null +++ b/tests/docs/fixtures/extra docs/mainpage.dox @@ -0,0 +1,3 @@ +/** @mainpage Extra fixture content + * This page comes from an additional, space-containing input directory. + */ diff --git a/tests/docs/fixtures/include/fixture/api.hpp b/tests/docs/fixtures/include/fixture/api.hpp new file mode 100644 index 0000000..ad3e3aa --- /dev/null +++ b/tests/docs/fixtures/include/fixture/api.hpp @@ -0,0 +1,12 @@ +/** @file + * @brief Fixture public API. + */ +/** @brief Fixture namespace. */ +namespace fixture { +/** @brief Return the fixture value. */ +inline int value() { return 42; } +/** @brief Internal API omitted from the extended documentation. */ +struct hidden {}; +/** @brief Internal API omitted through an additive Doxygen option. */ +struct extra {}; +} diff --git a/tests/docs/test_docs.cmake b/tests/docs/test_docs.cmake new file mode 100644 index 0000000..6609f4e --- /dev/null +++ b/tests/docs/test_docs.cmake @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: BSL-1.0 +# Run as: cmake -P tests/docs/test_docs.cmake (requires Doxygen and Ninja). +cmake_minimum_required(VERSION 3.24) +get_filename_component(toolkit_source "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +string(RANDOM LENGTH 16 ALPHABET 0123456789abcdef test_id) +set(test_root "${toolkit_source}/build/docs-${test_id}") + +foreach(case default extended custom missing-input malformed-option + malformed-terminal-continuation-option malformed-continuation-option multiline-option + missing-input-value missing-option-value) + set(source "${test_root}/${case}/source with spaces") + set(binary "${test_root}/${case}/build with spaces") + file(MAKE_DIRECTORY "${source}") + file(COPY "${CMAKE_CURRENT_LIST_DIR}/fixtures/" DESTINATION "${source}") + execute_process(COMMAND "${CMAKE_COMMAND}" -S "${source}" -B "${binary}" -G Ninja + "-DTOOLKIT_SOURCE=${toolkit_source}" "-DCASE=${case}" + RESULT_VARIABLE result OUTPUT_VARIABLE out ERROR_VARIABLE err) + if(case MATCHES "^(missing-|malformed-|multiline-)") + if(case STREQUAL "missing-input") + set(expected "cpp-library: Documentation input does not exist") + elseif(case STREQUAL "missing-input-value") + set(expected "cpp_library_setup: DOCS_INPUTS requires a value") + elseif(case STREQUAL "missing-option-value") + set(expected "cpp_library_setup: DOCS_OPTIONS requires a value") + else() + set(expected "cpp-library: DOCS_OPTIONS requires single-line Doxygen assignments") + endif() + string(FIND "${out}\n${err}" "${expected}" diagnostic) + if(result EQUAL 0 OR diagnostic LESS 0) + message(FATAL_ERROR "${case}: Expected '${expected}':\n${out}\n${err}") + endif() + else() + if(NOT result EQUAL 0) + message(FATAL_ERROR "${case}: Configure failed:\n${out}\n${err}") + endif() + execute_process(COMMAND "${CMAKE_COMMAND}" --build "${binary}" --target docs + RESULT_VARIABLE result OUTPUT_VARIABLE out ERROR_VARIABLE err) + if(NOT result EQUAL 0) + message(FATAL_ERROR "${case}: Doxygen failed:\n${out}\n${err}") + endif() + file(READ "${binary}/html/index.html" index) + if(case STREQUAL "default") + string(FIND "${index}" "Documentation fixture" brief) + else() + string(FIND "${index}" "Customized documentation" brief) + string(FIND "${index}" "Extra fixture content" page) + if(page LESS 0) + message(FATAL_ERROR "${case}: Additional main page is missing") + endif() + endif() + if(brief LESS 0) + message(FATAL_ERROR "${case}: Project brief is missing") + endif() + if(case STREQUAL "default" OR case STREQUAL "extended") + foreach(symbol hidden extra) + set(page "${binary}/html/structfixture_1_1${symbol}.html") + if(case STREQUAL "extended" AND EXISTS "${page}") + message(FATAL_ERROR "Excluded symbol is present: ${symbol}") + elseif(case STREQUAL "default" AND NOT EXISTS "${page}") + message(FATAL_ERROR "Default documentation lost a public symbol: ${symbol}") + endif() + endforeach() + endif() + if(case STREQUAL "extended") + if(NOT EXISTS "${binary}/html/group__extra__group.html") + message(FATAL_ERROR "Absolute input group is missing") + endif() + endif() + if(case STREQUAL "custom") + string(FIND "${index}" "Legacy configuration" legacy) + if(legacy LESS 0) + message(FATAL_ERROR "Custom Doxyfile compatibility was lost") + endif() + endif() + endif() + message(STATUS "PASS documentation ${case}") +endforeach() + +file(REMOVE_RECURSE "${test_root}") diff --git a/tests/setup/fixtures/runtime_dlls/CMakeLists.txt b/tests/setup/fixtures/runtime_dlls/CMakeLists.txt new file mode 100644 index 0000000..7e41fb3 --- /dev/null +++ b/tests/setup/fixtures/runtime_dlls/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.24) +project(runtime-dlls LANGUAGES CXX) +include("${TOOLKIT_SOURCE}/cpp-library.cmake") +enable_testing() +add_library(runtime SHARED runtime.cpp) +set_target_properties(runtime PROPERTIES + WINDOWS_EXPORT_ALL_SYMBOLS ON + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/runtime") +add_executable(client main.cpp) +target_link_libraries(client PRIVATE runtime) +set_target_properties(client PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/client apps") +cpp_library_copy_runtime_dlls(client) +add_test(NAME runtime-consumer COMMAND client) +add_executable(static_client main.cpp runtime.cpp) +set_target_properties(static_client PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/static apps") +cpp_library_copy_runtime_dlls(static_client) +add_test(NAME static-consumer COMMAND static_client) +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/runtime-dlls.cmake" + CONTENT "set(runtime_dlls [[$]])\n") diff --git a/tests/setup/fixtures/runtime_dlls/main.cpp b/tests/setup/fixtures/runtime_dlls/main.cpp new file mode 100644 index 0000000..9babfac --- /dev/null +++ b/tests/setup/fixtures/runtime_dlls/main.cpp @@ -0,0 +1,2 @@ +int runtime_value(); +int main() { return runtime_value() == 42 ? 0 : 1; } diff --git a/tests/setup/fixtures/runtime_dlls/runtime.cpp b/tests/setup/fixtures/runtime_dlls/runtime.cpp new file mode 100644 index 0000000..7cc4f2c --- /dev/null +++ b/tests/setup/fixtures/runtime_dlls/runtime.cpp @@ -0,0 +1 @@ +int runtime_value() { return 42; } diff --git a/tests/setup/test_runtime_dlls.cmake b/tests/setup/test_runtime_dlls.cmake new file mode 100644 index 0000000..2c03a03 --- /dev/null +++ b/tests/setup/test_runtime_dlls.cmake @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: BSL-1.0 +# Run as: cmake -P tests/setup/test_runtime_dlls.cmake +cmake_minimum_required(VERSION 3.24) +get_filename_component(toolkit_source "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +string(RANDOM LENGTH 16 ALPHABET 0123456789abcdef test_id) +set(test_root "${toolkit_source}/build/runtime-dlls-${test_id}") +find_program(ctest NAMES ctest REQUIRED) + +function(run) + execute_process(COMMAND ${ARGV} RESULT_VARIABLE result + OUTPUT_VARIABLE out ERROR_VARIABLE err) + if(NOT result EQUAL 0) + message(FATAL_ERROR "Command failed (${result}): ${ARGV}\n${out}\n${err}") + endif() +endfunction() + +run("${CMAKE_COMMAND}" -S "${CMAKE_CURRENT_LIST_DIR}/fixtures/runtime_dlls" + -B "${test_root}" -G Ninja "-DTOOLKIT_SOURCE=${toolkit_source}") +run("${CMAKE_COMMAND}" --build "${test_root}") +if(WIN32) + include("${test_root}/runtime-dlls.cmake") + if(NOT runtime_dlls) + message(FATAL_ERROR "Shared runtime fixture did not produce a DLL") + endif() + foreach(dll IN LISTS runtime_dlls) + get_filename_component(filename "${dll}" NAME) + if(NOT EXISTS "${test_root}/client apps/${filename}") + message(FATAL_ERROR "Runtime DLL was not deployed: ${filename}") + endif() + endforeach() +endif() +run("${ctest}" --test-dir "${test_root}" --output-on-failure) +file(REMOVE_RECURSE "${test_root}") +message(STATUS "PASS custom shared and static executable runtime deployment")