CMake helper functions and utilities for libhal projects. Provides convenient functions for common patterns.
Note
Using v4? The v4 API (toolchain injection style) is deprecated. See
v4/README.md for legacy documentation. Consider migrating
to v5 for explicit find_package() integration and C++20 module support.
Via Conan:
def build_requirements(self):
self.tool_requires("libhal-cmake-util/[^5.0.0]")cmake_minimum_required(VERSION 4.0)
project(my_project LANGUAGES CXX)
# Find the helpers package
find_package(LibhalCMakeUtil REQUIRED)
# Initialize your project (required)
libhal_project_init()
# Create a library target named `my_library` with the following source and
# module files.
libhal_add_library(my_library
SOURCES src/foo.cpp
MODULES modules/bar.cppm
)
# Apply standard libhal compiler options
libhal_apply_compile_options(my_library)
# Setup library installation info
libhal_install_library(my_library NAMESPACE libhal)
# Add tests
libhal_add_tests(my_library
TEST_NAMES foo bar baz
MODULES tests/util.cppm
)Required - Sets up project-level configuration. This is the only mandatory function.
What it does:
- Enables compile_commands.json export
- Checks for Ninja/Visual Studio generator (required for modules)
- Sets up clang-tidy (enabled by default, see Clang-tidy)
- Adds compile_commands.json copy target
- Globally injects
-Qunused-argumentsintoCMAKE_CXX_FLAGSfor Clang/AppleClang
Note
-Qunused-arguments is appended to CMAKE_CXX_FLAGS (not
add_compile_options) so it is visible to CMake's C++20 module dependency
scanning commands for imported targets. During scanning, clang-scan-deps
passes both --precompile and -c to the compiler, making -c unused and
producing noisy clang++: warning: argument unused during compilation: '-c'
diagnostics. -Qunused-arguments suppresses this at the Clang driver level,
where -Wno-unused-command-line-argument does not.
libhal_project_init()After libhal_project_init() these flag attachment files become available:
Standard compile flags for libhal projects.
libhal_apply_compile_options(my_lib)Flags included:
- GCC/Clang:
-g
-Werror
-Wall
-Wextra
-Wshadow
-pedantic
-fexceptions
-fno-rtti
-Wno-unused-command-line-argument
- MSVC:
/W4 /WX /EHsc /permissive- /GR-
AddressSanitizer support (non-Windows only):
libhal_apply_asan(my_lib)This API is safe to use on Windows. Executing it on a Windows machine does nothing.
This is recommended only for unit and integration tests.
Linker identical code folding for an executable target:
libhal_apply_icf(my_app)Folds functions that compile to identical machine code (a common result of
template instantiation over structurally equivalent types, such as many
libhal-org futures/tasks differing only in a pointer-sized template
parameter) into a single copy in the final binary. Uses --icf=safe rather
than --icf=all: safe only folds functions the linker can prove are
interchangeable under all observable program behavior (including that
nothing compares their addresses for identity), so it carries no correctness
risk.
This is an LLD-only flag and is gated to Clang/AppleClang, since this org's Clang toolchain is the one paired with LLD. It is safe to call on any toolchain; on a non-Clang compiler it applies no flags.
Creates a static library target with optional sources and modules.
libhal_add_library(my_lib
SOURCES src/foo.cpp src/bar.cpp
MODULES modules/baz.cppm modules/qux.cppm
)Arguments:
- 1st argument is the library name (my_lib in the above example)
SOURCES- List of .cpp filesMODULES- List of .cppm module files
You may use the target my_lib elsewhere in the code with standard CMake commands like:
target_compiler_options(my_lib PRIVATE -Wall)Configures library installation with CMake config files.
libhal_install_library(my_lib NAMESPACE libhal)Arguments:
NAMESPACE(optional) - Namespace for exported target (default: library name)PACKAGE_NAME(optional) - Name used forfind_package()and the installed config file/directory (default: library name). Useful when thefind_package()name should differ from the linked target name, e.g. a library target namedutilthat should be discoverable asfind_package(libhal-util)while still linking aslibhal::util:
libhal_install_library(util NAMESPACE libhal PACKAGE_NAME libhal-util)find_package(libhal-util REQUIRED CONFIG)
target_link_libraries(my_app PRIVATE libhal::util)Adds unit tests for a library. Supports two modes:
libhal_add_tests(my_lib
TEST_SOURCES tests/foo.test.cpp tests/bar.test.cpp
PACKAGES libhal-mock
LINK_LIBRARIES libhal::mock
MODULES tests/util.cppm
)libhal_add_tests(my_lib
TEST_NAMES foo bar baz
MODULES tests/util.cppm
)Looks for tests/foo.test.cpp, tests/bar.test.cpp, etc.
Arguments:
TEST_SOURCES- Explicit list of test filesTEST_NAMES- Generate test filenames (tests/NAME.test.cpp)MODULES- Optional additional modules for testsPACKAGES- Additional packages to findLINK_LIBRARIES- Additional libraries to link
Creates a single executable/app.
libhal_add_executable(my_app
SOURCES apps/my_app.cpp src/common.cpp
INCLUDES include/
PACKAGES libhal-util libhal-lpc40
LINK_LIBRARIES libhal::util libhal::lpc40
)Builds multiple apps at once.
libhal_build_apps(
APPS app1 app2 app3
SOURCES src/common.cpp
INCLUDES include/
PACKAGES libhal-util libhal-expander
LINK_LIBRARIES libhal::util libhal::expander
)Looks for apps/app1.cpp, apps/app2.cpp, etc.
For embedded targets, these functions generate additional output files:
Creates Intel HEX file from ELF executable.
libhal_create_hex_file_from(my_firmware)
libhal_create_hex_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/app.hex")Without the OUTPUT_DIR parameter, the file will have the same name as the
target but with the extension .hex.
Creates raw binary file from ELF executable.
libhal_create_binary_file_from(my_firmware)
libhal_create_binary_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/app.hex")Without the OUTPUT_DIR parameter, the file will have the same name as the
target but with the extension .bin.
Creates disassembly files (.S and .demangled.S).
libhal_create_disassembly_from(my_firmware)Creates a disassembly file with source code interleaved (.lst).
libhal_create_disassembly_with_source_from(my_firmware)Prints size information (text, data, bss sections).
libhal_print_size_of(my_firmware)Clang-tidy checks are enabled by default (skipped automatically when
cross-compiling, or when clang-tidy isn't found on PATH). Control it via
CMake options:
# Disable clang-tidy checks
cmake -DLIBHAL_ENABLE_CLANG_TIDY=OFF ..
# Enable with automatic fixes
cmake -DLIBHAL_CLANG_TIDY_FIX=ON ..Or at the start of your CMakeList.txt file:
cmake_minimum_required(VERSION 4.0)
set(LIBHAL_ENABLE_CLANG_TIDY OFF)cmake_minimum_required(VERSION 4.0)
project(strong_ptr LANGUAGES CXX)
find_package(LibhalCMakeUtil REQUIRED)
libhal_project_init()
# Create library with modules
libhal_add_library(strong_ptr
MODULES
modules/strong_ptr.cppm
)
# Opt-in to compile options
libhal_apply_compile_options(strong_ptr)
# Install
libhal_install_library(strong_ptr NAMESPACE libhal)
# Add tests
libhal_add_tests(strong_ptr
TEST_NAMES
enable_from_this
strong_ptr
mixins
weak_ptr
optional_ptr
MODULES
tests/util.cppm
)cmake_minimum_required(VERSION 4.0)
project(my_app LANGUAGES CXX)
find_package(LibhalCMakeUtil REQUIRED)
libhal_project_init()
libhal_build_apps(
APPS
drc_v2
mc_x_v2
rc_servo
INCLUDES .
PACKAGES
libhal-actuator
libhal-expander
LINK_LIBRARIES
libhal::actuator
libhal::expander
)cmake_minimum_required(VERSION 4.0)
project(my_advanced_lib LANGUAGES CXX)
find_package(LibhalCMakeUtil REQUIRED)
# Initialize project
libhal_project_init()
# Create library manually
add_library(my_advanced_lib STATIC)
target_sources(my_advanced_lib PUBLIC
FILE_SET CXX_MODULES
TYPE CXX_MODULES
FILES
modules/foo.cppm
modules/bar.cppm
PRIVATE src/impl.cpp
)
# Opt-in to compile options
target_compile_options(my_advanced_lib PRIVATE ${LIBHAL_CXX_FLAGS})
# Custom compile definitions
target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG)
# Install
libhal_install_library(my_advanced_lib NAMESPACE mycompany)This package provides granular building blocks with optional convenience wrappers:
- Granular functions - Full control for complex needs
- Convenience wrappers - Sensible defaults for common patterns
- Opt-in compile options - Via apply functions &
LIBHAL_*_FLAGvariables. - Explicit, not magical - Clear what's happening
- Composable - Mix and match as needed
Apache-2.0
See CONTRIBUTING.md