Skip to content

Commit e4a3ee5

Browse files
committed
Improve dependency mapping and version override support
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.
1 parent 0516b9b commit e4a3ee5

File tree

4 files changed

+89
-32
lines changed

4 files changed

+89
-32
lines changed

README.md

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,22 @@ When installed, the generated `my-libConfig.cmake` will include:
149149
include(CMakeFindDependencyMacro)
150150
151151
# Find dependencies required by this package
152-
find_dependency(copy-on-write)
153-
find_dependency(enum-ops)
152+
find_dependency(stlab-copy-on-write)
153+
find_dependency(stlab-enum-ops)
154154
find_dependency(Threads)
155155
156156
include("${CMAKE_CURRENT_LIST_DIR}/my-libTargets.cmake")
157157
```
158158

159159
**Default dependency handling:**
160160

161-
- **cpp-library dependencies** (matching your project's `NAMESPACE`): Automatically mapped to their package names (e.g., `stlab::copy-on-write``find_dependency(copy-on-write)`)
162-
- **Other packages**: Uses the package name only by default (e.g., `PackageName::Target``find_dependency(PackageName)`)
161+
- **cpp-library dependencies** (matching your project's `NAMESPACE`):
162+
- When namespace and component match: `namespace::namespace``find_dependency(namespace)`
163+
- When they differ: `namespace::component``find_dependency(namespace-component)`
164+
- Example: `stlab::copy-on-write``find_dependency(stlab-copy-on-write)`
165+
- **Other packages**: Uses the package name only by default
166+
- Example: `Threads::Threads``find_dependency(Threads)`
167+
- Example: `Boost::filesystem``find_dependency(Boost)`
163168

164169
**Custom dependency mappings:**
165170

@@ -222,6 +227,8 @@ git push origin v1.0.0
222227

223228
Tags should follow [semantic versioning](https://semver.org/) (e.g., `v1.0.0`, `v2.1.3`).
224229

230+
Alternatively, you can override the version using `-DCPP_LIBRARY_VERSION=x.y.z` (useful for package managers). See [Version Management](#version-management) for details.
231+
225232
#### GitHub Pages Deployment
226233

227234
To enable automatic documentation deployment to GitHub Pages:
@@ -257,30 +264,52 @@ cpp_library_setup(
257264
**Notes:**
258265

259266
- The project name is automatically taken from `PROJECT_NAME` (set by the `project()` command). You must call `project(your-library)` before `cpp_library_setup()`.
260-
- Version is automatically detected from git tags (see [Version Tagging](#version-tagging)).
267+
- Version is automatically detected from git tags, or can be overridden with `-DCPP_LIBRARY_VERSION=x.y.z` (see [Version Management](#version-management)).
261268
- Examples using doctest should include `test` in the filename to be visible in the [C++ TestMate](https://marketplace.visualstudio.com/items?itemName=matepek.vscode-catch2-test-adapter) extension for VS Code test explorer.
262269

263270
### Target Naming
264271

265-
For `project(your-library)`, `cpp_library_setup` will create a target called `your-library`.
266-
267-
The utility will additionally create an alias target based on the `NAMESPACE` option: `your_namespace::your-library`.
272+
**Recommended Pattern** (collision-safe):
268273

269-
If your project name starts with the namespace followed by a dash, the namespace in the project name is stripped from the alias target:
274+
Use the component name as your project name, and specify the organizational namespace separately:
270275

271276
```cmake
272-
cmake_minimum_required(VERSION 3.20)
273-
project(namespace-library)
277+
project(enum-ops) # Component name only
278+
279+
cpp_library_setup(
280+
NAMESPACE stlab # Organizational namespace
281+
# ...
282+
)
283+
```
284+
285+
This produces:
286+
287+
- Target name: `enum-ops`
288+
- Package name: `stlab-enum-ops` (used in `find_package(stlab-enum-ops)`)
289+
- Target alias: `stlab::enum-ops` (used in `target_link_libraries()`)
290+
291+
**Alternative Patterns:**
274292

275-
# ... CPM setup ...
293+
You can also use `project(namespace-component)` - the namespace prefix will be detected and stripped from the target alias:
294+
295+
```cmake
296+
project(stlab-enum-ops) # Includes namespace prefix
276297
277298
cpp_library_setup(
278-
NAMESPACE namespace
299+
NAMESPACE stlab
279300
# ...
280301
)
281302
```
282303

283-
Results in `namespace-library` and `namespace::library` targets.
304+
Produces the same result as above.
305+
306+
**Single-component namespace** (e.g., `project(stlab)` with `NAMESPACE stlab`):
307+
308+
- Target name: `stlab`
309+
- Package name: `stlab` (used in `find_package(stlab)`)
310+
- Target alias: `stlab::stlab` (used in `target_link_libraries()`)
311+
312+
All package names include the namespace prefix for collision prevention.
284313

285314
### `cpp_library_map_dependency`
286315

@@ -380,6 +409,15 @@ Version is automatically detected from git tags:
380409
- Falls back to `0.0.0` if no tag is found (with warning)
381410
- Version used in CMake package config files
382411

412+
For package managers or CI systems building from source archives without git history, you can override the version using the `CPP_LIBRARY_VERSION` cache variable:
413+
414+
```bash
415+
cmake -DCPP_LIBRARY_VERSION=1.2.3 -B build
416+
cmake --build build
417+
```
418+
419+
This is particularly useful for vcpkg, Conan, or other package managers that don't have access to git tags.
420+
383421
### Testing
384422

385423
- **Test framework**: [doctest](https://github.com/doctest/doctest)

cmake/cpp-library-install.cmake

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ include(CMakePackageConfigHelpers)
1616
# - Precondition: TARGET is a namespaced target (e.g., "Qt5::Core", "Qt5::Widgets")
1717
# - Postcondition: FIND_DEPENDENCY_CALL stored for TARGET, used in package config generation
1818
# - Example: cpp_library_map_dependency("Qt5::Core" "Qt5 COMPONENTS Core")
19+
# - Note: Most dependencies work automatically; only use for special syntax (COMPONENTS, etc.)
1920
function(cpp_library_map_dependency TARGET FIND_DEPENDENCY_CALL)
2021
set_property(GLOBAL PROPERTY _CPP_LIBRARY_DEPENDENCY_MAP_${TARGET} "${FIND_DEPENDENCY_CALL}")
2122
endfunction()
@@ -24,7 +25,8 @@ endfunction()
2425
# - Precondition: TARGET_NAME specifies existing target with INTERFACE_LINK_LIBRARIES
2526
# - Postcondition: OUTPUT_VAR contains newline-separated find_dependency() calls for public dependencies
2627
# - Uses cpp_library_map_dependency() mappings if registered, otherwise uses defaults
27-
# - Automatically handles cpp-library dependencies (namespace::package → find_dependency(package))
28+
# - cpp-library dependencies: namespace::namespace → find_dependency(namespace), namespace::component → find_dependency(namespace-component)
29+
# - External dependencies: name::name → find_dependency(name), name::component → find_dependency(name)
2830
function(_cpp_library_generate_dependencies OUTPUT_VAR TARGET_NAME NAMESPACE)
2931
get_target_property(LINK_LIBS ${TARGET_NAME} INTERFACE_LINK_LIBRARIES)
3032

@@ -53,11 +55,17 @@ function(_cpp_library_generate_dependencies OUTPUT_VAR TARGET_NAME NAMESPACE)
5355
# Use custom mapping (e.g., "Qt5 COMPONENTS Core" for Qt5::Core)
5456
list(APPEND DEPENDENCY_LIST "find_dependency(${CUSTOM_MAPPING})")
5557
elseif(PKG_NAME STREQUAL NAMESPACE)
56-
# Internal cpp-library dependency: use component as package name
57-
# (e.g., stlab::copy-on-write → find_dependency(copy-on-write))
58-
list(APPEND DEPENDENCY_LIST "find_dependency(${COMPONENT})")
58+
# Internal cpp-library dependency
59+
if(PKG_NAME STREQUAL COMPONENT)
60+
# Namespace and component match: namespace::namespace → find_dependency(namespace)
61+
list(APPEND DEPENDENCY_LIST "find_dependency(${PKG_NAME})")
62+
else()
63+
# Different names: namespace::component → find_dependency(namespace-component)
64+
list(APPEND DEPENDENCY_LIST "find_dependency(${PKG_NAME}-${COMPONENT})")
65+
endif()
5966
else()
60-
# Default: use package name only (e.g., libdispatch::libdispatch → find_dependency(libdispatch))
67+
# External dependency: use package name only
68+
# (e.g., Threads::Threads → find_dependency(Threads), Boost::filesystem → find_dependency(Boost))
6169
list(APPEND DEPENDENCY_LIST "find_dependency(${PKG_NAME})")
6270
endif()
6371
endif()
@@ -81,7 +89,7 @@ endfunction()
8189
function(_cpp_library_setup_install)
8290
set(oneValueArgs
8391
NAME # Target name (e.g., "stlab-enum-ops")
84-
PACKAGE_NAME # Package name for find_package() (e.g., "enum-ops")
92+
PACKAGE_NAME # Package name for find_package() (e.g., "stlab-enum-ops")
8593
VERSION # Version string (e.g., "1.2.3")
8694
NAMESPACE # Namespace for alias (e.g., "stlab")
8795
)

cmake/cpp-library-setup.cmake

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
#
33
# cpp-library-setup.cmake - Core library setup functionality
44

5-
# Returns version string from PROJECT_VERSION (if set), git tag (with 'v' prefix removed), or
5+
# Returns version string from CPP_LIBRARY_VERSION cache variable (if set), git tag (with 'v' prefix removed), or
66
# "0.0.0" fallback
77
function(_cpp_library_get_git_version OUTPUT_VAR)
8-
# If PROJECT_VERSION is already set (e.g., by vcpkg or other package manager),
8+
# If CPP_LIBRARY_VERSION is set (e.g., by vcpkg or other package manager via -DCPP_LIBRARY_VERSION=x.y.z),
99
# use it instead of trying to query git (which may not be available in source archives)
10-
if(DEFINED PROJECT_VERSION AND NOT PROJECT_VERSION STREQUAL "")
11-
set(${OUTPUT_VAR} "${PROJECT_VERSION}" PARENT_SCOPE)
10+
if(DEFINED CPP_LIBRARY_VERSION AND NOT CPP_LIBRARY_VERSION STREQUAL "")
11+
set(${OUTPUT_VAR} "${CPP_LIBRARY_VERSION}" PARENT_SCOPE)
1212
return()
1313
endif()
1414

@@ -33,15 +33,16 @@ function(_cpp_library_get_git_version OUTPUT_VAR)
3333
endfunction()
3434

3535
# Creates library target (INTERFACE or compiled) with headers and proper configuration.
36-
# - Precondition: NAME, NAMESPACE, PACKAGE_NAME, and REQUIRES_CPP_VERSION specified
37-
# - Postcondition: library target created with alias NAMESPACE::PACKAGE_NAME, install configured if TOP_LEVEL
36+
# - Precondition: NAME, NAMESPACE, PACKAGE_NAME, CLEAN_NAME, and REQUIRES_CPP_VERSION specified
37+
# - Postcondition: library target created with alias NAMESPACE::CLEAN_NAME, install configured if TOP_LEVEL
3838
function(_cpp_library_setup_core)
3939
set(oneValueArgs
4040
NAME
4141
VERSION
4242
DESCRIPTION
4343
NAMESPACE
4444
PACKAGE_NAME
45+
CLEAN_NAME
4546
REQUIRES_CPP_VERSION
4647
TOP_LEVEL
4748
)
@@ -64,7 +65,7 @@ function(_cpp_library_setup_core)
6465
if(ARG_SOURCES)
6566
# Create a library with sources (respects BUILD_SHARED_LIBS variable)
6667
add_library(${ARG_NAME} ${ARG_SOURCES})
67-
add_library(${ARG_NAMESPACE}::${ARG_PACKAGE_NAME} ALIAS ${ARG_NAME})
68+
add_library(${ARG_NAMESPACE}::${ARG_CLEAN_NAME} ALIAS ${ARG_NAME})
6869
target_include_directories(${ARG_NAME} PUBLIC
6970
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
7071
$<INSTALL_INTERFACE:include>
@@ -81,7 +82,7 @@ function(_cpp_library_setup_core)
8182
else()
8283
# Header-only INTERFACE target
8384
add_library(${ARG_NAME} INTERFACE)
84-
add_library(${ARG_NAMESPACE}::${ARG_PACKAGE_NAME} ALIAS ${ARG_NAME})
85+
add_library(${ARG_NAMESPACE}::${ARG_CLEAN_NAME} ALIAS ${ARG_NAME})
8586
target_include_directories(${ARG_NAME} INTERFACE
8687
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
8788
$<INSTALL_INTERFACE:include>

cpp-library.cmake

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ function(_cpp_library_setup_executables)
3434

3535
cmake_parse_arguments(ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
3636

37-
# Extract the clean library name for linking
38-
string(REPLACE "${ARG_NAMESPACE}-" "" CLEAN_NAME "${ARG_NAME}")
37+
# Extract the clean library name for linking (strip namespace prefix if present)
38+
string(REGEX REPLACE "^${ARG_NAMESPACE}-" "" CLEAN_NAME "${ARG_NAME}")
3939

4040
# Download doctest dependency via CPM
4141
if(NOT TARGET doctest::doctest)
@@ -140,8 +140,17 @@ function(cpp_library_setup)
140140
endif()
141141
set(ARG_NAME "${PROJECT_NAME}")
142142

143-
# Calculate PACKAGE_NAME (clean name without namespace prefix) for template substitution
144-
string(REPLACE "${ARG_NAMESPACE}-" "" PACKAGE_NAME "${ARG_NAME}")
143+
# Calculate clean name (without namespace prefix) for target alias
144+
# If PROJECT_NAME starts with NAMESPACE-, strip it; otherwise use PROJECT_NAME as-is
145+
string(REGEX REPLACE "^${ARG_NAMESPACE}-" "" CLEAN_NAME "${ARG_NAME}")
146+
147+
# Always prefix package name with namespace for collision prevention
148+
# Special case: if namespace equals clean name, don't duplicate (e.g., stlab::stlab → stlab)
149+
if(ARG_NAMESPACE STREQUAL CLEAN_NAME)
150+
set(PACKAGE_NAME "${ARG_NAMESPACE}")
151+
else()
152+
set(PACKAGE_NAME "${ARG_NAMESPACE}-${CLEAN_NAME}")
153+
endif()
145154

146155
# Set defaults
147156
if(NOT ARG_REQUIRES_CPP_VERSION)
@@ -188,6 +197,7 @@ function(cpp_library_setup)
188197
DESCRIPTION "${ARG_DESCRIPTION}"
189198
NAMESPACE "${ARG_NAMESPACE}"
190199
PACKAGE_NAME "${PACKAGE_NAME}"
200+
CLEAN_NAME "${CLEAN_NAME}"
191201
HEADERS "${GENERATED_HEADERS}"
192202
SOURCES "${GENERATED_SOURCES}"
193203
REQUIRES_CPP_VERSION "${ARG_REQUIRES_CPP_VERSION}"

0 commit comments

Comments
 (0)