Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ The library will be automatically fetched and built as part of your project.

#### Installation (optional)

Installation is optional and typically not required when using CPM. If you need to install your library (e.g., for system-wide deployment or use with a package manager) use:
Installation is optional and typically not required when using CPM. If you need to install your library (e.g., for system-wide deployment or use with a package manager):

```bash
# Build and install to default system location
Expand All @@ -202,8 +202,48 @@ cmake --install build/default
cmake --install build/default --prefix /opt/mylib
```

**Testing your installation:**

Use the `install` preset to verify that installed packages can be found correctly:

```bash
# Build with CPM_USE_LOCAL_PACKAGES to test finding installed dependencies
cmake --preset=install
cmake --build --preset=install

# Install and test
cmake --install build/install --prefix /tmp/test-install
CMAKE_PREFIX_PATH=/tmp/test-install cmake --preset=install
```

The `install` preset enables `CPM_USE_LOCAL_PACKAGES`, which makes CPM call `find_package()` first before fetching. This verifies your generated Config.cmake works correctly.

For information about using installed packages with `find_package()`, see the [CPM.cmake documentation](https://github.com/cpm-cmake/CPM.cmake) about [controlling how dependencies are found](https://github.com/cpm-cmake/CPM.cmake#cpm_use_local_packages).

**Re-exporting dependencies from external packages:**

When your library re-exports dependencies (via `INTERFACE` linkage) that come from CPMAddPackage, you must wrap them in `BUILD_INTERFACE` to avoid CMake export errors:

```cmake
# Correct: Wrap CPM-fetched dependencies in BUILD_INTERFACE
CPMAddPackage("gh:other-org/some-package@1.0.0")
target_link_libraries(my-library INTERFACE $<BUILD_INTERFACE:other::package>)
```

**Why this is needed:** CPMAddPackage creates regular (non-IMPORTED) targets in your build tree. When CMake exports your library, it sees these dependencies but cannot include them in your export set (they belong to a different package). The `BUILD_INTERFACE` wrapper tells CMake:
- Use this dependency during build
- Don't include it in the export
- Let consumers find it themselves via `find_dependency()` in the generated Config.cmake

cpp-library automatically extracts dependencies from `BUILD_INTERFACE` expressions and generates the appropriate `find_dependency()` calls in your package configuration.

**When BUILD_INTERFACE is NOT needed:**
- Dependencies from `find_package()` (already IMPORTED targets)
- System libraries like `Threads::Threads`
- Internal (PRIVATE) dependencies (not applicable to INTERFACE libraries)

This is a standard CMake pattern for re-exporting external package dependencies, not specific to cpp-library.

#### Dependency Handling in Installed Packages

cpp-library automatically generates `find_dependency()` calls in the installed CMake package configuration. Call `cpp_library_enable_dependency_tracking()` before `project()`:
Expand Down Expand Up @@ -470,6 +510,8 @@ cpp-library generates a `CMakePresets.json` file with the following configuratio

All presets automatically configure `CPM_SOURCE_CACHE` to `${sourceDir}/.cache/cpm` for faster dependency resolution. You can override this by setting the `CPM_SOURCE_CACHE` environment variable.

**Best Practice:** Set `CPM_SOURCE_CACHE` in presets or via environment variable/command line, not in CMakeLists.txt. Setting it in CMakeLists.txt with `FORCE` can override parent project settings when used as a subdirectory, and prevents users from configuring it themselves.

### Version Management

Version is automatically detected from git tags:
Expand Down
19 changes: 17 additions & 2 deletions cmake/cpp-library-install.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,23 @@ function(_cpp_library_generate_dependencies OUTPUT_VAR TARGET_NAME NAMESPACE)

# Process each linked library
foreach(LIB IN LISTS LINK_LIBS)
# Skip generator expressions (typically BUILD_INTERFACE dependencies)
if(LIB MATCHES "^\\$<")
# Handle BUILD_INTERFACE generator expressions
# When re-exporting dependencies from external packages, they must be wrapped in BUILD_INTERFACE
# to avoid CMake export errors, but we still want to track them for find_dependency()
if(LIB MATCHES "^\\$<BUILD_INTERFACE:([^>]+)>$")
set(EXTRACTED_TARGET "${CMAKE_MATCH_1}")
# Only process if it's a namespaced target (external dependency)
# Non-namespaced targets in BUILD_INTERFACE are local build targets
if(EXTRACTED_TARGET MATCHES "::")
set(LIB "${EXTRACTED_TARGET}")
message(DEBUG "cpp-library: Extracted ${LIB} from BUILD_INTERFACE generator expression")
else()
# Skip non-namespaced BUILD_INTERFACE targets (local build targets)
message(DEBUG "cpp-library: Skipping non-namespaced BUILD_INTERFACE target: ${EXTRACTED_TARGET}")
continue()
endif()
elseif(LIB MATCHES "^\\$<")
# Skip other generator expressions (INSTALL_INTERFACE, etc.)
continue()
endif()

Expand Down
2 changes: 1 addition & 1 deletion templates/.github/workflows/ci.yml.in
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Configure CMake
run: cmake --preset=test
Expand Down
18 changes: 17 additions & 1 deletion templates/CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,30 @@
"CPP_LIBRARY_FORCE_INIT": "ON",
"CPM_SOURCE_CACHE": "${sourceDir}/.cache/cpm"
}
},
{
"name": "install",
"displayName": "Install Configuration",
"description": "Release build for installation with CPM_USE_LOCAL_PACKAGES enabled for testing installed packages",
"binaryDir": "${sourceDir}/build/install",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"BUILD_TESTING": "OFF",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_CXX_EXTENSIONS": "OFF",
"CPM_USE_LOCAL_PACKAGES": "ON",
"CPM_SOURCE_CACHE": "${sourceDir}/.cache/cpm"
}
}
],
"buildPresets": [
{ "name": "default", "displayName": "Default Build", "configurePreset": "default" },
{ "name": "test", "displayName": "Build Tests", "configurePreset": "test" },
{ "name": "docs", "displayName": "Build Docs", "configurePreset": "docs", "targets": "docs" },
{ "name": "clang-tidy", "displayName": "Build with Clang-Tidy", "configurePreset": "clang-tidy" },
{ "name": "init", "displayName": "Initialize Templates", "configurePreset": "init" }
{ "name": "init", "displayName": "Initialize Templates", "configurePreset": "init" },
{ "name": "install", "displayName": "Build for Installation", "configurePreset": "install" }
],
"testPresets": [
{ "name": "test", "displayName": "Run All Tests", "configurePreset": "test", "output": { "outputOnFailure": true } },
Expand Down