project-local.cmake: How To Hook Into LIBRA

To hook into LIBRA, you define a cmake/project-local.cmake. Basically, you can put WHATEVER you want in this file–all the usual cmake stuff–drawing on predefined things in LIBRA to make your life easier:

Note

All cmake functions which LIBRA exposes are prefixed with libra_; all other functions should be considered not part of the API and can change at any time.

Target Declaration Wrappers

libra_add_library

Thin wrapper around add_library() which forwards all arguments to the built in function, and adds the target name to LIBRA_TARGETS. You don’t have to use this function, but if you don’t then much of the LIBRA magic w.r.t. compilers/compilation can only be applied to the PROJECT_NAME target.

libra_add_executable

Thin wrapper around add_executable() which forwards all arguments to the built in function, and adds the target name to LIBRA_TARGETS. You don’t have to use this function, but if you don’t then much of the LIBRA magic w.r.t. compilers/compilation can only be applied to the PROJECT_NAME target.

Variables

The variables listed in this section are generally for configuring various LIBRA features, and therefore are intended to be set via project-local.cmake. However, many of the cmdline interface variables detailed in Configure-Time can be set permanently in project-local.cmake too, but not all of them. Exceptions are:

LIBRA_ANALYSIS_LANGUAGE

Defines the language that the different static analysis checkers/formatters/fixers will use for checking the project. This should be specified BEFORE any subdirectories, external projects, etc. are specified. Only used if LIBRA_ANALYSIS is true. If used, value must be one of:

  • C

  • CXX

You should only ever need to set this if your project contains both C and C++ code, to switch between which is checked.

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-format 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 a each category of checks that LIBRA creates targets for. Defaults to:

,-clang-diagnostic-*

Added in version 0.8.15.

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.

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`.

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`.

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_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}.

${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.

Build And Configure-time Diagnostics

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_config_summary_prepare_fields

Prepare configuration fields for display by adding padding and colorization.

Given a list of configurable fields in a project as strings, this function defines a set of new variables, one per field, with the prefix EMIT_. The value of each new variable is right-padded with spaces so that any extra content on each line (when the variables are printed to the screen) can be left-aligned. Additionally, common values like ON/OFF and YES/NO are colorized for easier visual parsing.

This function is typically used in conjunction with libra_config_summary() to create nicely formatted configuration summaries.

Param FIELDS:

List of field names (variable names) to prepare for display. Each field will have a corresponding EMIT_<field> variable created in the parent scope that contains the padded and colorized value.

Colorization Rules:

  • ON, on, YES, yes - Displayed in green

  • OFF, off, NO, no - Displayed in red

  • Special strings (NONE, ALL, CONAN) - No colorization

  • Version numbers (x.y.z format) - No colorization

Example:

set(MY_FIELDS
  CMAKE_BUILD_TYPE
  LIBRA_TESTS
  LIBRA_CODE_COV)

libra_config_summary_prepare_fields("${MY_FIELDS}")

# Now you can use EMIT_CMAKE_BUILD_TYPE, EMIT_LIBRA_TESTS, etc.
message(STATUS "Build type: ${EMIT_CMAKE_BUILD_TYPE}")
message(STATUS "Tests: ${EMIT_LIBRA_TESTS}")
libra_config_summary

Print a comprehensive summary of LIBRA configuration variables to the terminal.

Displays a nicely formatted, colorized summary of all LIBRA configuration variables and their current values. This helps debug the inevitable “Did I actually set the variable I thought I did?” questions. Using this function, you can see EXACTLY what variable values will be when you invoke your chosen build engine.

The summary includes:

  • LIBRA version and driver mode

  • Generator and build system information

  • Installation paths

  • Build type and architecture

  • Compiler information and standards

  • All LIBRA feature flags (tests, sanitizers, optimizations, etc.)

  • Available make targets for each enabled feature

Usage Patterns:

You can put this at the end of project-local.cmake if you want to control when LIBRA’s configuration summary vs. your project’s configuration summary is emitted. Otherwise, LIBRA will run it automatically at the end of the configure step, as determined by LIBRA_SUMMARY.

Example:

# In your CMakeLists.txt or project-local.cmake
libra_config_summary()

# Output will show something like:
# --------------------------------------------------------------------------------
#                            LIBRA Configuration Summary
# --------------------------------------------------------------------------------
# LIBRA version.........................: 0.9.25
# Build type............................: Release
# Build tests...........................: ON
# ...

Note

This function only displays the summary once per configure run. Subsequent calls in the same configure will be ignored.

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 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 Template:

The following variables are available for use in your 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_BUILD - The configured C compiler flags relevant for building (excludes diagnostic flags like -W*).

You can also use any standard CMake variables (e.g., CMAKE_C_FLAGS_RELEASE, PROJECT_VERSION, CMAKE_BUILD_TYPE, etc.).

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_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.

To use, include(libra/package/install.cmake).

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).

To use, include(libra/package/install.cmake).

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.

To use, include(libra/package/install.cmake).

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.

To use, include(libra/package/install.cmake).

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}/include
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.

To use, include(libra/package/install.cmake).

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_PREFIX}/include

  • 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.

To use, include(libra/package/deploy.cmake).

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_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