project-local.cmake: How To Hook Into LIBRA#

cmake/project-local.cmake is the file where you define your project’s targets, configure LIBRA features, and hook into LIBRA’s build machinery. You can put standard CMake in this file alongside LIBRA-specific calls.

For an introduction to what this file should contain and a minimal working example, see project-local.cmake. This page covers the full reference: target declaration wrappers, all available variables, configure-time utilities, and installation/deployment helpers.

Note

All cmake functions which LIBRA exposes are prefixed with libra_; anything else should be considered non-API and may change at any time.

Target Declaration Wrappers#

libra_add_library#

Register a library target.

Thin wrapper around add_library() which forwards all arguments to the built in function, and adds the target name to the list of targets to apply the LIBRA magic to.

libra_add_executable#

Register an executable target.

Thin wrapper around add_executable() which forwards all arguments to the built in function, and adds the target name to the list of targets to apply the LIBRA magic to.

Variables#

The variables listed in this section are generally for configuring various LIBRA features on a per-project basis, and are stable for the duration of the project. However, they are NOT defined as cache variables because (a) they don’t need to be, and (b) so the user doesn’t need to remember to set(VAR "value" CACHE FORCE) them instead of just set(VAR "value") them.

Note

Many of the cmdline interface variables detailed in Variable reference can be set permanently in project-local.cmake too, but not all of them. Exceptions are:

If you do set any, you will need to add CACHE FORCE when setting or things may break in subtle ways.

General#

LIBRA_C_DIAG_CANDIDATES#

The list of compiler warning options you want to pass to the C compiler. This can be a superset of the options supported by the minimum C compiler version you target; each option in the list is checked to see if the current C compiler supports it. If not defined, uses LIBRA’s internal C diagnostic option set, which is fairly comprehensive. If you don’t want to compile with any warnings, set this to "".

Added in version 0.8.6.

LIBRA_CXX_DIAG_CANDIDATES#

The list of compiler warning options you want to pass to the compiler. This can be a superset of the options supported by the minimum compiler version you target; each option in the list is checked to see if the current CXX compiler supports it. If not defined, uses LIBRA’s internal CXX diagnostic option set, which is fairly comprehensive. If you don’t want to compile with any warnings, set this to "".

Added in version 0.8.6.

Source Discovery#

${PROJECT_NAME}_C_SRC#

Glob containing all C source files.

${PROJECT_NAME}_CXX_SRC#

Glob containing all C++ source files.

${PROJECT_NAME}_C_HEADERS#

Glob containing all C header files.

${PROJECT_NAME}_CXX_HEADERS#

Glob containing all C++ header files.

Note

See Using cmake Globbing for rationale on why globs are used, contrary to common cmake guidance.

Analysis#

LIBRA_CPPCHECK_IGNORES#

A list of files to totally ignore when running cppcheck. Only used if LIBRA_ANALYSIS is enabled and cppcheck is found. The -i separators are added by LIBRA–this should just be a raw list.

Added in version 0.8.5.

LIBRA_CPPCHECK_SUPPRESSIONS#

A list of categories of warnings to suppress for matching patterns cppcheck. Only used if LIBRA_ANALYSIS is enabled and cppcheck is found. The --suppress= separators are added by LIBRA–this should just be a raw list.

Added in version 0.8.5.

LIBRA_CPPCHECK_EXTRA_ARGS#

A list of extra arguments to pass to cppcheck. If you want to pass suppressions or ignores, use the above variables; this is for other things which don’t fit in those buckets. Passed as-is to cppcheck.

Added in version 0.8.5.

LIBRA_CLANG_FORMAT_FILEPATH#

The path to the .clang-format file you want to use. If not defined, LIBRA will use its internal .clang-format file.

Added in version 0.8.8.

LIBRA_CLANG_TIDY_FILEPATH#

The path to the .clang-tidy file you want to use. If not defined, LIBRA will use its internal .clang-tidy file.

Added in version 0.8.8.

LIBRA_CLANG_TIDY_CHECKS_CONFIG#

Any additional things to pass to --checks. If non empty, must start with ,. Useful to disable certain checks within each category of checks that LIBRA creates targets for.

Added in version 0.8.15.

LIBRA_CLANG_TIDY_EXTRA_ARGS#

Additional flags appended verbatim to every clang-tidy invocation. Useful for flags that LIBRA does not otherwise expose, such as --allow-enabling-analyzer-alpha-checkers. Passed as-is; no separators are added.

Added in version 0.8.15.

Testing#

LIBRA_TEST_HARNESS_LIBS#

Defines the link libraries that all tests/test harnesses need to link with, if any. Goes hand in hand with LIBRA_TEST_HARNESS_PACKAGES. Does not apply to interpreted tests.

LIBRA_TEST_HARNESS_PACKAGES#

Defines the packages that contain the libraries that all tests/test harnesses need to link with, if any. Goes hand in hand with LIBRA_TEST_HARNESS_LIBS. Does not apply to interpreted tests.

LIBRA_UNIT_TEST_MATCHER#

The common suffix before the .cpp that all unit tests under tests/ will have so LIBRA can glob them. If not specified, defaults to -utest; a valid unit test would then be, e.g., tests/myclass-utest.cpp.

LIBRA_INTEGRATION_TEST_MATCHER#

The common suffix before the .cpp that all integration tests under tests/ will have so LIBRA can glob them. If not specified, defaults to -itest; a valid integration test would then be, e.g., tests/thing-itest.cpp.

LIBRA_REGRESSION_TEST_MATCHER#

The common suffix before the .cpp that all regression tests under tests/ will have so LIBRA can glob them. If not specified, defaults to -rtest; a valid integration test would then be, e.g., tests/thing-rtest.cpp.

LIBRA_NEGATIVE_TEST_INCLUDE_DIRS#

Knob for additional include directories that need to be passed to negative compile tests. -I is added to each directory by LIBRA. Because these tests do not depend on the main target, we can only extract the dirs from the main target itself, not from its transitive dependencies. This is a limitation of CMake.

LIBRA_NEGATIVE_TEST_COMPILE_FLAGS#

Knob for additional compile flags that need to be passed to negative compile tests. Because these tests do not depend on the main target, we can only extract the flags, definitions, etc. from the main target itself, not from its transitive dependencies. This is a limitation of CMake.

LIBRA_TEST_HARNESS_MATCHER#

The common suffix before the {.cpp,.hpp} that all test harness files tests under tests/ will have so LIBRA can glob them. If not specified, defaults to _test; valid test harness would then be, e.g., tests/thing_test{.cpp,.hpp}. Does not apply to interpreted tests.

Configure-time Utilities#

LIBRA provides a number of functions/macros to simplify the complexity of cmake, and answer questions such as “am I really building/running what I think I am?”. Some useful functions available in project-local.cmake are:

libra_require_compiler#

Enforce a minimum major version for a given compiler and language. Can be called multiple times to enforce requirements for different compilers or languages independently.

Signature:

libra_require_compiler(
    [LANG  <C|CXX> ...]   # Languages to check. Defaults to both C and CXX.
    ID      <compiler-id> # Compiler ID: GNU, Clang, AppleClang, IntelLLVM
    VERSION <major>       # Minimum required major version (integer)
)
Param LANG:

Accepts one or more languages. If omitted, both C and CXX are checked. Languages not enabled in the project are silently skipped.

Param ID:

The ID of the compiler to check. If the active compiler ID does not match ID, the check is silently skipped. This allows calling libra_require_compiler once per supported compiler without needing if() guards around each call. Basically, if and only if the active compiler ID matches the argument is the version checked.

Param VERSION:

Compiler major version to check against. If the active compiler ID matches and its major version is less than this, a fatal error is issued immediately.

Examples:

# Require GCC >= 13 for both C and C++
libra_require_compiler(ID GNU VERSION 13)

# Require Clang >= 17 for C++ only
libra_require_compiler(LANG CXX ID Clang VERSION 17)

# Require GCC >= 13 for C, IntelLLVM >= 2024 for C++
libra_require_compiler(LANG C   ID GNU       VERSION 13)
libra_require_compiler(LANG CXX ID IntelLLVM VERSION 2024)

Fatal error format:

[LIBRA] C compiler version requirement not met:
  Required: GNU >= 13
  Found:    GNU 12.3.1
libra_config_summary_row#

Add a custom row to the LIBRA configuration summary feature table.

Intended for use in project-local.cmake to extend the LIBRA summary with project-specific configuration options, displayed in the same style and column alignment as built-in LIBRA rows.

Must be called after libra_config_summary() has been called (or from within a project-local.cmake that is included before LIBRA emits the summary), so that libra_config_summary_prepare_fields() has already run and the EMIT_ variable for the status field is populated.

Signature:

libra_config_summary_row(
    LABEL    <string>
    STATUS   <variable-name>
    VARIABLE <string>
)
Param LABEL:

Feature description shown in column 1. Will be truncated/padded to the column width.

Param STATUS:

Name of an EMIT_<X> variable (prepared via libra_config_summary_prepare_fields()) whose value is shown in column 2.

Param VARIABLE:

Variable name shown in column 3, e.g. [MY_OPTION]. Pass "" to leave blank.

Example:

set(my_fields MY_BACKEND MY_FEATURE_X)
libra_config_summary_prepare_fields("${my_fields}")

libra_config_summary()

libra_config_summary_row(
    LABEL    "Backend type........................."
    STATUS   EMIT_MY_BACKEND
    VARIABLE "[MY_BACKEND]")

libra_config_summary_row(
    LABEL    "Enable feature X....................."
    STATUS   EMIT_MY_FEATURE_X
    VARIABLE "[MY_FEATURE_X]")

Notes:

  • LABEL should use trailing . characters to reach the column width (_LIBRA_SUMMARY_COL_FEATURE = 37), matching the style of built-in rows. Shorter labels are right-padded with spaces automatically; longer labels are truncated to fit.

  • STATUS is the name of a variable, not its value — pass EMIT_MY_VAR, not ${EMIT_MY_VAR}.

  • Call libra_config_summary_prepare_fields() on your custom fields before calling this function so the EMIT_ variables exist and are colorized.

See Also:

libra_config_summary#

Print a summary of the current LIBRA configuration to the terminal during cmake configure. Displays the feature table (table 1) only: all LIBRA options with their current values and controlling variable names.

For additional information available after configure:

  • make help-targets — shows all LIBRA make targets with YES/NO availability status and the reason each unavailable target is disabled.

  • make help-vars — shows all enumerated LIBRA option variables with their valid values.

Note

This function only displays the summary once per configure run.

See Also:

libra_configure_source_file#

Populate a source file template with build and git information.

Use build information from LIBRA and your project to populate a source file template. LIBRA automatically adds the generated file to the list of files for the main PROJECT_NAME target. This is useful for printing information when your library loads or application starts as a sanity check during debugging to help ensure that you are running what you think you are. Must be called after the PROJECT_NAME target is defined.

Param TARGET:

The target the the configured source file should be added to.

Param INFILE:

The input template file. Should contain CMake variable references like @LIBRA_GIT_REV@ that will be replaced with actual values.

Param OUTFILE:

The output file path where the configured file will be written.

Available Variables for INFILE Template:

  • LIBRA_GIT_REV - Git SHA of the current tip. Result of git log --pretty=format:%H -n 1.

  • LIBRA_GIT_DIFF - Indicates if the build is “dirty” (contains local changes not in git). Result of git diff --quiet --exit-code || echo +. Will be + if dirty, empty otherwise.

  • LIBRA_GIT_TAG - The current git tag for the git rev, if any. Result of git describe --exact-match --tags.

  • LIBRA_GIT_BRANCH - The current git branch, if any. Result of git rev-parse --abbrev-ref HEAD.

  • LIBRA_TARGET_FLAGS_COMPILE - The configured compiler flags relevant for building (excludes diagnostic flags like -W).

  • LIBRA_TARGET_FLAGS_LINK - The configured linker flags relevant for building (excludes diagnostic flags like -W). Note that IPO related flags for GCC/clang do not appear here, because CMake relies on the compiler driver to inject those into actual compiler commands during the final link if the compiler sees that IPO is active at compile time. This is not true for the Intel compilers.

You can also use any standard CMake variables (e.g., CMAKE_C_FLAGS_RELEASE, PROJECT_VERSION, CMAKE_BUILD_TYPE, etc.). Note that if you consume LIBRA in a chained fashion via CPM (i.e., have multiple local repos which are built using LIBRA), then the version info will correspond to the top-level repo for all sub-repos, since when they are poplated into the build/ directory in the root repo, they are not git repos, and so versioning info can’t be extracted.

Example:

# In CMakeLists.txt
set(MY_SOURCES src/main.cpp src/foo.cpp)

libra_add_executable(${PROJECT_NAME} ${MY_SOURCES})

libra_configure_source_file(
  ${PROJECT_NAME}
  ${PROJECT_SOURCE_DIR}/src/version.cpp.in
  ${CMAKE_BINARY_DIR}/version.cpp)
// In src/version.cpp.in
#include <iostream>

void print_version() {
  std::cout << "Git Rev: @LIBRA_GIT_REV@@LIBRA_GIT_DIFF@" << std::endl;
  std::cout << "Branch: @LIBRA_GIT_BRANCH@" << std::endl;
  std::cout << "Tag: @LIBRA_GIT_TAG@" << std::endl;
  std::cout << "Build Type: @CMAKE_BUILD_TYPE@" << std::endl;
}

Note

If your code is not in a git repository, all git-related fields will be stubbed out with N/A and will not be very useful. A warning will be emitted during configuration.

Installation#

All functions in this section are only available if LIBRA_DRIVER is SELF.

libra_configure_exports#

Configure the exports for a TARGET to be installed at CMAKE_INSTALL_PREFIX.

Enables the installed project to be used with find_package() by downstream projects. This function requires a cmake/config.cmake.in template file in your project root.

Param TARGET:

The target name for which to configure exports. This will be used to generate <TARGET>-config.cmake and must match the name used in find_package(). You may need to call this on header-only dependencies to get them into the export set for your project. If you do, make sure you do not add said dependencies to your config.cmake.in file via find_dependency(), as that will cause an infinite loop.

Requirements:

The function expects a template file at ${PROJECT_SOURCE_DIR}/cmake/config.cmake.in. This template is processed by configure_package_config_file() to generate the final config file that defines everything necessary to use the project with find_package().

Example:

libra_configure_exports(mylib)
libra_register_extra_configs_for_install#

Register extra .cmake files for a TARGET to be installed at ${CMAKE_INSTALL_PREFIX}.

Configure additional .cmake files/directories for export. Useful if your project provides reusable CMake functionality that you want downstream projects to access. Supports both individual files and directories (processed recursively).

Param TARGET:

The target name (used for install destination). Must be a target for which libra_configure_exports() has already been called.

Param FILES_OR_DIRS:

One or more .cmake files or directories containing .cmake files. Directories are searched recursively, and the directory structure is preserved during installation (not flattened).

Examples:

# Install individual files
libra_register_extra_configs_for_install(mylib
  cmake/MyLibHelpers.cmake
  cmake/MyLibUtils.cmake)

# Install entire directory (recursive, structure preserved)
libra_register_extra_configs_for_install(mylib
  cmake/modules)

# Mix files and directories
libra_register_extra_configs_for_install(mylib
  cmake/special.cmake
  cmake/modules)

Changed in version 0.9.26: Can now handle files OR directories of extra configs.

Register a copyright notice file to be installed at CMAKE_INSTALL_DOCDIR.

The file is automatically renamed to copyright during installation, which is the standard name expected by Debian package tools (lintian). This function is useful when configuring CPack to generate .deb/.rpm packages.

Param TARGET:

The target name (used for the installation directory path).

Param FILE:

Path to the copyright file (typically LICENSE, COPYING, etc.). Can be any filename; it will be renamed to copyright during installation.

Installation Path:

The file is installed to: ${CMAKE_INSTALL_DATAROOTDIR}/doc/${TARGET}/copyright

Example:

libra_register_copyright_for_install(mylib ${PROJECT_SOURCE_DIR}/LICENSE)
libra_register_headers_for_install#

Register header files from a DIRECTORY to be installed at ${CMAKE_INSTALL_PREFIX}.

Recursively finds and installs all .hpp and .h files from the specified directory, preserving the directory structure. These can be from your project, a header-only dependency, etc.

Param DIRECTORY:

The directory containing header files to install. Searched recursively for .hpp and .h files.

Example:

# Install headers from include/ to ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}
libra_register_headers_for_install(
  ${PROJECT_SOURCE_DIR}/include
  )

# This installs: include/mylib/foo.hpp -> ${CMAKE_INSTALL_PREFIX}/include/mylib/foo.hpp
libra_register_target_for_install#

Register a TARGET for installation with proper export configuration.

Installs the target’s library files (.so, .a) and public headers, and creates an export file (<TARGET>-exports.cmake) that allows downstream projects to use the target with find_package(). The target is associated with the necessary exports file so child projects can find it.

Param TARGET:

The CMake target to install. Must be a valid library target created with add_library() or add_executable(). Must be a target for which libra_configure_exports() has already been called.

The target is installed with:

  • Libraries: ${CMAKE_INSTALL_LIBDIR}

  • Public headers: ${CMAKE_INSTALL_INCLUDEDIR}

  • Export file: ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET}/${TARGET}-exports.cmake

What Gets Installed:

  • Shared libraries (.so, .dylib, .dll)

  • Static libraries (.a, .lib)

  • Executables (if applicable)

  • Public headers (as specified by target properties)

  • CMake export file for use with find_package()

Example:

add_library(mylib src/mylib.cpp)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER "include/mylib.hpp")

libra_register_target_for_install(mylib)

# Downstream projects can now use:
# find_package(mylib REQUIRED)
# target_link_libraries(their_target mylib::mylib)

Deployment#

All functions in this section are only available if LIBRA_DRIVER is SELF.

libra_configure_cpack#

Configure CPack to generate packages via make package.

Supports multiple package formats including Debian archives (.deb), RPM packages (.rpm), and various compressed archives (tar.gz, zip, etc.). Automatically detects license type, configures package metadata, and sets up format-specific options.

This is a MACRO (not function) intentionally so that all CPACK_* variables propagate to parent scope as required by CPack.

libra_configure_cpack(<GENERATORS>
                     <SUMMARY>
                     <DESCRIPTION>
                     <VENDOR>
                     <HOMEPAGE>
                     <CONTACT>)
Param GENERATORS:

One or more CPack generators, separated by semicolons. Valid options are:

  • DEB - Debian archive. Packages are set to always install into /usr unless CPACK_PACKAGE_INSTALL_DIRECTORY is set prior to calling this function.

  • RPM - RPM archive. Packages are set to always install into /usr unless CPACK_PACKAGE_INSTALL_DIRECTORY is set prior to calling this function.

  • TGZ - Compressed tar archive (.tar.gz)

  • ZIP - ZIP archive

  • STGZ - Self-extracting tar.gz archive

  • TBZ2 - Compressed tar archive (.tar.bz2)

  • TXZ - Compressed tar archive (.tar.xz)

Multiple generators can be specified, e.g., "DEB;RPM;TGZ".

Param SUMMARY:

One line package summary.

Param DESCRIPTION:

Detailed package description.

Param VENDOR:

Package vendor/maintainer organization.

Param HOMEPAGE:

Project home page URL.

Param CONTACT:

Project contact. Email address for DEB packages, name for RPM packages.

Automatic Features:

  • License type detection from LICENSE file (supports MIT, Apache, GPL, BSD)

  • Version information from PROJECT_VERSION_* variables

  • Automatic dependency detection (SHLIBDEPS for DEB, AUTOREQ/AUTOPROV for RPM)

  • Standard directory permissions to satisfy lintian/rpmlint

  • Architecture detection

Respects Pre-set Variables:

  • CPACK_PACKAGE_FILE_NAME - If set, used as-is. Otherwise defaults to ${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}

  • CPACK_PACKAGE_INSTALL_DIRECTORY - If set, used as-is. Otherwise defaults to ${CMAKE_INSTALL_PREFIX}

  • CPACK_DEBIAN_PACKAGE_SECTION - Package section (defaults to “devel”)

  • CPACK_DEBIAN_PACKAGE_PRIORITY - Package priority (defaults to “optional”)

  • CPACK_RPM_PACKAGE_GROUP - RPM group (defaults to “Development/Libraries”)

  • CPACK_RPM_PACKAGE_LICENSE - RPM license (auto-detected if not set)

  • CPACK_RPM_PACKAGE_RELEASE - RPM release number (defaults to “1”)

Expected Files:

The function automatically searches for these files in CMAKE_CURRENT_SOURCE_DIR:

  • LICENSE* - License file (used for package and license detection)

  • README* - README file

Warnings are emitted if these files are not found, but package generation continues.

Example:

project(mylib VERSION 1.0.0)

libra_configure_cpack(
  "DEB;RPM"
  "A short project description"
  "Long description of the project features and capabilities."
  "John Doe"
  "https://example.com/mylib"
  "John Doe <john@example.com>")

# Generate packages with: make package

DEB-Specific Configuration:

  • File naming: DEB-DEFAULT (includes architecture)

  • Automatic shared library dependency detection

  • Strict control file permissions

  • Debug output enabled for troubleshooting

RPM-Specific Configuration:

  • File naming: RPM-DEFAULT (includes version, release, architecture)

  • Relocatable packages (allows installation to different prefixes)

  • Standard system directories excluded from package

Complete Example#

Here’s a full-featured cmake/project-local.cmake showing common patterns:

# Library target
libra_add_library(my_library STATIC
    src/core.cpp
    src/utils.cpp
)
target_include_directories(my_library PUBLIC include)

# Application target
libra_add_executable(my_app src/main.cpp)
target_link_libraries(my_app PRIVATE my_library)