Packaging and Installing#
This guide covers how to configure a LIBRA project for installation and
packaging — making your library consumable by other CMake projects via
find_package(), generating distributable packages (.deb, .rpm,
archives), and optionally exposing components so downstream projects can
request only what they need.
All functions described here are only available when
LIBRA_DRIVER is SELF. For the full function reference
see Packaging.
Basic installation#
The minimum setup to make your library installable and usable with
find_package() is two calls in cmake/project-local.cmake:
libra_configure_exports(mylib)
libra_install_target(mylib INCLUDE_DIR include/)
After cmake --build . --target install, downstream projects can use:
find_package(mylib REQUIRED)
target_link_libraries(their_target PRIVATE mylib::mylib)
Required: config.cmake.in template
libra_configure_exports() requires a template at
cmake/config.cmake.in. A minimal template for a library with no
dependencies:
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/mylib-exports.cmake")
check_required_components(mylib)
If your library depends on other packages, add find_dependency() calls
before the include():
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(fmt REQUIRED)
find_dependency(spdlog REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/mylib-exports.cmake")
check_required_components(mylib)
Note
Do not add find_dependency() for header-only dependencies passed
directly to libra_configure_exports() — this causes an
infinite loop when find_package() is called.
Header-only libraries#
For a header-only library there is no compiled target to install:
libra_configure_exports(mylib)
libra_install_headers(${PROJECT_SOURCE_DIR}/include)
The cmake/config.cmake.in for a header-only library sets up the include
path rather than importing exported targets:
@PACKAGE_INIT@
set_and_check(mylib_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")
check_required_components(mylib)
Installing CMake modules#
If your project ships reusable .cmake modules for downstream projects,
install them alongside the config file:
libra_install_cmake_modules(mylib cmake/modules) # whole directory
libra_install_cmake_modules(mylib cmake/MyHelpers.cmake) # individual file
Installed modules land in ${CMAKE_INSTALL_LIBDIR}/cmake/mylib/ and are
accessible to downstream projects after find_package(mylib).
Components#
Components let downstream projects request only a subset of your library:
find_package(mylib REQUIRED COMPONENTS networking serialization)
Choosing a strategy
LIBRA offers libra_add_component_library(), which builds a
separate mylib_<component> library target for each component. Useful when
setting components are large, optional, or have distinct link dependencies;
downstream projects link mylib_networking explicitly.
libra_add_component_library(
TARGET mylib COMPONENT networking
SOURCES ${ALL_SRC} REGEX "src/net/.*\\.cpp")
libra_add_component_library(
TARGET mylib COMPONENT serialization
SOURCES ${ALL_SRC} REGEX "src/serial/.*\\.cpp")
# Downstream:
# find_package(mylib REQUIRED COMPONENTS networking)
# target_link_libraries(their_target PRIVATE mylib_networking)
Wiring up config.cmake.in
Whichever strategy you use, call libra_check_components() at
the end of cmake/config.cmake.in so missing required components produce a
clear error at find_package() time:
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/mylib-exports.cmake")
libra_add_component_library(TARGET mylib COMPONENT networking ...)
libra_add_component_library(TARGET mylib COMPONENT serialization ...)
libra_check_components(mylib)
Generating packages#
LIBRA wraps CPack to generate distributable packages via
cmake --build . --target package. Add this to cmake/project-local.cmake
after your target definitions:
libra_configure_cpack(
"DEB;RPM;TGZ"
"A short one-line summary of mylib"
"Longer description of what mylib does."
"Your Name or Organisation"
"https://example.com/mylib"
"Your Name <you@example.com>")
Then build packages:
cmake --preset release
cmake --build --preset release --target package
Packages land in the build directory. Filename format per generator:
DEB:
<n>_<version>_<arch>.debRPM:
<n>-<version>-<release>.<arch>.rpmTGZ/ZIP/etc.:
<n>-<version>-<arch>.<ext>
Overriding CPack variables
Set any CPACK_* variable before calling libra_configure_cpack()
to override its defaults. The full set of overridable variables:
Variable |
Default |
Notes |
|---|---|---|
|
|
Install prefix inside the package |
|
|
Override to set a fixed filename |
|
|
|
|
|
|
|
|
|
|
Auto-detected from |
Set manually if auto-detection fails |
|
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY /opt/mylib)
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
libra_configure_cpack(...)
License auto-detection reads the first 200 bytes of your LICENSE file
and recognises MIT, Apache, GPL, and BSD. For anything else set
CPACK_RPM_PACKAGE_LICENSE manually or a warning is emitted and
"Unknown" is used.
Note
libra_configure_cpack() is a CMake macro, not a function, so
CPACK_* variables propagate to the calling scope as required by CPack.
Call it from the top level of project-local.cmake, not from inside a
function.
Full example#
A complete cmake/project-local.cmake for a library with components,
installation, and packaging:
# ── Components ─────────────────────────────────────────────────────────────
libra_add_component_library(
TARGET ${PROJECT_NAME} COMPONENT networking
SOURCES ${${PROJECT_NAME}_CXX_SRC}
REGEX "src/net/.*\\.cpp")
libra_add_component_library(
TARGET ${PROJECT_NAME} COMPONENT serialization
SOURCES ${${PROJECT_NAME}_CXX_SRC}
REGEX "src/serial/.*\\.cpp")
# ── Main target ────────────────────────────────────────────────────────────
libra_add_library(${PROJECT_NAME} ${${PROJECT_NAME}_CXX_SRC})
# ── Installation ───────────────────────────────────────────────────────────
libra_configure_exports(${PROJECT_NAME})
libra_install_target(${PROJECT_NAME}
INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
libra_install_cmake_modules(${PROJECT_NAME} cmake/modules)
libra_install_copyright(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/LICENSE)
# ── Packaging ──────────────────────────────────────────────────────────────
libra_configure_cpack(
"DEB;TGZ"
"One-line summary"
"Full description."
"Your Organisation"
"https://example.com/${PROJECT_NAME}"
"maintainer@example.com")