Project setup#
This page covers the setup decisions that apply to every LIBRA project
regardless of whether you use the clibra CLI or plain CMake. Read this
once — the quickstarts reference it rather than repeat it.
Integrating LIBRA into your project#
Choose the integration method that fits your workflow:
Method |
Best for |
Use when |
|---|---|---|
CPM (recommended) |
Most projects; no pre-installation needed |
You want version-pinned, zero-install dependency management in CMake |
Conan |
Multi-repo organisations, complex dependencies |
You already use Conan, or manage 5+ related projects |
CMake package |
System-wide or shared team installation |
Multiple developers sharing one LIBRA installation |
In situ (submodule) |
Quick prototyping, standalone repos, CI/CD simplicity |
Single project; you want version control over LIBRA itself |
CPM fetches and caches LIBRA automatically at configure time. No pre-installation required; the version is pinned in your repository.
cmake_minimum_required(VERSION 3.31)
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v0.40.2/CPM.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)
set(CPM_SOURCE_CACHE
$ENV{HOME}/.cache/CPM
CACHE PATH "CPM source cache")
# Prefer local packages when present — useful for simultaneous
# local development of multiple LIBRA-enabled projects.
set(CPM_USE_LOCAL_PACKAGES ON)
include(${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM.cmake)
CPMAddPackage(
NAME libra
GIT_REPOSITORY https://github.com/jharwell/libra.git
GIT_TAG master)
list(APPEND CMAKE_MODULE_PATH ${libra_SOURCE_DIR}/cmake)
project(my_project C CXX)
include(libra/project)
Best for managing dependencies and multi-repo scaling.
conanfile.py:
def build_requirements(self):
self.tool_requires("libra/0.8.0")
CMakeLists.txt:
cmake_minimum_required(VERSION 3.31)
find_package(libra REQUIRED)
project(my_project C CXX)
include(libra/project)
Requires LIBRA to be installed to a system prefix or the same
CMAKE_INSTALL_PREFIX as the consuming project.
Install LIBRA:
git clone https://github.com/jharwell/libra.git
cmake -S libra -B libra/build -DCMAKE_INSTALL_PREFIX=/opt/libra
cmake --build libra/build --target install
CMakeLists.txt:
cmake_minimum_required(VERSION 3.31)
find_package(libra REQUIRED)
project(my_project C CXX)
include(libra/project)
Best for standalone repos where you want LIBRA under version control.
git submodule add https://github.com/jharwell/libra.git
CMakeLists.txt:
cmake_minimum_required(VERSION 3.31)
add_subdirectory(libra)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libra/cmake)
project(my_project C CXX)
include(libra/project)
Project layout#
LIBRA auto-discovers sources, headers, and tests when you follow these conventions:
my_project/
├── CMakeLists.txt
├── CMakePresets.json
├── cmake/
│ └── project-local.cmake ← target definitions (required)
├── src/ ← .cpp / .c files (auto-discovered)
├── include/ ← .hpp / .h headers (auto-discovered)
├── tests/ ← test files (auto-discovered by suffix)
└── docs/
├── Doxyfile.in ← doxygen configuration (LIBRA_DOCS=ON)
└── conf.py ← sphinx configuration (LIBRA_DOCS=ON)
Mandatory file extension conventions:
C++ source files must end in
.cpp; C++ headers in.hppC source files must end in
.c; C headers in.h
If your structure differs from the above, you can disable globbing and
list files manually in project-local.cmake.
In addition, build outputs in the binary directory follow these conventions. The build directory can of course be wherever you like.
libraries ->
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}(usually lib or lib64)executables ->
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}(usually bin)
Test file naming#
LIBRA discovers tests by matching filenames against suffix patterns. The
built-in defaults are -utest (unit), -itest (integration),
-rtest (regression), and _test (harness). Negative compilation
tests use .neg.cpp / .neg.c.
All patterns are configurable via matcher variables in
project-local.cmake. See Testing Reference for the complete
naming reference including negative tests, the test harness, and
interpreted test support.
project-local.cmake#
LIBRA expects your target definitions in cmake/project-local.cmake.
This keeps the root CMakeLists.txt clean and portable:
# Register the main executable. CXX_SRC is auto-populated
# from src/ at configure time.
libra_add_executable(${${PROJECT_NAME}_CXX_SRC})
# For a library instead (C_SRC auto-populated):
# libra_add_library(${${PROJECT_NAME}_C_SRC})
# Optional: enable project-wide quality gates
# set(LIBRA_ANALYSIS ON)
# set(LIBRA_FORTIFY ALL)
Note
libra_add_executable() and
libra_add_library() wrap CMake’s built-in equivalents
and automatically apply LIBRA’s compiler flags, analysis targets, and
quality gates. Targets registered with plain add_executable() or
add_library() will not receive LIBRA features unless you manually
apply them.
Always prefer the LIBRA variants for targets you want LIBRA to manage.
CMakePresets.json#
LIBRA and clibra are preset-driven. The following is the recommended
starting-point preset hierarchy. See Feature flags for a detailed
explanation of every preset and the rationale behind the structure.
{
"version": 6,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"LIBRA_TESTS": "OFF",
"LIBRA_COVERAGE": "OFF",
"LIBRA_ANALYSIS": "OFF",
"LIBRA_DOCS": "OFF"
}
},
{
"name": "debug",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LIBRA_TESTS": "ON"
}
},
{
"name": "release",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"LIBRA_LTO": "ON"
}
},
{
"name": "coverage",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LIBRA_TESTS": "ON",
"LIBRA_COVERAGE": "ON"
}
},
{
"name": "ci",
"inherits": "coverage"
},
{
"name": "analyze",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LIBRA_ANALYSIS": "ON"
}
}
],
"buildPresets": [
{ "name": "debug", "configurePreset": "debug" },
{ "name": "release", "configurePreset": "release" },
{ "name": "coverage", "configurePreset": "coverage" },
{ "name": "ci", "configurePreset": "ci" },
{ "name": "analyze", "configurePreset": "analyze",
"targets": ["analyze"] }
],
"testPresets": [
{
"name": "debug",
"configurePreset": "debug",
"output": { "outputOnFailure": true }
},
{
"name": "coverage",
"configurePreset": "coverage",
"output": { "outputOnFailure": true }
},
{
"name": "ci",
"configurePreset": "ci",
"output": { "outputOnFailure": true }
}
]
}