Skip to content

Conversation

@sean-parent
Copy link
Member

@sean-parent sean-parent commented Nov 11, 2025

Added cleaned up installation support, along with an overhauled readme.


Note

Adds full CMake install/package config generation with dependency mapping, generates CI workflows with install verification, introduces an interactive setup script, and significantly expands the README and presets.

  • CMake/Build System:
    • Installation & Packaging: New cmake/cpp-library-install.cmake to install targets (HEADER-ONLY and compiled), generate Config.cmake/version files, and auto-derive find_dependency() from INTERFACE_LINK_LIBRARIES (with cpp_library_map_dependency). Updates templates/Config.cmake.in accordingly.
    • Core Integration: _cpp_library_get_git_version supports CPP_LIBRARY_VERSION; targets add INSTALL_INTERFACE:include; install step wired into _cpp_library_setup_core; cleaner alias name handling; improved exec setup linking.
    • CI Workflow Templating: Add cmake/cpp-library-ci.cmake and template templates/.github/workflows/ci.yml.in; copy step now configures workflow with PACKAGE_NAME. Removes static workflow template.
    • Presets: Add install configure/build presets in templates/CMakePresets.json.
  • Tooling/Docs:
    • Setup Script: New interactive setup.cmake to scaffold projects (dirs, CPM, sample files, CMakeLists, git init).
    • Docs: Minor enhancements in Doxygen setup comments; README massively expanded with Quick Start, manual/consumption, install, dependency handling, target naming, CI, versioning, and reference sections.
  • Misc:
    • Add .vscode/settings.json; tweak recommended extensions template.

Written by Cursor Bugbot for commit f13bae3. This will update automatically on new commits. Configure here.

Added cleaned up installation support, along with an overhauled readme.
cursor[bot]

This comment was marked as resolved.

This CI test added complexity without adding value. Testing find_package() is sufficient to ensure the package installs correctly.
To simplify the ci logic, I'm changing the ci.yml file to a template.
Adds conditional execution for environment setup and CMake configuration steps based on the presence of matrix.cc.
Copy link

@Ikar-Rahl Ikar-Rahl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your work ! I am happy to participate if needed. :-)

tested on my vcpkg environment, got stuck on stlab, but otherwise promising !


# Generate package config file from template
configure_file(
"${CPP_LIBRARY_ROOT}/templates/Config.cmake.in"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the delicate part (the PITA).
This would work well for enum and copy, because their only dependency is cpp_library, and it is a private dependency (no need to find_dependency it).
For stlab, it has internal (enum, copy) and external (qt, libdispatch, Threads) that are public (stlab users will need them if they want to use stlab, well, those who were used during stlab build).

the classic way is that each project will have it own tmpl, and double edit each time he modify a dependency.
this is where my previous proposal of a wrapper "get_dependency" would be handy.
It would store the public dependency, and will be used to generate the corresponding find_dependency calls.
Config.cmake.in (I use Config.tmpl.cmake to keep syntax highlights) would have @Dependencies@ that will replaced here.

If you wish, I can implement a prototype of that. Ideally _cpp_library_setup_install should be called in stlab to test it.

If you wonder in term of test and ci, this issue would have become apparent if it was used on a project with external dependencies, installed, and imported in a test project. It would have complained that Qt5, enum or something was not found and stlab link against it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mind adding a way to specify additional dependencies that are not through CPM (e.g., qt, libdispatch). Are CPM dependencies handled correctly (modulo the above naming issue)?

I'm less thrilled with something that requires wrapping CPMAddPackage.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though about just using CPM_PACKAGES and CPM_PACKAGE_*_VERSION for theses, but there is one main issue with that, CPM does not indicate anywhere if a dependency is public or private cpp_library and enum are good 2 examples, one is for internal use only, another has to be exposed, and CPM does not have this information.
We could, of course, make workarounds, keeping a hard coded list of private deps like cpp_library. But I feel it a bit shaky.

There is also benefit that all you dependency go through the same function call, no matter the flavor.

It is up to you to decide what you prefer, I can implement it either way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public/private/interface information is provided in target_link_libraries() - in my experiments the dependency on cpp_library is not currently exported but if the other dependencies are.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay - I updated the auto dependency() call generation to allow a map for custom dependency formats. This way, I'm not on the hook to keep a custom table up-to-date.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So consumer will need to provide correct name for stlab dependencies ?
Sure you do not want a wrapper for dependencies ? It would solve all of this imbroglio.
Anyway, this would work, even though currently it would still generate find_dependency(stlab-copy-on-write) as stlab target name was not changed, only the package name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So consumer will need to provide correct name for stlab dependencies ?

I'm not sure what that means - where would they need to provide the name? I reviewed all the naming and made changes. If you have project(enum-ops) ... cpp_library(NAMESPACE stlab...), the package name is stlab-enum-ops both for the installed package and the generated dependency for a client. There were a few inconsistencies in this area but I think I've cleaned them all up (check the read-me and the enum-ops library branch that is using this version).

The install should correctly generate all the dependency calls.

# - Postcondition: OUTPUT_VAR set to version string with 'v' prefix removed
function(_cpp_library_get_git_version OUTPUT_VAR)
# Try to get version from git tags
execute_process(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While unrelated with cmake install, there is a vcpkg limitation in that the .git folder is not available after extracting the sources. But vcpkg know which version get pulled;
This make the install generate a set(PACKAGE_VERSION "0.0.0")

if(DEFINED PROJECT_VERSION AND NOT PROJECT_VERSION STREQUAL "")
set(${OUTPUT_VAR} "${PROJECT_VERSION}" PARENT_SCOPE)
return()
endif()

Would solve the issue with minimal interference.
This could be done in the vcpkg port though it is a bit dirty. But i would understand if you do not which to add variables that would have such specific use cases.

In term of test and ci, this would become apparent only if the component was build through vcpkg, or any other package manager that strips the .git folder from the sources. Or in an sandboxed environment that does not have git.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good catch - fixed (please verify).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, crap, I sent the wrong code, it is not PROJECT_VERSION, but it should be something else (anything really), I used EXTERNAL_PROJECT_VERSION.
Issue with PROJECT_VERSION is that it is overwritten by cmake during project(stlab-copy-on-write), so there is no version there.
My bad ^^'

The _cpp_library_setup_install function now requires a PACKAGE_NAME argument, ensuring correct package naming in generated CMake config files and install locations. Version detection in _cpp_library_get_git_version now prefers PROJECT_VERSION if set, improving compatibility with package managers and source archives.
Added logic to introspect INTERFACE_LINK_LIBRARIES and generate appropriate find_dependency() calls in installed CMake package config files. Updated documentation to explain dependency handling, added a helper function in cpp-library-install.cmake, and modified the config template to include generated dependencies. This ensures downstream users automatically find and link required dependencies.
Replaces usage of a 'clean' name with PACKAGE_NAME for library target aliases and CI workflow templates. Ensures consistent naming between CMake targets and package discovery, improving clarity and reducing potential mismatches.
Introduces the cpp_library_map_dependency() function to allow custom find_dependency() calls for specific targets, such as Qt components requiring COMPONENTS syntax. Updates documentation and dependency generation logic to prioritize custom mappings, improving flexibility for complex dependencies in installed CMake package config files.
Updated internal CMake functions to require PACKAGE_NAME as a parameter instead of relying on PROJECT_NAME. This clarifies the preconditions for template and CI workflow generation, ensuring correct substitution and improving maintainability.
Copy link

@Ikar-Rahl Ikar-Rahl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was able to test it with stlab and confirmed that it generate a config file, with some fixes as noted above.
I would suggest also reconsidering not using a dependency wrapper, but I may not have the full picture on the rational regarding that.

# - Postcondition: OUTPUT_VAR set to version string with 'v' prefix removed
function(_cpp_library_get_git_version OUTPUT_VAR)
# Try to get version from git tags
execute_process(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, crap, I sent the wrong code, it is not PROJECT_VERSION, but it should be something else (anything really), I used EXTERNAL_PROJECT_VERSION.
Issue with PROJECT_VERSION is that it is overwritten by cmake during project(stlab-copy-on-write), so there is no version there.
My bad ^^'

The install-test job was removed from the GitHub Actions workflow because it duplicated functionality already provided by the main test job. This simplifies the workflow and reduces unnecessary runs across multiple operating systems.

Also fixed a quoting issue with the install prefix on Windows.
Updated dependency mapping logic to distinguish between internal cpp-library and external dependencies, ensuring package names are collision-safe and consistent. Added support for overriding the version using the CPP_LIBRARY_VERSION variable, which is useful for package managers and CI systems without git history. Improved documentation in README.md to clarify dependency handling, target naming patterns, and version management. Refactored CMake scripts to align with these changes and updated comments for clarity.
Adds conditional steps in the CI workflow to set CC and CXX environment variables only when matrix.cc is defined. Ensures builds use the correct compiler configuration based on the matrix setup.
Clarifies that GitHub repository names must match package names, including namespace prefixes, for CPM compatibility. Updates usage examples, instructions, and project links to reflect this requirement and prevent issues with CPM's local package finding and source fetching.

Repository naming requirements may be backed out if [this CPM PR](cpm-cmake/CPM.cmake#682) lands.
add_library(${ARG_NAMESPACE}::${ARG_CLEAN_NAME} ALIAS ${ARG_NAME})
target_include_directories(${ARG_NAME} INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Exported target name incorrect for alternative naming pattern

When using project(namespace-component) with NAMESPACE namespace, the exported target becomes namespace::namespace-component instead of namespace::component. The target is created with name ARG_NAME (which could be namespace-component), but no EXPORT_NAME property is set. When install(EXPORT) adds the NAMESPACE prefix at line 162 of cmake/cpp-library-install.cmake, it creates a double prefix. Consumers expecting namespace::component (matching the build-tree alias) will fail to link. The target needs EXPORT_NAME set to CLEAN_NAME to ensure consistent exported names regardless of project naming pattern.

Fix in Cursor Fix in Web


# Calculate clean name (without namespace prefix) for target alias
# If PROJECT_NAME starts with NAMESPACE-, strip it; otherwise use PROJECT_NAME as-is
string(REGEX REPLACE "^${ARG_NAMESPACE}-" "" CLEAN_NAME "${ARG_NAME}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Namespace not escaped in regex pattern

The namespace is used directly in a regex pattern without escaping special regex characters. If the namespace contains characters like ., +, *, or [, the regex will interpret them as metacharacters rather than literals, causing incorrect pattern matching. For example, namespace my.lib would match my-lib because . matches any character. This affects clean name calculation and could lead to incorrect target naming. The same issue exists at line 38 in _cpp_library_setup_executables.

Fix in Cursor Fix in Web

Introduces setup.cmake, an interactive script for initializing new C++ library projects using the cpp-library template. The README is updated with detailed quick start instructions for using the script in both interactive and non-interactive modes, as well as guidance for manual setup.
# Skip generator expressions (typically BUILD_INTERFACE dependencies)
if(LIB MATCHES "^\\$<")
continue()
endif()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Generator expressions incorrectly skipped in dependency generation

The dependency generation skips all generator expressions, including $<INSTALL_INTERFACE:...> dependencies that should be processed. This causes dependencies specified with $<INSTALL_INTERFACE:package::target> to be omitted from the generated find_dependency() calls in the package config file, breaking downstream builds that rely on those dependencies. Only $<BUILD_INTERFACE:...> should be skipped, while $<INSTALL_INTERFACE:...> content needs extraction and processing.

Fix in Cursor Fix in Web

set(DEPENDENCY_LINES "")
endif()

set(${OUTPUT_VAR} "${DEPENDENCY_LINES}" PARENT_SCOPE)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Multiple find_dependency calls for same package with components

When a library links against multiple components of the same package (e.g., Qt5::Core and Qt5::Widgets), the dependency generation creates separate find_dependency() calls for each component instead of merging them into a single call with multiple components. This generates find_dependency(Qt5 COMPONENTS Core) followed by find_dependency(Qt5 COMPONENTS Widgets), which may fail or behave unexpectedly. The correct output should be a single find_dependency(Qt5 COMPONENTS Core Widgets) call.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants