CMake utilities for creating installable packages. Linux, Windows and macOS are supported. CMake installation configuration uses a single function that generates a CMake package with default settings.
# Producer project
target_install_package(my_library)
# Consumer project
find_package(my_library CONFIG REQUIRED)Most use cases require minimal configuration. The goal is to simplify this process while still allowing interleaving CMake installs and configuration.
This project requires some other cmake projects, which have been inlined under the cmake/ folder. You can do the same in your project, but check installation first, or the examples.
- CMake 3.25+ for core utilities and examples
- C++20 modules examples require CMake 3.28+
| File/Function | Type | Description |
|---|---|---|
| target_install_package | Function | Main utility for creating installable packages with automatic CMake config generation |
| target_configure_sources | Function | Configure template files and add them to target's FILE_SET |
| export_cpack | Function | CPack configuration with component detection, platform-appropriate generators, and optional GPG signing (see tutorial) |
| generic-config.cmake.in | Template | Default CMake config template (can be overridden with custom templates) |
| sign_packages.cmake.in | Template | GPG signing template (see tutorial) |
| project_log | Function | Enhanced logging with color support and project context |
| project_include_guard | Macro | Project-level include guard with version checking (guard against submodules/inlining cmake files, protecting previous definitions) |
| list_file_include_guard | Macro | File-level include guard with version checking (guard against submodules/inlining cmake files, protecting previous definitions) |
Note
The target_install_package() function generates CMake package configuration files (<TargetName>Config.cmake and <TargetName>ConfigVersion.cmake) from the template. These files allow other CMake projects to find and use your installed target via find_package(<TargetName>), setting up include directories, link libraries, and version compatibility checks. This makes your project a well-behaved CMake package.
The target_install_package() function now uses a simplified and predictable template selection:
- User-provided
CONFIG_TEMPLATEparameter — absolute or relative path to a CMake config template file. If provided but not found, configuration fails. - Fallback to the built-in generic template:
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/cmake/generic-config.cmake.in(Generic Config Template).
Auto-discovery of export-specific templates (e.g., searching the target or script cmake/ directories for <ExportName>Config.cmake.in) has been removed. Declare CONFIG_TEMPLATE explicitly if you need a custom template; otherwise, the generic template is used.
Note
Config templates use @EXPORT_NAME@ for CMake substitution, which it defaults to ${TARGET_NAME}. This is important to remember when trying to add multiple targets to the same CMake package. To join multiple targets, you just have to share the same EXPORT_NAME.
Note
target_install_package() uses standard CMake installation directories via GNUInstallDirs: executables and DLLs(Windows) go to bin/, libraries to lib/ or lib64, and config files to share/cmake/<package>/. See Default Installation Directories for complete reference.
- Features
- Installation
- Usage
- Component-Based Installation
- Multi-Target Exports
- More Examples
- FILE_SET Features
- Similar projects
- Templated source file configuration with proper include paths
- Package installation with CMake config file generation
- CPack integration with platform-appropriate package generators (TGZ, ZIP, DEB, RPM, WIX)
- CPack signing for all platforms using GPG
- Support for modern CMake including file sets and C++20 modules (CMake 3.25+)
- Component-based installation with runtime/development separation
- Build variant support for debug/release/custom configurations
- Destination paths for headers and configured files
Tip
Use colors and higher log level for more information about what's going on.
cmake .. -DPROJECT_LOG_COLORS=ON --log-level=DEBUGTip
Prefer FILE_SET for Modern CMake
FILE_SET solves key limitations of PUBLIC_HEADER:
- preserves directory structure
- provides integration with IDEs
- allows per-target/file-set header installation instead of installing entire directories/files
- same api for c++20 modules(add TYPE CXX_MODULES too, see module example)
# FILE_SET usage for header install and includes
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/my_library/api.h
)
# Installation - detects all HEADER_SETS (not only HEADERS)
target_install_package(my_library) # Installs all HEADER file setsNote: Using target_configure_sources() with targets that also have PUBLIC_HEADER property will trigger a warning about mixing the FILE_SETS and the PUBLIC_HEADER property.
Tip
Remember you can use CMake's built-in property for position independent code for SHARED libraries. It's the most platform-agnostic way to enable PIC.
set_target_properties(yourTarget PROPERTIES POSITION_INDEPENDENT_CODE ON)See
POSITION_INDEPENDENT_CODEproperty documentation.
Tip
Windows-specific: ensure import library is generated (if you don't have explicit dllimport/export definitions in your code)
if(WIN32)
set_target_properties(yourTarget PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()See
WINDOWS_EXPORT_ALL_SYMBOLSproperty documentation.
For most projects, use FetchContent to automatically download and configure the utilities:
include(FetchContent)
FetchContent_Declare(
target_install_package
GIT_REPOSITORY https://github.com/jkammerland/target_install_package.cmake.git
GIT_TAG v6.1.4
)
FetchContent_MakeAvailable(target_install_package)
# Now you can directly use target_install_package(...)Typically, you would want to wrap this in some if statement, e.g
# add_library(${PROJECT_NAME} ...)
option(${PROJECT_NAME}_INSTALL "Install ${PROJECT_NAME} configuration" OFF)
if(${PROJECT_NAME}_INSTALL)
include(FetchContent)
FetchContent_Declare(
target_install_package
GIT_REPOSITORY https://github.com/jkammerland/target_install_package.cmake.git
GIT_TAG v6.1.4
# Optional arg to first try find_package locally before fetching, see manual installation
# NOTE: This must be called last, with 0 to N args following FIND_PACKAGE_ARGS
# FIND_PACKAGE_ARGS
)
FetchContent_MakeAvailable(target_install_package)
# Install your target
target_install_package(${PROJECT_NAME})
else()
message(STATUS "Enable install of ${PROJECT_NAME} with -D${PROJECT_NAME}_INSTALL=ON")
endif()To prevent your project from unintentionally being installed when used in another project!
For system-wide installation or package manager integration, install the utilities manually:
# Clone the repository
git clone https://github.com/jkammerland/target_install_package.cmake.git
cd target_install_package.cmake
# Configure and install
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build
cmake --install build
# Or install to a custom prefix
cmake --install build --prefix /opt/cmake-utils
# The package is now available for use in your CMake projects. e.g
## 1.
# find_package(target_install_package CONFIG REQUIRED)
## 2.
# find_package(target_install_package CONFIG REQUIRED PATHS /opt/cmake-utils)
## 3.
# set(CMAKE_PREFIX_PATH "/opt/cmake-utils") # Or command line, cmake -DCMAKE_PREFIX_PATH="/opt/cmake-utils" ..
# find_package(target_install_package CONFIG REQUIRED)This project installs itself via the INCLUDE_ON_FIND_PACKAGE option. See the main CMakeLists.txt. An example of a pure cmake package.
Using the manually installed utilities:
# Find the installed package
find_package(target_install_package REQUIRED)
# Now you can use the functions
add_library(my_library STATIC)
target_sources(my_library PRIVATE src/library.cpp)
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES "include/my_library/api.h"
)
# Use the installed utilities
target_install_package(my_library NAMESPACE MyLib::)With custom installation prefix:
# Set CMAKE_PREFIX_PATH to find the utilities
list(APPEND CMAKE_PREFIX_PATH "/opt/cmake-utils")
find_package(target_install_package REQUIRED)
# Or set it via command line
# cmake -DCMAKE_PREFIX_PATH="/opt/cmake-utils" ..The simplest case - a library with headers that is also installable:
# Create a library target
add_library(math_utils STATIC)
target_sources(math_utils PRIVATE src/matrix.cpp src/vector.cpp)
# Declare public headers using FILE_SET (BASE_DIRS automatically become include directories)
target_sources(math_utils PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES
"include/math/matrix.h"
"include/math/vector.h"
"include/math/constants.h"
)
# Install the package
target_install_package(math_utils NAMESPACE Math::)This works with INTERFACE or SHARED library targets. Check the defaults in target_install_package, which are printed when you use --log-level=DEBUG.
What this creates:
- Installs headers to
${CMAKE_INSTALL_INCLUDEDIR}(defined by cross-platform friendly GNUInstallDirs) - Installs library to
${CMAKE_INSTALL_LIBDIR}(GNUInstallDirs) - Creates
math_utils-config.cmakeandmath_utils-config-version.cmake - Consumers can use:
find_package(math_utils REQUIRED)
For libraries that need version information or build-time configuration:
add_library(my_library STATIC)
target_sources(my_library PRIVATE src/library.cpp)
# Regular headers
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES "include/my_library/api.h"
)
# Configure template files for version info (also uses FILE_SET automatically)
target_configure_sources(my_library
PUBLIC
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/include/my_library
FILE_SET HEADERS
BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include
FILES include/my_library/version.h.in
)
target_install_package(my_library NAMESPACE MyLib::)Template file example (include/my_library/version.h.in):
#pragma once
#define MY_LIBRARY_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define MY_LIBRARY_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define MY_LIBRARY_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define MY_LIBRARY_VERSION "@PROJECT_VERSION@"For libraries that depend on other packages:
add_library(graphics_lib SHARED)
target_sources(graphics_lib PRIVATE src/renderer.cpp src/shader.cpp)
# Collect headers using globbing
file(GLOB_RECURSE GRAPHICS_HEADERS "include/graphics/*.h")
target_sources(graphics_lib PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES ${GRAPHICS_HEADERS}
)
# Link to dependencies during build
find_package(OpenGL REQUIRED)
find_package(glfw3 REQUIRED)
target_link_libraries(graphics_lib PUBLIC OpenGL::GL glfw)
# Install with dependencies - consumers will automatically get OpenGL and glfw3
target_install_package(graphics_lib
NAMESPACE Graphics::
PUBLIC_DEPENDENCIES "OpenGL REQUIRED" "glfw3 3.3 REQUIRED"
)Consumer usage:
find_package(graphics_lib REQUIRED)
target_link_libraries(my_app PRIVATE Graphics::graphics_lib)
# OpenGL and glfw3 are automatically found and linkedGenerate distributable packages (TGZ, ZIP, DEB, RPM, WIX) with component separation:
add_library(my_library SHARED)
target_sources(my_library PRIVATE src/library.cpp)
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES "include/my_library/api.h"
)
# Install with default components (Runtime and Development)
target_install_package(my_library
NAMESPACE MyLib::
)
# Auto-configure CPack
export_cpack(
PACKAGE_NAME "MyLibrary"
PACKAGE_VENDOR "Acme Corp"
# AUTO-DETECTED: Components (Runtime, Development)
# AUTO-DETECTED: Generators (TGZ, DEB, RPM on Linux; TGZ, ZIP, WIX on Windows)
# AUTO-DETECTED: Architecture (amd64, i386, arm64, etc.)
)
# No need for include(CPack) - export_cpack() does it automaticallyGenerate packages:
cmake --build .
cpack # Generates: MyLibrary-1.0.0-Linux-Runtime.tar.gz, MyLibrary-1.0.0-Linux-Development.tar.gz, etc.See examples/cpack-basic for a complete working example.
For container packaging using CPack’s External generator (scratch images via podman/docker), see docs/Container-Packaging.md.
đź“– For a comprehensive comparison with manual CPack setup and advanced usage patterns, see the CPack Integration Tutorial.
You can mix these utilities with standard CMake install commands for additional files:
add_library(game_engine SHARED)
target_sources(game_engine PRIVATE src/engine.cpp)
target_sources(game_engine PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES "include/engine/engine.h"
)
# Install the main package with default components
target_install_package(game_engine
NAMESPACE Engine::
)
# Install additional documentation with standard install()
install(FILES
"docs/API.md"
"docs/tutorial.md"
DESTINATION "${CMAKE_INSTALL_DOCDIR}"
COMPONENT "Documentation"
)
# Install example configs
install(DIRECTORY "configs/"
DESTINATION "${CMAKE_INSTALL_DATADIR}/game_engine/configs"
COMPONENT "Runtime"
)
# Install development tools
add_executable(asset_converter tools/asset_converter.cpp)
install(TARGETS asset_converter
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
COMPONENT "Tools"
)Installation commands:
# Install only runtime (engine + configs)
cmake --install . --component Runtime
# Install everything for developers (specify each logical component explicitly)
cmake --install . --component Runtime --component Development --component Tools
# Install full package without selecting components (installs every runtime + development file)
cmake --install .
# Install documentation separately
cmake --install . --component Documentationtarget_install_package supports logical component grouping using the Component Prefix Pattern (v6.0+), providing predictable component naming and clean installation control.
The Component Prefix Pattern creates logical component groups with predictable naming:
- Without COMPONENT: Creates
RuntimeandDevelopmentcomponents (traditional) - With COMPONENT: Creates
{COMPONENT}(runtime) and{COMPONENT}_Development(development) components
add_library(my_library SHARED)
target_sources(my_library PRIVATE src/library.cpp)
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES "include/my_library/api.h"
)
# Traditional: Creates Runtime and Development components
target_install_package(my_library)
# Logical grouping: Creates Core (runtime) and Core_Development (development) components
target_install_package(my_library COMPONENT Core)Multiple targets can share the same logical component group by using the same COMPONENT name:
# Create related targets for a game engine
add_library(engine_core SHARED)
add_library(engine_tools STATIC)
add_executable(level_editor)
# Configure targets...
target_sources(engine_core PUBLIC FILE_SET HEADERS BASE_DIRS include FILES include/engine/core.h)
target_sources(engine_tools PUBLIC FILE_SET HEADERS BASE_DIRS include FILES include/engine/tools.h)
# Logical grouping with shared export
target_install_package(engine_core
EXPORT_NAME "GameEngine"
NAMESPACE Engine::
COMPONENT Core) # Creates: Core (runtime), Core_Development (development)
target_install_package(engine_tools
EXPORT_NAME "GameEngine"
NAMESPACE Engine::
COMPONENT Core) # Shares Core logical group with engine_core
target_install_package(level_editor
EXPORT_NAME "GameEngine"
NAMESPACE Engine::
COMPONENT Tools) # Creates: Tools (runtime), Tools_Development (development)Result: Single GameEngine package with logical component groups:
- Core:
libengine_core.so(runtime) - Core_Development: Headers from both targets +
libengine_tools.a+ shared CMake config files - Tools:
level_editorexecutable (runtime) - Tools_Development: Headers for tools + development-only assets
# Install Core logical group - runtime only (deployment)
cmake --install . --component Core
# Install Core logical group - development files
cmake --install . --component Core_Development
# Install Tools logical group - runtime only
cmake --install . --component Tools
# Shared CMake config files live with the first development component
cmake --install . --component Core_Development
# Install all runtime + development files (no component filtering)
cmake --install .
# Install everything for developers
cmake --install . --component Core --component Core_Development
cmake --install . --component Tools --component Tools_Development
# Install everything
cmake --install .For projects with multiple related targets that should be packaged together, call target_install_package() multiple times with the same EXPORT_NAME:
- Shared Package Name: Multiple targets that should be found with one
find_package()call - Aggregated Dependencies: Different targets with different dependencies that should be combined
- Component Organization: Different targets with different component assignments
Note
Single target packages have less complexity. Multi-target exports are useful when packaging a set of static libraries where one static library forwards others via public linking.
# Core library
add_library(myproject_core STATIC)
target_sources(myproject_core PRIVATE src/core.cpp)
target_sources(myproject_core PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/myproject/core.h
)
# Utilities library
add_library(myproject_utils STATIC)
target_sources(myproject_utils PRIVATE src/utils.cpp)
target_sources(myproject_utils PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/myproject/utils.h
)
# Command line tool
add_executable(myproject_cli)
target_sources(myproject_cli PRIVATE src/cli.cpp)
# Package all targets with logical component grouping
target_install_package(myproject_core
EXPORT_NAME "myproject"
NAMESPACE MyProject::
COMPONENT Core # Creates: Core (runtime), Core_Development (development)
PUBLIC_DEPENDENCIES "fmt 11.1.4 REQUIRED" # Shared by all targets
)
target_install_package(myproject_utils
EXPORT_NAME "myproject"
NAMESPACE MyProject::
COMPONENT Core # Shares Core logical group
PUBLIC_DEPENDENCIES "spdlog 1.15.3 REQUIRED" # Additional dependency
)
target_install_package(myproject_cli
EXPORT_NAME "myproject"
NAMESPACE MyProject::
COMPONENT Tools # Creates: Tools (runtime), Tools_Development (development)
)Result: Single package with logical component groups:
- Core: Static libraries (
libmyproject_core.a,libmyproject_utils.a) (runtime) - Core_Development: Headers from both Core libraries
- Tools: CLI executable (
myproject_cli) (runtime) - Development: Shared CMake config files
Consumer usage:
# One find_package call gets all targets and dependencies (fmt + spdlog)
find_package(myproject REQUIRED)
target_link_libraries(my_app PRIVATE
MyProject::myproject_core
MyProject::myproject_utils
)
# fmt and spdlog are automatically found and linkedFor libraries with optional features that have different dependencies based on requested components:
# Graphics library
add_library(engine_graphics STATIC)
target_sources(engine_graphics PRIVATE src/graphics.cpp)
target_sources(engine_graphics PUBLIC
FILE_SET HEADERS BASE_DIRS include
FILES include/engine/graphics.h
)
# Audio library
add_library(engine_audio STATIC)
target_sources(engine_audio PRIVATE src/audio.cpp)
target_sources(engine_audio PUBLIC
FILE_SET HEADERS BASE_DIRS include
FILES include/engine/audio.h
)
# Networking library
add_library(engine_network STATIC)
target_sources(engine_network PRIVATE src/network.cpp)
target_sources(engine_network PUBLIC
FILE_SET HEADERS BASE_DIRS include
FILES include/engine/network.h
)
# Package with component-dependent dependencies (automatic finalization)
target_install_package(engine_graphics
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
PUBLIC_DEPENDENCIES "fmt 11.1.4 REQUIRED" # Always loaded
COMPONENT_DEPENDENCIES
"graphics" "OpenGL 4.5 REQUIRED;glfw3 3.3 REQUIRED" # Only when graphics requested
)
target_install_package(engine_audio
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
COMPONENT_DEPENDENCIES
"audio" "portaudio 19.7 REQUIRED" # Only when audio requested
)
target_install_package(engine_network
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
COMPONENT_DEPENDENCIES
"networking" "Boost 1.79 REQUIRED COMPONENTS system network"
)Consumer usage with selective dependencies:
# Only loads fmt (package global dependency)
find_package(game_engine REQUIRED)
# Loads fmt + OpenGL + glfw3
find_package(game_engine REQUIRED COMPONENTS graphics)
# Loads fmt + OpenGL + glfw3 + portaudio
find_package(game_engine REQUIRED COMPONENTS graphics audio)
# Loads all dependencies
find_package(game_engine REQUIRED COMPONENTS graphics audio networking)
target_link_libraries(my_game PRIVATE
GameEngine::engine_graphics
GameEngine::engine_audio
)Note:
- You can list multiple dependencies for a component using a semicolon-separated list inside quotes (as in the examples). CMake splits that list internally; the package generator reconstructs and preserves the full set.
- You may add
COMPONENT_DEPENDENCIESacross multipletarget_install_package()calls that share the sameEXPORT_NAME. Dependencies are merged and de-duplicated per component.
Example showing a modular game engine with optional components:
# Core engine (always required)
add_library(engine_core SHARED)
target_sources(engine_core PRIVATE
src/core/engine.cpp
src/core/entity.cpp
src/core/component.cpp
)
target_sources(engine_core PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES
include/engine/core/engine.h
include/engine/core/entity.h
include/engine/core/component.h
)
# Graphics module (optional)
add_library(engine_graphics SHARED)
target_sources(engine_graphics PRIVATE src/graphics/renderer.cpp)
target_sources(engine_graphics PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/engine/graphics/renderer.h
)
target_link_libraries(engine_graphics PUBLIC engine_core)
# Physics module (optional)
add_library(engine_physics SHARED)
target_sources(engine_physics PRIVATE src/physics/world.cpp)
target_sources(engine_physics PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/engine/physics/world.h
)
target_link_libraries(engine_physics PUBLIC engine_core)
# Level editor tool
add_executable(level_editor tools/level_editor.cpp)
target_link_libraries(level_editor PRIVATE engine_core engine_graphics)
# Package everything with component-based dependencies
target_install_package(engine_core
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
PUBLIC_DEPENDENCIES "fmt 11.1.4 REQUIRED"
# Uses default Runtime and Development components
)
target_install_package(engine_graphics
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
COMPONENT_DEPENDENCIES
"graphics" "OpenGL 4.5 REQUIRED;glfw3 3.3 REQUIRED"
COMPONENT "Graphics" # Creates Graphics and Graphics_Development
)
target_install_package(engine_physics
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
COMPONENT_DEPENDENCIES
"physics" "Bullet3 3.24 REQUIRED"
COMPONENT "Physics" # Creates Physics and Physics_Development
)
target_install_package(level_editor
EXPORT_NAME "game_engine"
NAMESPACE GameEngine::
COMPONENT "Tools"
)
# Install additional assets using standard install()
install(DIRECTORY "assets/shaders/"
DESTINATION "${CMAKE_INSTALL_DATADIR}/game_engine/shaders"
COMPONENT "Graphics" # Matches the Graphics runtime component
)
install(FILES "configs/physics.json"
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/game_engine"
COMPONENT "Physics" # Matches the Physics runtime component
)Flexible installation:
# Minimal runtime (just core engine)
cmake --install . --component Runtime
# Graphics-enabled runtime
cmake --install . --component Runtime --component Graphics
# Full game development environment with all runtime components
cmake --install . --component Runtime --component Graphics --component Physics --component Tools
# Development files for Graphics and Physics
cmake --install . --component Development --component Graphics_Development --component Physics_Development
# Install every component at once (no explicit selection)
cmake --install .For projects that need different build configurations:
# Set up variant suffix based on build type or custom options
set(VARIANT_SUFFIX "")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(VARIANT_SUFFIX "-debug")
set(CONFIG_SUFFIX "_DEBUG")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(VARIANT_SUFFIX "-relwithdebinfo")
set(CONFIG_SUFFIX "_RELWITHDEBINFO")
endif()
# Custom variant support
option(BUILD_WITH_PROFILING "Enable profiling support" OFF)
if(BUILD_WITH_PROFILING)
set(VARIANT_SUFFIX "${VARIANT_SUFFIX}-profiling")
set(CONFIG_SUFFIX "${CONFIG_SUFFIX}_PROFILING")
endif()
add_library(my_library STATIC)
target_sources(my_library PRIVATE src/library.cpp)
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS include
FILES include/my_library/api.h
)
# Configure variant-specific header
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/include/my_library/build_config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/my_library/build_config.h"
@ONLY
)
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/include"
FILES "${CMAKE_CURRENT_BINARY_DIR}/include/my_library/build_config.h"
)
# Variant-specific compilation
if(BUILD_WITH_PROFILING)
target_compile_definitions(my_library PUBLIC MY_LIBRARY_PROFILING=1)
target_link_libraries(my_library PRIVATE profiler_lib)
endif()
# Install with variant suffix
target_install_package(my_library
NAMESPACE MyLib::
CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/my_library${VARIANT_SUFFIX}"
INCLUDE_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/my_library${VARIANT_SUFFIX}"
)Build and install variants:
# Standard release build
cmake -B build-release -DCMAKE_BUILD_TYPE=Release
cmake --build build-release
cmake --install build-release --prefix /usr/local
# Debug build with profiling
cmake -B build-debug -DCMAKE_BUILD_TYPE=Debug -DBUILD_WITH_PROFILING=ON
cmake --build build-debug
cmake --install build-debug --prefix /usr/localConsumer usage:
# Use specific variant
find_package(my_library-debug-profiling REQUIRED)
target_link_libraries(my_app PRIVATE MyLib::my_library)For modern header-only libraries with dependencies:
add_library(math_header_lib INTERFACE)
# Collect all headers
file(GLOB_RECURSE MATH_HEADERS "include/math/*.hpp")
target_sources(math_header_lib INTERFACE
FILE_SET HEADERS
BASE_DIRS "include"
FILES ${MATH_HEADERS}
)
# Header-only libraries can still have dependencies
find_package(Eigen3 REQUIRED)
target_link_libraries(math_header_lib INTERFACE Eigen3::Eigen)
# Configure version header
target_configure_sources(math_header_lib
INTERFACE
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/include/math
FILE_SET HEADERS
BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include
FILES include/math/version.hpp.in
)
target_install_package(math_header_lib
NAMESPACE MathLib::
PUBLIC_DEPENDENCIES "Eigen3 3.4 REQUIRED"
# Additional CMake utilities for consumers
INCLUDE_ON_FIND_PACKAGE
cmake/MathLibHelpers.cmake
cmake/CompilerWarnings.cmake
)- Headers are installed by
target_install_package - BASE_DIRS become include directories
- CMake correctly tracks header file dependencies
- Headers are properly propagated to consuming targets
- Integration with IDEs for header file management
- Separation between runtime and development files
- Works alongside standard CMake install() commands
Manual approach:
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
target_include_directories(my_library PUBLIC
$<BUILD_INTERFACE:include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
install(TARGETS my_library
EXPORT my_library
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(EXPORT my_library
FILE my_library.cmake
NAMESPACE MyLib::
DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/my_library
)
# ... more boilerplate for config filesModern approach (with target_install_package):
target_sources(my_library PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES ${HEADER_FILES}
)
target_install_package(my_library NAMESPACE MyLib::)
# That's it - include directories, installation, and config files are handledThe FILE_SET approach combined with target_install_package reduces boilerplate while allowing standard install() commands where needed.