From c4dd7396561d00ee420ed42dc5cf46cdc2881cc0 Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Sun, 8 Feb 2026 13:20:23 -0800 Subject: [PATCH 1/6] Initial --- CMakeLists.txt | 47 +++ cmake/LibhalBinUtils.cmake | 230 ++++++++++++ cmake/LibhalBuildConfig.cmake | 60 +++ cmake/LibhalClangTidy.cmake | 61 +++ cmake/LibhalCompileOptions.cmake | 61 +++ cmake/LibhalExecutable.cmake | 125 +++++++ cmake/LibhalLibrary.cmake | 159 ++++++++ ...colors.cmake => LibhalTerminalColor.cmake} | 0 cmake/LibhalTesting.cmake | 114 ++++++ cmake/{ => deprecated}/build.cmake | 2 +- cmake/{ => deprecated}/build_outputs.cmake | 0 cmake/{ => deprecated}/clang-tidy.conf | 0 cmake/deprecated/colors.cmake | 33 ++ .../optimize_debug_build.cmake | 0 conanfile.py | 35 +- examples/demos_CMakeLists.txt | 70 ++++ examples/libhal-actuator_CMakeLists.txt | 94 +++++ examples/strong_ptr_CMakeLists.txt | 66 ++++ files/libhal-cmake-helpers/LICENSE | 17 + files/libhal-cmake-helpers/MIGRATION.md | 353 ++++++++++++++++++ files/libhal-cmake-helpers/PACKAGE_SUMMARY.md | 136 +++++++ files/libhal-cmake-helpers/README.md | 350 +++++++++++++++++ files/libhal-cmake-helpers/conanfile.py | 58 +++ 23 files changed, 2055 insertions(+), 16 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 cmake/LibhalBinUtils.cmake create mode 100644 cmake/LibhalBuildConfig.cmake create mode 100644 cmake/LibhalClangTidy.cmake create mode 100644 cmake/LibhalCompileOptions.cmake create mode 100644 cmake/LibhalExecutable.cmake create mode 100644 cmake/LibhalLibrary.cmake rename cmake/{colors.cmake => LibhalTerminalColor.cmake} (100%) create mode 100644 cmake/LibhalTesting.cmake rename cmake/{ => deprecated}/build.cmake (99%) rename cmake/{ => deprecated}/build_outputs.cmake (100%) rename cmake/{ => deprecated}/clang-tidy.conf (100%) create mode 100644 cmake/deprecated/colors.cmake rename cmake/{ => deprecated}/optimize_debug_build.cmake (100%) create mode 100644 examples/demos_CMakeLists.txt create mode 100644 examples/libhal-actuator_CMakeLists.txt create mode 100644 examples/strong_ptr_CMakeLists.txt create mode 100644 files/libhal-cmake-helpers/LICENSE create mode 100644 files/libhal-cmake-helpers/MIGRATION.md create mode 100644 files/libhal-cmake-helpers/PACKAGE_SUMMARY.md create mode 100644 files/libhal-cmake-helpers/README.md create mode 100644 files/libhal-cmake-helpers/conanfile.py diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3cae309 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.15) + +project(libhal-cmake-helpers LANGUAGES NONE) + +include(GNUInstallDirs) + +# Install all CMake modules +install( + FILES + cmake/LibhalBuildConfig.cmake + cmake/LibhalCompileOptions.cmake + cmake/LibhalLibrary.cmake + cmake/LibhalExecutable.cmake + cmake/LibhalTesting.cmake + cmake/LibhalClangTidy.cmake + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalBuild +) + +# Create a version file +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/LibhalBuildConfigVersion.cmake" + VERSION 1.0.0 + COMPATIBILITY SameMajorVersion +) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/LibhalBuildConfigVersion.cmake" + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalBuild +) diff --git a/cmake/LibhalBinUtils.cmake b/cmake/LibhalBinUtils.cmake new file mode 100644 index 0000000..075e3a8 --- /dev/null +++ b/cmake/LibhalBinUtils.cmake @@ -0,0 +1,230 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Firmware output generation helpers for embedded targets + +# Create Intel HEX file from ELF executable +# +# Generates a .hex file suitable for flashing to microcontrollers. +# +# Usage: +# libhal_create_hex_file_from(my_firmware) +# libhal_create_hex_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/artifacts") +function(libhal_create_hex_file_from TARGET_NAME) + cmake_parse_arguments(ARG "" "OUTPUT_DIR" "" ${ARGN}) + + # Verify target exists + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") + endif() + + # Verify objcopy is available + if(NOT CMAKE_OBJCOPY) + message(WARNING "objcopy not found - cannot generate hex file for ${TARGET_NAME}") + return() + endif() + + # Determine output directory + if(ARG_OUTPUT_DIR) + set(OUTPUT_PATH "${ARG_OUTPUT_DIR}/${TARGET_NAME}.hex") + else() + set(OUTPUT_PATH "$.hex") + endif() + + # Create hex file using objcopy + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} -O ihex + $ + ${OUTPUT_PATH} + COMMENT "Creating Intel HEX file: ${TARGET_NAME}.hex" + VERBATIM + ) + + message(STATUS "Will generate Intel HEX file for ${TARGET_NAME}") +endfunction() + +# Create binary file from ELF executable +# +# Generates a .bin file (raw binary) suitable for direct flashing. +# +# Usage: +# libhal_create_binary_file_from(my_firmware) +# libhal_create_binary_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/artifacts") +function(libhal_create_binary_file_from TARGET_NAME) + cmake_parse_arguments(ARG "" "OUTPUT_DIR" "" ${ARGN}) + + # Verify target exists + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") + endif() + + # Verify objcopy is available + if(NOT CMAKE_OBJCOPY) + message(WARNING "objcopy not found - cannot generate binary file for ${TARGET_NAME}") + return() + endif() + + # Determine output directory + if(ARG_OUTPUT_DIR) + set(OUTPUT_PATH "${ARG_OUTPUT_DIR}/${TARGET_NAME}.bin") + else() + set(OUTPUT_PATH "$.bin") + endif() + + # Create binary file using objcopy + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} -O binary + $ + ${OUTPUT_PATH} + COMMENT "Creating binary file: ${TARGET_NAME}.bin" + VERBATIM + ) + + message(STATUS "Will generate binary file for ${TARGET_NAME}") +endfunction() + +# Create disassembly files from ELF executable +# +# Generates two disassembly files: +# - .S - raw disassembly +# - .demangled.S - disassembly with demangled C++ symbols +# +# Usage: +# libhal_create_disassembly_from(my_firmware) +# libhal_create_disassembly_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/debug") +function(libhal_create_disassembly_from TARGET_NAME) + cmake_parse_arguments(ARG "" "OUTPUT_DIR" "" ${ARGN}) + + # Verify target exists + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") + endif() + + # Verify objdump is available + if(NOT CMAKE_OBJDUMP) + message(WARNING "objdump not found - cannot generate disassembly for ${TARGET_NAME}") + return() + endif() + + # Determine output directory + if(ARG_OUTPUT_DIR) + set(OUTPUT_PATH_RAW "${ARG_OUTPUT_DIR}/${TARGET_NAME}.S") + set(OUTPUT_PATH_DEMANGLED "${ARG_OUTPUT_DIR}/${TARGET_NAME}.demangled.S") + else() + set(OUTPUT_PATH_RAW "$.S") + set(OUTPUT_PATH_DEMANGLED "$.demangled.S") + endif() + + # Create raw disassembly file + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_OBJDUMP} -d -s + $ + > ${OUTPUT_PATH_RAW} + COMMENT "Creating disassembly: ${TARGET_NAME}.S" + VERBATIM + ) + + # Create demangled disassembly file + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_OBJDUMP} -d -s --demangle + $ + > ${OUTPUT_PATH_DEMANGLED} + COMMENT "Creating demangled disassembly: ${TARGET_NAME}.demangled.S" + VERBATIM + ) + + message(STATUS "Will generate disassembly files for ${TARGET_NAME}") +endfunction() + +# Create disassembly with source code interleaved +# +# Generates a .lst file containing disassembly mixed with original source code. +# This is useful for analyzing how the compiler translated specific code sections. +# +# Usage: +# libhal_create_disassembly_with_source_from(my_firmware) +# libhal_create_disassembly_with_source_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/debug") +function(libhal_create_disassembly_with_source_from TARGET_NAME) + cmake_parse_arguments(ARG "" "OUTPUT_DIR" "" ${ARGN}) + + # Verify target exists + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") + endif() + + # Verify objdump is available + if(NOT CMAKE_OBJDUMP) + message(WARNING "objdump not found - cannot generate listing for ${TARGET_NAME}") + return() + endif() + + # Determine output directory + if(ARG_OUTPUT_DIR) + set(OUTPUT_PATH "${ARG_OUTPUT_DIR}/${TARGET_NAME}.lst") + else() + set(OUTPUT_PATH "$.lst") + endif() + + # Create listing file with source and disassembly + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_OBJDUMP} --all-headers --source --disassemble --demangle + $ + > ${OUTPUT_PATH} + COMMENT "Creating source+disassembly listing: ${TARGET_NAME}.lst" + VERBATIM + ) + + message(STATUS "Will generate source-annotated disassembly for ${TARGET_NAME}") +endfunction() + +# Print size information for the executable +# +# Displays text, data, and bss section sizes after building. +# Useful for tracking memory usage on resource-constrained embedded systems. +# +# Usage: +# libhal_print_size_of(my_firmware) +function(libhal_print_size_of TARGET_NAME) + # Verify target exists + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") + endif() + + # Verify size utility is available + if(NOT CMAKE_SIZE_UTIL) + message(WARNING "size utility not found - cannot print size info for ${TARGET_NAME}") + return() + endif() + + # Print executable size after build + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_SIZE_UTIL} $ + COMMENT "Memory usage for ${TARGET_NAME}:" + VERBATIM + ) + + message(STATUS "Will print size information for ${TARGET_NAME}") +endfunction() diff --git a/cmake/LibhalBuildConfig.cmake b/cmake/LibhalBuildConfig.cmake new file mode 100644 index 0000000..5917c0d --- /dev/null +++ b/cmake/LibhalBuildConfig.cmake @@ -0,0 +1,60 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# LibhalBuild CMake Configuration +# Main entry point for find_package(LibhalBuild) +# Documentation: https://github.com/libhal/libhal-cmake-helpers + +# Include all helper modules +include(${CMAKE_CURRENT_LIST_DIR}/LibhalCompileOptions.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/LibhalClangTidy.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/LibhalLibrary.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/LibhalExecutable.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/LibhalTesting.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/LibhalTerminalColor.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/LibhalBinUtils.cmake) + +# Main project initialization function +# This is the only "required" function - handles project-level setup +function(libhal_project_init PROJECT_NAME) + # Standard CMake setup + set(CMAKE_EXPORT_COMPILE_COMMANDS ON PARENT_SCOPE) + set(CMAKE_COLOR_DIAGNOSTICS ON PARENT_SCOPE) + set(CMAKE_CXX_SCAN_FOR_MODULES ON PARENT_SCOPE) + + project(${PROJECT_NAME} LANGUAGES CXX) + + # Require Ninja or Visual Studio for C++20 modules + if(NOT CMAKE_GENERATOR MATCHES "Ninja|Visual Studio") + message(FATAL_ERROR "C++20 modules require Ninja or Visual Studio generator") + endif() + + # Set up clang-tidy if enabled + libhal_setup_clang_tidy() + + # Create the interface targets for compile options + libhal_create_interface_targets() + + # Add compile_commands.json copy target + add_custom_target(copy_compile_commands ALL + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_BINARY_DIR}/compile_commands.json + ${CMAKE_SOURCE_DIR}/compile_commands.json + DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json + COMMENT "Copying compile_commands.json to source directory" + ) +endfunction() + +message(STATUS "LibhalBuild CMake helpers loaded!") +message(STATUS " Source: https://github.com/libhal/libhal-cmake-util") diff --git a/cmake/LibhalClangTidy.cmake b/cmake/LibhalClangTidy.cmake new file mode 100644 index 0000000..74251f6 --- /dev/null +++ b/cmake/LibhalClangTidy.cmake @@ -0,0 +1,61 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Clang-tidy integration for libhal projects + +# Options for controlling clang-tidy +option(LIBHAL_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) +option(LIBHAL_CLANG_TIDY_FIX "Apply clang-tidy fixes automatically" OFF) + +# Internal function to set up clang-tidy +# Called by libhal_project_init() +function(libhal_setup_clang_tidy) + if(CMAKE_CROSSCOMPILING) + message(STATUS "Cross-compiling, skipping clang-tidy") + return() + endif() + + if(NOT LIBHAL_ENABLE_CLANG_TIDY AND NOT LIBHAL_CLANG_TIDY_FIX) + message(STATUS "Clang-tidy disabled (use -DLIBHAL_ENABLE_CLANG_TIDY=ON to enable)") + return() + endif() + + find_program(CLANG_TIDY_EXE NAMES clang-tidy) + + if(NOT CLANG_TIDY_EXE) + message(STATUS "Clang-tidy not found - continuing without it") + return() + endif() + + message(STATUS "Clang-tidy found: ${CLANG_TIDY_EXE}") + + # Build the clang-tidy command + set(CLANG_TIDY_CMD "${CLANG_TIDY_EXE}") + + # Look for .clang-tidy file + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy") + list(APPEND CLANG_TIDY_CMD "--config-file=${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy") + endif() + + # Add --fix if requested + if(LIBHAL_CLANG_TIDY_FIX) + list(APPEND CLANG_TIDY_CMD "--fix") + message(STATUS "Clang-tidy will apply fixes automatically") + endif() + + # Set the CMake variable to enable clang-tidy for all targets + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_CMD} PARENT_SCOPE) + + message(STATUS "Clang-tidy enabled") +endfunction() diff --git a/cmake/LibhalCompileOptions.cmake b/cmake/LibhalCompileOptions.cmake new file mode 100644 index 0000000..3a1cb42 --- /dev/null +++ b/cmake/LibhalCompileOptions.cmake @@ -0,0 +1,61 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Creates interface targets for compile options +# Users can opt-in via: target_link_libraries(my_lib PRIVATE libhal::compile_options) + +function(libhal_create_interface_targets) + # Only create once (in case multiple projects use this) + if(TARGET libhal::compile_options) + return() + endif() + + # Standard compile options for libhal libraries + add_library(libhal::compile_options INTERFACE IMPORTED GLOBAL) + target_compile_options(libhal::compile_options INTERFACE + $<$: + -g + -Werror + -Wall + -Wextra + -Wshadow + -Wno-unused-command-line-argument + -Wpedantic + -fexceptions + -fno-rtti + > + $<$: + /W4 + /WX + /EHsc + /permissive- + /GR- + > + ) + + # AddressSanitizer support (GCC/Clang on non-Windows only) + add_library(libhal::asan INTERFACE IMPORTED GLOBAL) + if(NOT WIN32) + target_compile_options(libhal::asan INTERFACE + $<$:-fsanitize=address> + ) + target_link_options(libhal::asan INTERFACE + $<$:-fsanitize=address> + ) + endif() + + message(STATUS "Created libhal interface targets:") + message(STATUS " - libhal::compile_options (opt-in compile flags)") + message(STATUS " - libhal::asan (opt-in AddressSanitizer)") +endfunction() diff --git a/cmake/LibhalExecutable.cmake b/cmake/LibhalExecutable.cmake new file mode 100644 index 0000000..c0ab011 --- /dev/null +++ b/cmake/LibhalExecutable.cmake @@ -0,0 +1,125 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Executable and demo helpers for libhal projects + +# Add a single executable/demo +# +# Usage: +# libhal_add_executable(my_demo +# SOURCES demos/my_demo.cpp +# INCLUDES include/ +# PACKAGES libhal-util libhal-lpc40 +# LINK_LIBRARIES libhal::util libhal::lpc40 +# ) +function(libhal_add_executable TARGET_NAME) + cmake_parse_arguments(ARG + "" + "" + "SOURCES;INCLUDES;PACKAGES;LINK_LIBRARIES" + ${ARGN} + ) + + if(NOT ARG_SOURCES) + message(FATAL_ERROR "SOURCES is required for ${TARGET_NAME}") + endif() + + # Create executable + add_executable(${TARGET_NAME}) + + # Add sources + target_sources(${TARGET_NAME} PRIVATE ${ARG_SOURCES}) + + # Add include directories + if(ARG_INCLUDES) + target_include_directories(${TARGET_NAME} PRIVATE ${ARG_INCLUDES}) + endif() + + # Find packages + foreach(PKG IN LISTS ARG_PACKAGES) + find_package(${PKG} REQUIRED) + endforeach() + + # Link libraries + if(ARG_LINK_LIBRARIES) + target_link_libraries(${TARGET_NAME} PRIVATE ${ARG_LINK_LIBRARIES}) + endif() + + # Apply compile options + target_link_libraries(${TARGET_NAME} PRIVATE libhal::compile_options) + + # Set C++ standard + target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23) + + message(STATUS "Created executable: ${TARGET_NAME}") +endfunction() + +# Build multiple demos/executables at once +# Keeps existing monolithic pattern for convenience +# +# Usage: +# libhal_build_demos( +# DEMOS demo1 demo2 demo3 +# SOURCES src/common.cpp # Optional shared sources +# INCLUDES include/ # Optional include directories +# PACKAGES libhal-util libhal-lpc40 +# LINK_LIBRARIES libhal::util libhal::lpc40 +# ) +# +# This will look for demos/demo1.cpp, demos/demo2.cpp, etc. +function(libhal_build_demos) + cmake_parse_arguments(ARG + "" + "" + "DEMOS;SOURCES;INCLUDES;PACKAGES;LINK_LIBRARIES" + ${ARGN} + ) + + if(NOT ARG_DEMOS) + message(FATAL_ERROR "DEMOS list is required") + endif() + + # Find packages once for all demos + foreach(PKG IN LISTS ARG_PACKAGES) + find_package(${PKG} REQUIRED) + endforeach() + + message(STATUS "Building demos:") + + # Create each demo + foreach(DEMO_NAME IN LISTS ARG_DEMOS) + set(DEMO_FILE "demos/${DEMO_NAME}.cpp") + + # Check if demo file exists + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DEMO_FILE}") + message(WARNING "Demo file not found: ${DEMO_FILE}, skipping...") + continue() + endif() + + # Build the demo sources list + set(DEMO_SOURCES ${DEMO_FILE}) + if(ARG_SOURCES) + list(APPEND DEMO_SOURCES ${ARG_SOURCES}) + endif() + + # Create the demo using the granular function + libhal_add_executable(${DEMO_NAME} + SOURCES ${DEMO_SOURCES} + INCLUDES ${ARG_INCLUDES} + LINK_LIBRARIES ${ARG_LINK_LIBRARIES} + ) + + message(STATUS " - ${DEMO_NAME}") + endforeach() +endfunction() diff --git a/cmake/LibhalLibrary.cmake b/cmake/LibhalLibrary.cmake new file mode 100644 index 0000000..f1882c0 --- /dev/null +++ b/cmake/LibhalLibrary.cmake @@ -0,0 +1,159 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(GNUInstallDirs) + +# Granular function: Create a library with sources and/or modules +# +# Usage: +# libhal_add_library(my_lib +# SOURCES src/foo.cpp src/bar.cpp +# MODULES modules/baz.cppm +# ) +function(libhal_add_library TARGET_NAME) + cmake_parse_arguments(ARG "" "" "SOURCES;MODULES" ${ARGN}) + + # Create the library + add_library(${TARGET_NAME} STATIC) + add_library(${TARGET_NAME}::${TARGET_NAME} ALIAS ${TARGET_NAME}) + + # Add sources if provided + if(ARG_SOURCES) + target_sources(${TARGET_NAME} PRIVATE ${ARG_SOURCES}) + endif() + + # Add modules if provided + if(ARG_MODULES) + target_sources(${TARGET_NAME} PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES ${ARG_MODULES} + ) + endif() + + # Set C++23 standard for modules support + target_compile_features(${TARGET_NAME} PUBLIC cxx_std_23) + + message(STATUS "Created library: ${TARGET_NAME}") +endfunction() + +# Granular function: Install a library with CMake config +# +# Usage: +# libhal_install_library(my_lib NAMESPACE libhal) +# libhal_install_library(my_lib) # Uses library name as namespace +function(libhal_install_library TARGET_NAME) + cmake_parse_arguments(ARG "" "NAMESPACE" "" ${ARGN}) + + # Default namespace is the target name + if(NOT ARG_NAMESPACE) + set(ARG_NAMESPACE ${TARGET_NAME}) + endif() + + # Install the library and its module files + install( + TARGETS ${TARGET_NAME} + EXPORT ${TARGET_NAME}_targets + FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + CXX_MODULES_BMI DESTINATION "${CMAKE_INSTALL_LIBDIR}/bmi" + ) + + # Install the CMake config files + install( + EXPORT ${TARGET_NAME}_targets + FILE "${TARGET_NAME}-config.cmake" + NAMESPACE ${ARG_NAMESPACE}:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME}" + CXX_MODULES_DIRECTORY "cxx-modules" + ) + + message(STATUS "Configured install for: ${TARGET_NAME} (namespace: ${ARG_NAMESPACE}::)") +endfunction() + +# Convenience function: Create and install a library in one call +# +# Usage: +# libhal_quick_library(my_lib +# SOURCES src/foo.cpp +# MODULES modules/bar.cppm +# NAMESPACE libhal +# ) +function(libhal_quick_library TARGET_NAME) + cmake_parse_arguments(ARG "" "NAMESPACE" "SOURCES;MODULES" ${ARGN}) + + # Create the library + libhal_add_library(${TARGET_NAME} + SOURCES ${ARG_SOURCES} + MODULES ${ARG_MODULES} + ) + + # Auto-apply compile options and asan + target_link_libraries(${TARGET_NAME} PRIVATE + libhal::compile_options + $<$>:libhal::asan> + ) + + # Install with namespace + if(ARG_NAMESPACE) + libhal_install_library(${TARGET_NAME} NAMESPACE ${ARG_NAMESPACE}) + else() + libhal_install_library(${TARGET_NAME}) + endif() +endfunction() + +# Monolithic convenience function (keeps existing pattern) +# Combines library creation, testing, and installation +# +# Usage: +# libhal_test_and_make_library( +# LIBRARY_NAME libhal-actuator +# SOURCES src/rc_servo.cpp src/drc_v2.cpp +# TEST_SOURCES tests/rc_servo.test.cpp tests/drc.test.cpp +# PACKAGES libhal-mock +# LINK_LIBRARIES libhal::mock +# ) +function(libhal_test_and_make_library) + cmake_parse_arguments(ARG + "" + "LIBRARY_NAME" + "SOURCES;TEST_SOURCES;PACKAGES;LINK_LIBRARIES" + ${ARGN} + ) + + if(NOT ARG_LIBRARY_NAME) + message(FATAL_ERROR "LIBRARY_NAME is required") + endif() + + # Create library + libhal_add_library(${ARG_LIBRARY_NAME} + SOURCES ${ARG_SOURCES} + ) + + # Apply compile options + target_link_libraries(${ARG_LIBRARY_NAME} PRIVATE libhal::compile_options) + + # Install library + libhal_install_library(${ARG_LIBRARY_NAME}) + + # Add tests if provided + if(ARG_TEST_SOURCES) + libhal_add_tests(${ARG_LIBRARY_NAME} + TEST_SOURCES ${ARG_TEST_SOURCES} + PACKAGES ${ARG_PACKAGES} + LINK_LIBRARIES ${ARG_LINK_LIBRARIES} + ) + endif() +endfunction() diff --git a/cmake/colors.cmake b/cmake/LibhalTerminalColor.cmake similarity index 100% rename from cmake/colors.cmake rename to cmake/LibhalTerminalColor.cmake diff --git a/cmake/LibhalTesting.cmake b/cmake/LibhalTesting.cmake new file mode 100644 index 0000000..7fde62c --- /dev/null +++ b/cmake/LibhalTesting.cmake @@ -0,0 +1,114 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test setup helpers for libhal projects + +# Add tests for a library +# +# Supports two modes: +# 1. TEST_SOURCES - explicit test file list +# 2. TEST_NAMES - derive filenames from names (tests/NAME.test.cpp) +# +# Usage: +# libhal_add_tests(my_lib +# TEST_SOURCES tests/foo.test.cpp tests/bar.test.cpp +# PACKAGES libhal-mock +# LINK_LIBRARIES libhal::mock +# ) +# +# libhal_add_tests(my_lib +# TEST_NAMES foo bar baz +# MODULES tests/util.cppm +# PACKAGES libhal-mock +# ) +function(libhal_add_tests TARGET_NAME) + cmake_parse_arguments(ARG + "" + "MODULES" + "TEST_SOURCES;TEST_NAMES;PACKAGES;LINK_LIBRARIES" + ${ARGN} + ) + + # Skip tests when cross-compiling + if(CMAKE_CROSSCOMPILING) + message(STATUS "Cross-compiling, skipping tests for ${TARGET_NAME}") + return() + endif() + + message(STATUS "Adding tests for ${TARGET_NAME}") + + # Enable testing + include(CTest) + enable_testing() + + # Find boost-ut for testing + find_package(ut REQUIRED) + + # Determine test list + set(TEST_LIST) + if(ARG_TEST_SOURCES) + set(TEST_LIST ${ARG_TEST_SOURCES}) + elseif(ARG_TEST_NAMES) + foreach(NAME IN LISTS ARG_TEST_NAMES) + list(APPEND TEST_LIST "tests/${NAME}.test.cpp") + endforeach() + else() + message(FATAL_ERROR "Either TEST_SOURCES or TEST_NAMES must be provided") + endif() + + # Find additional packages if specified + foreach(PKG IN LISTS ARG_PACKAGES) + find_package(${PKG} REQUIRED) + endforeach() + + # Create test executable for each test file + foreach(TEST_FILE IN LISTS TEST_LIST) + # Extract test name from file path + get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) + string(REPLACE ".test" "" TEST_NAME ${TEST_NAME}) + set(TEST_TARGET "test_${TEST_NAME}") + + # Create test executable + add_executable(${TEST_TARGET}) + + # Add utility module if provided + if(ARG_MODULES) + target_sources(${TEST_TARGET} PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES ${ARG_MODULES} + PRIVATE ${TEST_FILE} + ) + else() + target_sources(${TEST_TARGET} PRIVATE ${TEST_FILE}) + endif() + + # Configure test + target_compile_features(${TEST_TARGET} PRIVATE cxx_std_23) + + # Link against the library under test and dependencies + target_link_libraries(${TEST_TARGET} PRIVATE + Boost::ut + ${TARGET_NAME} + libhal::compile_options + libhal::asan + ${ARG_LINK_LIBRARIES} + ) + + # Register with CTest + add_test(NAME ${TEST_TARGET} COMMAND ${TEST_TARGET}) + + message(STATUS " - ${TEST_TARGET}") + endforeach() +endfunction() diff --git a/cmake/build.cmake b/cmake/deprecated/build.cmake similarity index 99% rename from cmake/build.cmake rename to cmake/deprecated/build.cmake index 7eb2ff4..76510ee 100644 --- a/cmake/build.cmake +++ b/cmake/deprecated/build.cmake @@ -110,7 +110,7 @@ function(libhal_unit_test) # (Windows ASAN requires runtime DLL setup) if(ADDRESS_SANITIZER_SUPPORT AND NOT WIN32) message(STATUS - "${LIBHAL_TITLE} Address Sanitizer available! Using it for tests!") + "${LIBHAL_TITLE} Address Sanitizer available! Using it for tests! WIN32 '${WIN32}'") target_compile_options(unit_test PRIVATE -fsanitize=address) target_link_options(unit_test PRIVATE -fsanitize=address) else() diff --git a/cmake/build_outputs.cmake b/cmake/deprecated/build_outputs.cmake similarity index 100% rename from cmake/build_outputs.cmake rename to cmake/deprecated/build_outputs.cmake diff --git a/cmake/clang-tidy.conf b/cmake/deprecated/clang-tidy.conf similarity index 100% rename from cmake/clang-tidy.conf rename to cmake/deprecated/clang-tidy.conf diff --git a/cmake/deprecated/colors.cmake b/cmake/deprecated/colors.cmake new file mode 100644 index 0000000..0cf6fda --- /dev/null +++ b/cmake/deprecated/colors.cmake @@ -0,0 +1,33 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(NOT WIN32) + string(ASCII 27 Esc) + set(ColourReset "${Esc}[m") + set(ColourBold "${Esc}[1m") + set(Red "${Esc}[31m") + set(Green "${Esc}[32m") + set(Yellow "${Esc}[33m") + set(Blue "${Esc}[34m") + set(Magenta "${Esc}[35m") + set(Cyan "${Esc}[36m") + set(White "${Esc}[37m") + set(BoldRed "${Esc}[1;31m") + set(BoldGreen "${Esc}[1;32m") + set(BoldYellow "${Esc}[1;33m") + set(BoldBlue "${Esc}[1;34m") + set(BoldMagenta "${Esc}[1;35m") + set(BoldCyan "${Esc}[1;36m") + set(BoldWhite "${Esc}[1;37m") +endif() diff --git a/cmake/optimize_debug_build.cmake b/cmake/deprecated/optimize_debug_build.cmake similarity index 100% rename from cmake/optimize_debug_build.cmake rename to cmake/deprecated/optimize_debug_build.cmake diff --git a/conanfile.py b/conanfile.py index b4752d3..10046ee 100644 --- a/conanfile.py +++ b/conanfile.py @@ -16,6 +16,7 @@ from conan.tools.files import copy from conan.tools.layout import basic_layout import os +from pathlib import Path required_conan_version = ">=2.0.6" @@ -56,39 +57,43 @@ def package(self): dst=self.package_folder) def package_info(self): + # ====================================================================== + # DEPRECATED part of package_info + # + # NOTE: It is not advised to continue using these cmake scripts and + # their functions, it is recommended to use the `LibhalBuild` package + # instead. + # ====================================================================== + # Add toolchain.cmake to user_toolchain configuration info to be used # by CMakeToolchain generator - build_outputs_path = os.path.join( - self.package_folder, "cmake/build_outputs.cmake") - optimize_debug_build_path = os.path.join( - self.package_folder, "cmake/optimize_debug_build.cmake") - build_path = os.path.join( - self.package_folder, "cmake/build.cmake") - colors_path = os.path.join( - self.package_folder, "cmake/colors.cmake") - clang_tidy_config_path = os.path.join( - self.package_folder, "cmake/clang-tidy.conf") + OLD_CMAKE = Path(self.package_folder) / "cmake" / "deprecated" + BUILD_OUTPUTS_PATH = OLD_CMAKE / "build_outputs.cmake" + OPTIMIZE_DEBUG_BUILD_PATH = OLD_CMAKE / "optimize_debug_build.cmake" + BUILD_PATH = OLD_CMAKE / "build.cmake" + COLORS_PATH = OLD_CMAKE / "colors.cmake" + CLANG_TIDY_CONFIG_PATH = OLD_CMAKE / "clang-tidy.conf" if self.options.add_build_outputs: self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - build_outputs_path) + str(BUILD_OUTPUTS_PATH)) if self.options.optimize_debug_build: self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - optimize_debug_build_path) + str(OPTIMIZE_DEBUG_BUILD_PATH)) self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - colors_path) + str(COLORS_PATH)) self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - build_path) + str(BUILD_PATH)) self.output.info( - f"clang_tidy_config_path: {clang_tidy_config_path}") + f"CLANG_TIDY_CONFIG_PATH: {CLANG_TIDY_CONFIG_PATH}") self.output.info( f"add_build_outputs: {self.options.add_build_outputs}") self.output.info( diff --git a/examples/demos_CMakeLists.txt b/examples/demos_CMakeLists.txt new file mode 100644 index 0000000..4249fac --- /dev/null +++ b/examples/demos_CMakeLists.txt @@ -0,0 +1,70 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.15) + +# CMake helpers from libhal-cmake-helpers +# Source: https://github.com/libhal/libhal-cmake-helpers +find_package(LibhalBuild REQUIRED) + +# From libhal-cmake-helpers +libhal_project_init(demos) + +# ============================================================================== +# Demos - Option 1: Bulk demo builder +# ============================================================================== + +# From libhal-cmake-helpers +libhal_build_demos( + DEMOS + drc_v2 + drc_v2_resources + mc_x_v2 + mc_x_v2_v5 + mc_x_v2_resources + rc_servo + rc_servo16 + + INCLUDES + . + + PACKAGES + libhal-actuator + libhal-expander + + LINK_LIBRARIES + libhal::actuator + libhal::expander +) + +# ============================================================================== +# OR Demos - Option 2: Individual demo control +# ============================================================================== + +# From libhal-cmake-helpers +# libhal_add_executable(drc_v2 +# SOURCES demos/drc_v2.cpp +# INCLUDES . +# PACKAGES libhal-actuator libhal-expander +# LINK_LIBRARIES libhal::actuator libhal::expander +# ) +# +# libhal_add_executable(mc_x_v2 +# SOURCES demos/mc_x_v2.cpp +# INCLUDES . +# PACKAGES libhal-actuator libhal-expander +# LINK_LIBRARIES libhal::actuator libhal::expander +# ) +# +# # ... etc for other demos diff --git a/examples/libhal-actuator_CMakeLists.txt b/examples/libhal-actuator_CMakeLists.txt new file mode 100644 index 0000000..638a463 --- /dev/null +++ b/examples/libhal-actuator_CMakeLists.txt @@ -0,0 +1,94 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.15) + +# CMake helpers from libhal-cmake-helpers +# Source: https://github.com/libhal/libhal-cmake-helpers +find_package(LibhalBuild REQUIRED) + +# From libhal-cmake-helpers +libhal_project_init(libhal-actuator) + +# ============================================================================== +# Library - Option 1: Use the monolithic convenience function +# ============================================================================== + +# From libhal-cmake-helpers +libhal_test_and_make_library( + LIBRARY_NAME libhal-actuator + + SOURCES + src/rc_servo.cpp + src/smart_servo/rmd/drc_v2.cpp + src/smart_servo/rmd/mc_x_v2.cpp + + TEST_SOURCES + tests/main.test.cpp + tests/rc_servo.test.cpp + tests/smart_servo/rmd/drc.test.cpp + tests/smart_servo/rmd/mc_x.test.cpp + + PACKAGES + libhal-mock + + LINK_LIBRARIES + libhal::mock +) + +# ============================================================================== +# OR Library - Option 2: Granular approach +# ============================================================================== + +# From libhal-cmake-helpers +# libhal_add_library(libhal-actuator +# SOURCES +# src/rc_servo.cpp +# src/smart_servo/rmd/drc_v2.cpp +# src/smart_servo/rmd/mc_x_v2.cpp +# ) +# +# target_link_libraries(libhal-actuator PRIVATE libhal::compile_options) +# +# libhal_install_library(libhal-actuator) +# +# libhal_add_tests(libhal-actuator +# TEST_SOURCES +# tests/main.test.cpp +# tests/rc_servo.test.cpp +# tests/smart_servo/rmd/drc.test.cpp +# tests/smart_servo/rmd/mc_x.test.cpp +# PACKAGES libhal-mock +# LINK_LIBRARIES libhal::mock +# ) + +# ============================================================================== +# OR Library - Option 3: Quick convenience +# ============================================================================== + +# From libhal-cmake-helpers +# libhal_quick_library(libhal-actuator +# SOURCES +# src/rc_servo.cpp +# src/smart_servo/rmd/drc_v2.cpp +# src/smart_servo/rmd/mc_x_v2.cpp +# ) +# +# libhal_add_tests(libhal-actuator +# TEST_SOURCES +# tests/main.test.cpp +# tests/rc_servo.test.cpp +# PACKAGES libhal-mock +# LINK_LIBRARIES libhal::mock +# ) diff --git a/examples/strong_ptr_CMakeLists.txt b/examples/strong_ptr_CMakeLists.txt new file mode 100644 index 0000000..416c440 --- /dev/null +++ b/examples/strong_ptr_CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 4.0) + +# CMake helpers from libhal-cmake-helpers +# Source: https://github.com/libhal/libhal-cmake-helpers +find_package(LibhalBuild REQUIRED) + +# Initialize project (handles clang-tidy, compile flags setup, etc.) +# From libhal-cmake-helpers +libhal_project_init(strong_ptr) + +# ============================================================================== +# Library - Option 1: Granular approach (full control) +# ============================================================================== + +# From libhal-cmake-helpers +libhal_add_library(strong_ptr + MODULES modules/strong_ptr.cppm +) + +# Opt-in to libhal compile options +target_link_libraries(strong_ptr PRIVATE libhal::compile_options) + +# From libhal-cmake-helpers +libhal_install_library(strong_ptr + NAMESPACE libhal +) + +# ============================================================================== +# OR Library - Option 2: Convenience wrapper (opinionated) +# ============================================================================== + +# From libhal-cmake-helpers - combines the above into one call +# libhal_quick_library(strong_ptr +# MODULES modules/strong_ptr.cppm +# NAMESPACE libhal +# ) + +# ============================================================================== +# Unit Testing +# ============================================================================== + +# From libhal-cmake-helpers +libhal_add_tests(strong_ptr + TEST_NAMES + enable_from_this + strong_ptr + mixins + weak_ptr + optional_ptr + monotonic_allocator + UTIL_MODULE tests/util.cppm +) diff --git a/files/libhal-cmake-helpers/LICENSE b/files/libhal-cmake-helpers/LICENSE new file mode 100644 index 0000000..17cc909 --- /dev/null +++ b/files/libhal-cmake-helpers/LICENSE @@ -0,0 +1,17 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +Copyright 2024 - 2025 Khalil Estell and the libhal contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/files/libhal-cmake-helpers/MIGRATION.md b/files/libhal-cmake-helpers/MIGRATION.md new file mode 100644 index 0000000..bda2161 --- /dev/null +++ b/files/libhal-cmake-helpers/MIGRATION.md @@ -0,0 +1,353 @@ +# Migration Guide + +This guide helps you transition existing libhal projects to use libhal-cmake-helpers. + +## Before & After Comparison + +### Before (Manual CMake) + +```cmake +cmake_minimum_required(VERSION 4.0) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_COLOR_DIAGNOSTICS ON) +set(CMAKE_CXX_SCAN_FOR_MODULES ON) + +project(strong_ptr LANGUAGES CXX) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja|Visual Studio") + message(FATAL_ERROR "C++20 modules require Ninja or Visual Studio generator") +endif() + +option(LIBHAL_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) +option(LIBHAL_CLANG_TIDY_FIX "Apply clang-tidy fixes automatically" OFF) + +# ... 50 lines of clang-tidy setup ... + +add_library(libhal_compile_flags INTERFACE) +target_compile_options(libhal_compile_flags INTERFACE + $<$: + -g -Werror -Wall -Wextra -Wshadow -fexceptions -fno-rtti + -Wno-unused-command-line-argument -pedantic> + $<$: + /W4 /WX /EHsc /permissive- /GR-> +) + +add_library(strong_ptr STATIC) +add_library(strong_ptr::strong_ptr ALIAS strong_ptr) +target_compile_features(strong_ptr PUBLIC cxx_std_23) +target_sources(strong_ptr PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES modules/strong_ptr.cppm +) +target_compile_options(strong_ptr PRIVATE + $<$: ... > + $<$: ... > +) + +include(GNUInstallDirs) +install(TARGETS strong_ptr ...) +install(EXPORT strong_ptr_targets ...) + +# ... 40 lines of test setup ... +``` + +### After (With libhal-cmake-helpers) + +```cmake +cmake_minimum_required(VERSION 4.0) + +# CMake helpers from libhal-cmake-helpers +# Source: https://github.com/libhal/libhal-cmake-helpers +find_package(LibhalBuild REQUIRED) + +libhal_project_init(strong_ptr) + +libhal_add_library(strong_ptr + MODULES modules/strong_ptr.cppm +) + +target_link_libraries(strong_ptr PRIVATE libhal::compile_options) + +libhal_install_library(strong_ptr NAMESPACE libhal) + +libhal_add_tests(strong_ptr + TEST_NAMES enable_from_this strong_ptr mixins weak_ptr + UTIL_MODULE tests/util.cppm +) +``` + +**Reduction**: ~175 lines → ~25 lines (85% reduction) + +## Step-by-Step Migration + +### Step 1: Add Dependency + +Update your `conanfile.py` to require libhal-cmake-helpers: + +```python +def build_requirements(self): + self.tool_requires("libhal-cmake-helpers/[>=1.0.0]") +``` + +### Step 2: Replace Project Setup + +**Old:** +```cmake +cmake_minimum_required(VERSION 4.0) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_COLOR_DIAGNOSTICS ON) +set(CMAKE_CXX_SCAN_FOR_MODULES ON) + +project(my_lib LANGUAGES CXX) + +# Generator check +if(NOT CMAKE_GENERATOR MATCHES "Ninja|Visual Studio") + message(FATAL_ERROR "...") +endif() + +# Clang-tidy setup (50+ lines) +# ... +``` + +**New:** +```cmake +cmake_minimum_required(VERSION 4.0) + +find_package(LibhalBuild REQUIRED) +libhal_project_init(my_lib) +``` + +### Step 3: Replace Library Creation + +**Old:** +```cmake +add_library(my_lib STATIC) +add_library(my_lib::my_lib ALIAS my_lib) +target_compile_features(my_lib PUBLIC cxx_std_23) +target_sources(my_lib PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES modules/foo.cppm +) +target_compile_options(my_lib PRIVATE ...) +``` + +**New (granular):** +```cmake +libhal_add_library(my_lib + MODULES modules/foo.cppm +) +target_link_libraries(my_lib PRIVATE libhal::compile_options) +``` + +**New (convenience):** +```cmake +libhal_quick_library(my_lib + MODULES modules/foo.cppm + NAMESPACE libhal +) +``` + +### Step 4: Replace Installation + +**Old:** +```cmake +include(GNUInstallDirs) + +install( + TARGETS my_lib + EXPORT my_lib_targets + FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + CXX_MODULES_BMI DESTINATION "${CMAKE_INSTALL_LIBDIR}/bmi" +) + +install( + EXPORT my_lib_targets + FILE "my_lib-config.cmake" + NAMESPACE libhal:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/my_lib" + CXX_MODULES_DIRECTORY "cxx-modules" +) +``` + +**New:** +```cmake +libhal_install_library(my_lib NAMESPACE libhal) +``` + +### Step 5: Replace Test Setup + +**Old:** +```cmake +if(CMAKE_CROSSCOMPILING) + message(STATUS "Cross compiling, skipping unit test execution") +else() + include(CTest) + enable_testing() + + find_package(ut REQUIRED) + + set(TEST_NAMES foo bar baz) + + foreach(TEST_NAME IN LISTS TEST_NAMES) + set(TEST_TARGET "test_${TEST_NAME}") + add_executable(${TEST_TARGET}) + target_sources(${TEST_TARGET} PUBLIC + FILE_SET CXX_MODULES + TYPE CXX_MODULES + FILES tests/util.cppm + PRIVATE tests/${TEST_NAME}.test.cpp + ) + target_compile_features(${TEST_TARGET} PRIVATE cxx_std_23) + target_link_libraries(${TEST_TARGET} PRIVATE + Boost::ut + my_lib + libhal_compile_flags + libhal_asan + ) + add_test(NAME ${TEST_TARGET} COMMAND ${TEST_TARGET}) + endforeach() +endif() +``` + +**New:** +```cmake +libhal_add_tests(my_lib + TEST_NAMES foo bar baz + UTIL_MODULE tests/util.cppm +) +``` + +## Migration Strategies + +### Strategy 1: All at Once +Replace everything in one commit. Best for small projects. + +### Strategy 2: Incremental +1. Add libhal-cmake-helpers dependency +2. Replace project init first +3. Keep old library/test code initially +4. Migrate library creation next +5. Migrate tests last + +### Strategy 3: Hybrid Approach +Use granular functions initially, then switch to convenience wrappers once comfortable: + +```cmake +# Week 1: Granular approach (similar to old code) +libhal_add_library(my_lib ...) +target_link_libraries(my_lib PRIVATE libhal::compile_options) +libhal_install_library(my_lib) + +# Week 2+: Switch to convenience wrapper +libhal_quick_library(my_lib ...) +``` + +## Common Patterns + +### Pattern: Modules + Sources Mixed + +**Old:** +```cmake +add_library(my_lib STATIC) +target_sources(my_lib PUBLIC + FILE_SET CXX_MODULES TYPE CXX_MODULES FILES modules/foo.cppm + PRIVATE src/impl.cpp +) +``` + +**New:** +```cmake +libhal_add_library(my_lib + MODULES modules/foo.cppm + SOURCES src/impl.cpp +) +``` + +### Pattern: Custom Compile Definitions + +**Old:** +```cmake +target_compile_definitions(my_lib PUBLIC MY_DEFINE=1) +``` + +**New (same):** +```cmake +libhal_add_library(my_lib ...) +target_compile_definitions(my_lib PUBLIC MY_DEFINE=1) +``` + +The helpers don't prevent you from using standard CMake commands! + +### Pattern: Demo Applications + +**Old:** +```cmake +add_executable(demo1) +target_sources(demo1 PRIVATE demos/demo1.cpp) +target_link_libraries(demo1 PRIVATE libhal::util) +# ... repeat for each demo ... +``` + +**New:** +```cmake +libhal_build_demos( + DEMOS demo1 demo2 demo3 + PACKAGES libhal-util + LINK_LIBRARIES libhal::util +) +``` + +## Troubleshooting + +### "LibhalBuild not found" + +Make sure you've added to `conanfile.py`: +```python +def build_requirements(self): + self.tool_requires("libhal-cmake-helpers/[>=1.0.0]") +``` + +### "libhal::compile_options target not found" + +You need to call `libhal_project_init()` first: +```cmake +find_package(LibhalBuild REQUIRED) +libhal_project_init(my_project) # This creates the interface targets +``` + +### "I need more control" + +You can always drop down to raw CMake! The helpers are opt-in: + +```cmake +libhal_project_init(my_lib) # Only use this for project setup + +# Then use standard CMake +add_library(my_lib STATIC) +target_sources(my_lib ...) +# Full control from here +``` + +### "Compile options not being applied" + +Remember to link the interface target: +```cmake +target_link_libraries(my_lib PRIVATE libhal::compile_options) +``` + +Or use the convenience wrapper which does it automatically: +```cmake +libhal_quick_library(my_lib ...) +``` + +## Getting Help + +- **Documentation**: See [README.md](README.md) for full API reference +- **Examples**: Check the `examples/` directory +- **Issues**: https://github.com/libhal/libhal-cmake-helpers/issues diff --git a/files/libhal-cmake-helpers/PACKAGE_SUMMARY.md b/files/libhal-cmake-helpers/PACKAGE_SUMMARY.md new file mode 100644 index 0000000..a65623c --- /dev/null +++ b/files/libhal-cmake-helpers/PACKAGE_SUMMARY.md @@ -0,0 +1,136 @@ +# libhal-cmake-helpers Package Summary + +## What's Included + +This package provides CMake helpers for the libhal ecosystem with a **granular + convenience wrapper** design philosophy. + +### Core Files + +1. **CMakeLists.txt** - Main package build file +2. **conanfile.py** - Conan package configuration +3. **LICENSE** - Apache 2.0 license + +### CMake Modules (cmake/) + +1. **LibhalBuildConfig.cmake** - Main entry point (`find_package(LibhalBuild)`) +2. **LibhalCompileOptions.cmake** - Interface targets for compile flags +3. **LibhalClangTidy.cmake** - Clang-tidy integration +4. **LibhalLibrary.cmake** - Library creation and installation helpers +5. **LibhalTesting.cmake** - Test setup helpers +6. **LibhalExecutable.cmake** - Demo/executable helpers + +### Documentation + +1. **README.md** - Complete API documentation with examples +2. **MIGRATION.md** - Guide for transitioning existing projects + +### Examples (examples/) + +1. **strong_ptr_CMakeLists.txt** - C++20 modules library example +2. **libhal-actuator_CMakeLists.txt** - Traditional source library example +3. **demos_CMakeLists.txt** - Demo applications example + +## Key Features + +### 1. Granular Building Blocks + +```cmake +libhal_project_init(my_lib) +libhal_add_library(my_lib MODULES modules/foo.cppm) +target_link_libraries(my_lib PRIVATE libhal::compile_options) +libhal_install_library(my_lib NAMESPACE libhal) +``` + +### 2. Opt-in Interface Targets + +```cmake +# Choose your compile options +target_link_libraries(my_lib PRIVATE libhal::compile_options) +target_link_libraries(my_lib PRIVATE libhal::asan) +``` + +### 3. Convenience Wrappers + +```cmake +# One-line library creation + installation +libhal_quick_library(my_lib + MODULES modules/foo.cppm + NAMESPACE libhal +) + +# Bulk demo builder +libhal_build_demos( + DEMOS demo1 demo2 demo3 + PACKAGES libhal-util + LINK_LIBRARIES libhal::util +) +``` + +### 4. Legacy Pattern Support + +```cmake +# Keeps existing monolithic pattern working +libhal_test_and_make_library( + LIBRARY_NAME my_lib + SOURCES src/foo.cpp + TEST_SOURCES tests/foo.test.cpp +) +``` + +## Usage in Your Projects + +### 1. Add to conanfile.py + +```python +def build_requirements(self): + self.tool_requires("libhal-cmake-helpers/[>=1.0.0]") +``` + +### 2. Update CMakeLists.txt + +**Minimal (175 lines → 25 lines):** +```cmake +cmake_minimum_required(VERSION 4.0) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(my_lib) +libhal_quick_library(my_lib MODULES modules/foo.cppm NAMESPACE libhal) +libhal_add_tests(my_lib TEST_NAMES foo bar) +``` + +**Granular (full control):** +```cmake +cmake_minimum_required(VERSION 4.0) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(my_lib) + +libhal_add_library(my_lib MODULES modules/foo.cppm) +target_link_libraries(my_lib PRIVATE libhal::compile_options) +libhal_install_library(my_lib NAMESPACE libhal) + +libhal_add_tests(my_lib TEST_NAMES foo bar) +``` + +## Benefits + +✅ **Reduces boilerplate** - 85% reduction in CMakeLists.txt size +✅ **Clear and explicit** - Comments show what comes from helpers +✅ **Single source of truth** - Update helpers package, all repos benefit +✅ **Flexible** - Choose granular or convenience based on needs +✅ **Opt-in everything** - Interface targets for compile options +✅ **Backward compatible** - Keeps existing patterns working +✅ **Well documented** - README + MIGRATION guide + examples + +## Next Steps + +1. **Review** the README.md for complete API documentation +2. **Check** examples/ for real-world usage patterns +3. **Read** MIGRATION.md for transition guidance +4. **Publish** to Conan repository +5. **Update** libhal repos to use the helpers + +## Questions? + +See README.md or file an issue at: +https://github.com/libhal/libhal-cmake-helpers/issues diff --git a/files/libhal-cmake-helpers/README.md b/files/libhal-cmake-helpers/README.md new file mode 100644 index 0000000..4f95323 --- /dev/null +++ b/files/libhal-cmake-helpers/README.md @@ -0,0 +1,350 @@ +# libhal-cmake-helpers + +CMake helper functions and utilities for libhal projects. Provides both granular building blocks and convenient wrapper functions for common patterns. + +## Installation + +Via Conan: + +```bash +conan install libhal-cmake-helpers/1.0.0@ +``` + +## Quick Start + +```cmake +cmake_minimum_required(VERSION 4.0) + +# Find the helpers package +# Source: https://github.com/libhal/libhal-cmake-helpers +find_package(LibhalBuild REQUIRED) + +# Initialize your project (required) +libhal_project_init(my_library) + +# Option 1: Use convenience wrapper +libhal_quick_library(my_library + SOURCES src/foo.cpp src/bar.cpp + MODULES modules/baz.cppm + NAMESPACE libhal +) + +# Option 2: Granular control +libhal_add_library(my_library + SOURCES src/foo.cpp + MODULES modules/bar.cppm +) +target_link_libraries(my_library PRIVATE libhal::compile_options) +libhal_install_library(my_library NAMESPACE libhal) + +# Add tests +libhal_add_tests(my_library + TEST_NAMES foo bar baz + UTIL_MODULE tests/util.cppm +) +``` + +## Core Functions + +### `libhal_project_init(PROJECT_NAME)` + +**Required** - Sets up project-level configuration. This is the only mandatory function. + +What it does: +- Calls `project()` with C++ language +- Enables compile_commands.json export +- Checks for Ninja/Visual Studio generator (required for modules) +- Sets up clang-tidy if enabled +- Creates `libhal::compile_options` and `libhal::asan` interface targets +- Adds compile_commands.json copy target + +```cmake +libhal_project_init(my_project) +``` + +### Interface Targets + +After `libhal_project_init()`, these targets are available: + +#### `libhal::compile_options` +Standard compile flags for libhal projects. **Opt-in** via linking: + +```cmake +target_link_libraries(my_lib PRIVATE libhal::compile_options) +``` + +Flags included: +- GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -Wpedantic -fexceptions -fno-rtti` +- MSVC: `/W4 /WX /EHsc /permissive- /GR-` + +#### `libhal::asan` +AddressSanitizer support (non-Windows only): + +```cmake +target_link_libraries(my_lib PRIVATE libhal::asan) +``` + +## Library Functions + +### Granular Functions + +#### `libhal_add_library(TARGET_NAME)` +Creates a static library with optional sources and modules. + +```cmake +libhal_add_library(my_lib + SOURCES src/foo.cpp src/bar.cpp + MODULES modules/baz.cppm modules/qux.cppm +) +``` + +Arguments: +- `SOURCES` - List of .cpp files +- `MODULES` - List of .cppm module files + +#### `libhal_install_library(TARGET_NAME)` +Configures library installation with CMake config files. + +```cmake +libhal_install_library(my_lib NAMESPACE libhal) +``` + +Arguments: +- `NAMESPACE` (optional) - Namespace for exported target (default: library name) + +### Convenience Functions + +#### `libhal_quick_library(TARGET_NAME)` +One-shot library creation, configuration, and installation. + +```cmake +libhal_quick_library(strong_ptr + MODULES modules/strong_ptr.cppm + NAMESPACE libhal +) +``` + +Automatically applies: +- `libhal::compile_options` +- `libhal::asan` (when not cross-compiling) +- Installs with CMake config + +#### `libhal_test_and_make_library()` +Monolithic function combining library and tests (legacy pattern). + +```cmake +libhal_test_and_make_library( + LIBRARY_NAME libhal-actuator + SOURCES src/rc_servo.cpp src/drc_v2.cpp + TEST_SOURCES tests/rc_servo.test.cpp tests/drc.test.cpp + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock +) +``` + +## Testing Functions + +### `libhal_add_tests(TARGET_NAME)` + +Adds unit tests for a library. Supports two modes: + +**Mode 1: Explicit test file list** +```cmake +libhal_add_tests(my_lib + TEST_SOURCES tests/foo.test.cpp tests/bar.test.cpp + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock +) +``` + +**Mode 2: Generate filenames from names** +```cmake +libhal_add_tests(my_lib + TEST_NAMES foo bar baz + UTIL_MODULE tests/util.cppm +) +``` +Looks for `tests/foo.test.cpp`, `tests/bar.test.cpp`, etc. + +Arguments: +- `TEST_SOURCES` - Explicit list of test files +- `TEST_NAMES` - Generate test filenames (tests/NAME.test.cpp) +- `UTIL_MODULE` - Optional utility module for tests +- `PACKAGES` - Additional packages to find +- `LINK_LIBRARIES` - Additional libraries to link + +## Executable/Demo Functions + +### `libhal_add_executable(TARGET_NAME)` + +Creates a single executable/demo. + +```cmake +libhal_add_executable(my_demo + SOURCES demos/my_demo.cpp src/common.cpp + INCLUDES include/ + PACKAGES libhal-util libhal-lpc40 + LINK_LIBRARIES libhal::util libhal::lpc40 +) +``` + +### `libhal_build_demos()` + +Builds multiple demos at once. + +```cmake +libhal_build_demos( + DEMOS demo1 demo2 demo3 + SOURCES src/common.cpp + INCLUDES include/ + PACKAGES libhal-util libhal-expander + LINK_LIBRARIES libhal::util libhal::expander +) +``` + +Looks for `demos/demo1.cpp`, `demos/demo2.cpp`, etc. + +## Clang-tidy + +Enable via CMake options: + +```bash +# Enable clang-tidy checks +cmake -DLIBHAL_ENABLE_CLANG_TIDY=ON .. + +# Enable with automatic fixes +cmake -DLIBHAL_CLANG_TIDY_FIX=ON .. +``` + +Or via Conan: + +```bash +conan install . -o enable_clang_tidy=True +conan install . -o clang_tidy_fix=True +``` + +## Complete Examples + +### Example 1: C++20 Modules Library (strong_ptr) + +```cmake +cmake_minimum_required(VERSION 4.0) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(strong_ptr) + +# Create library with modules +libhal_add_library(strong_ptr + MODULES modules/strong_ptr.cppm +) + +# Opt-in to compile options +target_link_libraries(strong_ptr PRIVATE libhal::compile_options) + +# 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 + UTIL_MODULE tests/util.cppm +) +``` + +### Example 2: Traditional Source Library (libhal-actuator) + +```cmake +cmake_minimum_required(VERSION 3.15) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(libhal-actuator) + +# Quick library with auto-install +libhal_quick_library(libhal-actuator + SOURCES + src/rc_servo.cpp + src/smart_servo/rmd/drc_v2.cpp + src/smart_servo/rmd/mc_x_v2.cpp +) + +# Add tests +libhal_add_tests(libhal-actuator + TEST_SOURCES + tests/main.test.cpp + tests/rc_servo.test.cpp + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock +) +``` + +### Example 3: Demo Applications + +```cmake +cmake_minimum_required(VERSION 3.15) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(demos) + +libhal_build_demos( + DEMOS + drc_v2 + mc_x_v2 + rc_servo + INCLUDES . + PACKAGES + libhal-actuator + libhal-expander + LINK_LIBRARIES + libhal::actuator + libhal::expander +) +``` + +### Example 4: Full Manual Control + +```cmake +cmake_minimum_required(VERSION 4.0) +find_package(LibhalBuild REQUIRED) + +# Initialize project +libhal_project_init(my_advanced_lib) + +# 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_link_libraries(my_advanced_lib PRIVATE + libhal::compile_options + libhal::asan +) + +# Custom compile definitions +target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG) + +# Install +libhal_install_library(my_advanced_lib NAMESPACE mycompany) +``` + +## Philosophy + +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 interface targets +- **Explicit, not magical** - Clear what's happening +- **Composable** - Mix and match as needed + +## License + +Apache-2.0 + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/files/libhal-cmake-helpers/conanfile.py b/files/libhal-cmake-helpers/conanfile.py new file mode 100644 index 0000000..b15380c --- /dev/null +++ b/files/libhal-cmake-helpers/conanfile.py @@ -0,0 +1,58 @@ +# Copyright 2024 - 2025 Khalil Estell and the libhal contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout +from conan.tools.files import copy + + +class LibhalCMakeHelpersConan(ConanFile): + name = "libhal-cmake-helpers" + version = "1.0.0" + license = "Apache-2.0" + author = "Khalil Estell and the libhal contributors" + url = "https://github.com/libhal/libhal-cmake-helpers" + description = "CMake helper functions and utilities for libhal projects" + topics = ("cmake", "build-helpers", "libhal", "embedded") + + settings = "os", "compiler", "build_type", "arch" + + exports_sources = "cmake/*", "CMakeLists.txt", "LICENSE" + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + # Also copy license + copy(self, "LICENSE", + src=self.source_folder, + dst=self.package_folder) + + def package_info(self): + # This is a build-time only package + self.cpp_info.set_property("cmake_find_mode", "both") + self.cpp_info.set_property("cmake_file_name", "LibhalBuild") + self.cpp_info.builddirs = ["lib/cmake/LibhalBuild"] + + def package_id(self): + # This is a header-only/build-tool package, no binary compatibility + self.info.clear() From 52328319e2053e9c819defa279be5728847f524f Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Sun, 8 Feb 2026 15:35:31 -0800 Subject: [PATCH 2/6] Split into v4 and v5 --- files/libhal-cmake-helpers/LICENSE | 17 - files/libhal-cmake-helpers/MIGRATION.md | 353 ------------------ files/libhal-cmake-helpers/PACKAGE_SUMMARY.md | 136 ------- {cmake/deprecated => v4/cmake}/build.cmake | 0 .../cmake}/build_outputs.cmake | 0 .../deprecated => v4/cmake}/clang-tidy.conf | 0 {cmake/deprecated => v4/cmake}/colors.cmake | 0 .../cmake}/optimize_debug_build.cmake | 0 conanfile.py => v4/conanfile.py | 35 +- CMakeLists.txt => v5/CMakeLists.txt | 1 + {files/libhal-cmake-helpers => v5}/README.md | 0 {cmake => v5/cmake}/LibhalBinUtils.cmake | 0 {cmake => v5/cmake}/LibhalBuildConfig.cmake | 0 {cmake => v5/cmake}/LibhalClangTidy.cmake | 0 .../cmake}/LibhalCompileOptions.cmake | 0 {cmake => v5/cmake}/LibhalExecutable.cmake | 0 {cmake => v5/cmake}/LibhalLibrary.cmake | 0 {cmake => v5/cmake}/LibhalTerminalColor.cmake | 0 {cmake => v5/cmake}/LibhalTesting.cmake | 0 .../libhal-cmake-helpers => v5}/conanfile.py | 25 +- .../examples}/demos_CMakeLists.txt | 0 .../examples}/libhal-actuator_CMakeLists.txt | 0 .../examples}/strong_ptr_CMakeLists.txt | 0 23 files changed, 28 insertions(+), 539 deletions(-) delete mode 100644 files/libhal-cmake-helpers/LICENSE delete mode 100644 files/libhal-cmake-helpers/MIGRATION.md delete mode 100644 files/libhal-cmake-helpers/PACKAGE_SUMMARY.md rename {cmake/deprecated => v4/cmake}/build.cmake (100%) rename {cmake/deprecated => v4/cmake}/build_outputs.cmake (100%) rename {cmake/deprecated => v4/cmake}/clang-tidy.conf (100%) rename {cmake/deprecated => v4/cmake}/colors.cmake (100%) rename {cmake/deprecated => v4/cmake}/optimize_debug_build.cmake (100%) rename conanfile.py => v4/conanfile.py (72%) rename CMakeLists.txt => v5/CMakeLists.txt (97%) rename {files/libhal-cmake-helpers => v5}/README.md (100%) rename {cmake => v5/cmake}/LibhalBinUtils.cmake (100%) rename {cmake => v5/cmake}/LibhalBuildConfig.cmake (100%) rename {cmake => v5/cmake}/LibhalClangTidy.cmake (100%) rename {cmake => v5/cmake}/LibhalCompileOptions.cmake (100%) rename {cmake => v5/cmake}/LibhalExecutable.cmake (100%) rename {cmake => v5/cmake}/LibhalLibrary.cmake (100%) rename {cmake => v5/cmake}/LibhalTerminalColor.cmake (100%) rename {cmake => v5/cmake}/LibhalTesting.cmake (100%) rename {files/libhal-cmake-helpers => v5}/conanfile.py (88%) rename {examples => v5/examples}/demos_CMakeLists.txt (100%) rename {examples => v5/examples}/libhal-actuator_CMakeLists.txt (100%) rename {examples => v5/examples}/strong_ptr_CMakeLists.txt (100%) diff --git a/files/libhal-cmake-helpers/LICENSE b/files/libhal-cmake-helpers/LICENSE deleted file mode 100644 index 17cc909..0000000 --- a/files/libhal-cmake-helpers/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -Copyright 2024 - 2025 Khalil Estell and the libhal contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/files/libhal-cmake-helpers/MIGRATION.md b/files/libhal-cmake-helpers/MIGRATION.md deleted file mode 100644 index bda2161..0000000 --- a/files/libhal-cmake-helpers/MIGRATION.md +++ /dev/null @@ -1,353 +0,0 @@ -# Migration Guide - -This guide helps you transition existing libhal projects to use libhal-cmake-helpers. - -## Before & After Comparison - -### Before (Manual CMake) - -```cmake -cmake_minimum_required(VERSION 4.0) - -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_COLOR_DIAGNOSTICS ON) -set(CMAKE_CXX_SCAN_FOR_MODULES ON) - -project(strong_ptr LANGUAGES CXX) - -if(NOT CMAKE_GENERATOR MATCHES "Ninja|Visual Studio") - message(FATAL_ERROR "C++20 modules require Ninja or Visual Studio generator") -endif() - -option(LIBHAL_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) -option(LIBHAL_CLANG_TIDY_FIX "Apply clang-tidy fixes automatically" OFF) - -# ... 50 lines of clang-tidy setup ... - -add_library(libhal_compile_flags INTERFACE) -target_compile_options(libhal_compile_flags INTERFACE - $<$: - -g -Werror -Wall -Wextra -Wshadow -fexceptions -fno-rtti - -Wno-unused-command-line-argument -pedantic> - $<$: - /W4 /WX /EHsc /permissive- /GR-> -) - -add_library(strong_ptr STATIC) -add_library(strong_ptr::strong_ptr ALIAS strong_ptr) -target_compile_features(strong_ptr PUBLIC cxx_std_23) -target_sources(strong_ptr PUBLIC - FILE_SET CXX_MODULES - TYPE CXX_MODULES - FILES modules/strong_ptr.cppm -) -target_compile_options(strong_ptr PRIVATE - $<$: ... > - $<$: ... > -) - -include(GNUInstallDirs) -install(TARGETS strong_ptr ...) -install(EXPORT strong_ptr_targets ...) - -# ... 40 lines of test setup ... -``` - -### After (With libhal-cmake-helpers) - -```cmake -cmake_minimum_required(VERSION 4.0) - -# CMake helpers from libhal-cmake-helpers -# Source: https://github.com/libhal/libhal-cmake-helpers -find_package(LibhalBuild REQUIRED) - -libhal_project_init(strong_ptr) - -libhal_add_library(strong_ptr - MODULES modules/strong_ptr.cppm -) - -target_link_libraries(strong_ptr PRIVATE libhal::compile_options) - -libhal_install_library(strong_ptr NAMESPACE libhal) - -libhal_add_tests(strong_ptr - TEST_NAMES enable_from_this strong_ptr mixins weak_ptr - UTIL_MODULE tests/util.cppm -) -``` - -**Reduction**: ~175 lines → ~25 lines (85% reduction) - -## Step-by-Step Migration - -### Step 1: Add Dependency - -Update your `conanfile.py` to require libhal-cmake-helpers: - -```python -def build_requirements(self): - self.tool_requires("libhal-cmake-helpers/[>=1.0.0]") -``` - -### Step 2: Replace Project Setup - -**Old:** -```cmake -cmake_minimum_required(VERSION 4.0) - -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_COLOR_DIAGNOSTICS ON) -set(CMAKE_CXX_SCAN_FOR_MODULES ON) - -project(my_lib LANGUAGES CXX) - -# Generator check -if(NOT CMAKE_GENERATOR MATCHES "Ninja|Visual Studio") - message(FATAL_ERROR "...") -endif() - -# Clang-tidy setup (50+ lines) -# ... -``` - -**New:** -```cmake -cmake_minimum_required(VERSION 4.0) - -find_package(LibhalBuild REQUIRED) -libhal_project_init(my_lib) -``` - -### Step 3: Replace Library Creation - -**Old:** -```cmake -add_library(my_lib STATIC) -add_library(my_lib::my_lib ALIAS my_lib) -target_compile_features(my_lib PUBLIC cxx_std_23) -target_sources(my_lib PUBLIC - FILE_SET CXX_MODULES - TYPE CXX_MODULES - FILES modules/foo.cppm -) -target_compile_options(my_lib PRIVATE ...) -``` - -**New (granular):** -```cmake -libhal_add_library(my_lib - MODULES modules/foo.cppm -) -target_link_libraries(my_lib PRIVATE libhal::compile_options) -``` - -**New (convenience):** -```cmake -libhal_quick_library(my_lib - MODULES modules/foo.cppm - NAMESPACE libhal -) -``` - -### Step 4: Replace Installation - -**Old:** -```cmake -include(GNUInstallDirs) - -install( - TARGETS my_lib - EXPORT my_lib_targets - FILE_SET CXX_MODULES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - CXX_MODULES_BMI DESTINATION "${CMAKE_INSTALL_LIBDIR}/bmi" -) - -install( - EXPORT my_lib_targets - FILE "my_lib-config.cmake" - NAMESPACE libhal:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/my_lib" - CXX_MODULES_DIRECTORY "cxx-modules" -) -``` - -**New:** -```cmake -libhal_install_library(my_lib NAMESPACE libhal) -``` - -### Step 5: Replace Test Setup - -**Old:** -```cmake -if(CMAKE_CROSSCOMPILING) - message(STATUS "Cross compiling, skipping unit test execution") -else() - include(CTest) - enable_testing() - - find_package(ut REQUIRED) - - set(TEST_NAMES foo bar baz) - - foreach(TEST_NAME IN LISTS TEST_NAMES) - set(TEST_TARGET "test_${TEST_NAME}") - add_executable(${TEST_TARGET}) - target_sources(${TEST_TARGET} PUBLIC - FILE_SET CXX_MODULES - TYPE CXX_MODULES - FILES tests/util.cppm - PRIVATE tests/${TEST_NAME}.test.cpp - ) - target_compile_features(${TEST_TARGET} PRIVATE cxx_std_23) - target_link_libraries(${TEST_TARGET} PRIVATE - Boost::ut - my_lib - libhal_compile_flags - libhal_asan - ) - add_test(NAME ${TEST_TARGET} COMMAND ${TEST_TARGET}) - endforeach() -endif() -``` - -**New:** -```cmake -libhal_add_tests(my_lib - TEST_NAMES foo bar baz - UTIL_MODULE tests/util.cppm -) -``` - -## Migration Strategies - -### Strategy 1: All at Once -Replace everything in one commit. Best for small projects. - -### Strategy 2: Incremental -1. Add libhal-cmake-helpers dependency -2. Replace project init first -3. Keep old library/test code initially -4. Migrate library creation next -5. Migrate tests last - -### Strategy 3: Hybrid Approach -Use granular functions initially, then switch to convenience wrappers once comfortable: - -```cmake -# Week 1: Granular approach (similar to old code) -libhal_add_library(my_lib ...) -target_link_libraries(my_lib PRIVATE libhal::compile_options) -libhal_install_library(my_lib) - -# Week 2+: Switch to convenience wrapper -libhal_quick_library(my_lib ...) -``` - -## Common Patterns - -### Pattern: Modules + Sources Mixed - -**Old:** -```cmake -add_library(my_lib STATIC) -target_sources(my_lib PUBLIC - FILE_SET CXX_MODULES TYPE CXX_MODULES FILES modules/foo.cppm - PRIVATE src/impl.cpp -) -``` - -**New:** -```cmake -libhal_add_library(my_lib - MODULES modules/foo.cppm - SOURCES src/impl.cpp -) -``` - -### Pattern: Custom Compile Definitions - -**Old:** -```cmake -target_compile_definitions(my_lib PUBLIC MY_DEFINE=1) -``` - -**New (same):** -```cmake -libhal_add_library(my_lib ...) -target_compile_definitions(my_lib PUBLIC MY_DEFINE=1) -``` - -The helpers don't prevent you from using standard CMake commands! - -### Pattern: Demo Applications - -**Old:** -```cmake -add_executable(demo1) -target_sources(demo1 PRIVATE demos/demo1.cpp) -target_link_libraries(demo1 PRIVATE libhal::util) -# ... repeat for each demo ... -``` - -**New:** -```cmake -libhal_build_demos( - DEMOS demo1 demo2 demo3 - PACKAGES libhal-util - LINK_LIBRARIES libhal::util -) -``` - -## Troubleshooting - -### "LibhalBuild not found" - -Make sure you've added to `conanfile.py`: -```python -def build_requirements(self): - self.tool_requires("libhal-cmake-helpers/[>=1.0.0]") -``` - -### "libhal::compile_options target not found" - -You need to call `libhal_project_init()` first: -```cmake -find_package(LibhalBuild REQUIRED) -libhal_project_init(my_project) # This creates the interface targets -``` - -### "I need more control" - -You can always drop down to raw CMake! The helpers are opt-in: - -```cmake -libhal_project_init(my_lib) # Only use this for project setup - -# Then use standard CMake -add_library(my_lib STATIC) -target_sources(my_lib ...) -# Full control from here -``` - -### "Compile options not being applied" - -Remember to link the interface target: -```cmake -target_link_libraries(my_lib PRIVATE libhal::compile_options) -``` - -Or use the convenience wrapper which does it automatically: -```cmake -libhal_quick_library(my_lib ...) -``` - -## Getting Help - -- **Documentation**: See [README.md](README.md) for full API reference -- **Examples**: Check the `examples/` directory -- **Issues**: https://github.com/libhal/libhal-cmake-helpers/issues diff --git a/files/libhal-cmake-helpers/PACKAGE_SUMMARY.md b/files/libhal-cmake-helpers/PACKAGE_SUMMARY.md deleted file mode 100644 index a65623c..0000000 --- a/files/libhal-cmake-helpers/PACKAGE_SUMMARY.md +++ /dev/null @@ -1,136 +0,0 @@ -# libhal-cmake-helpers Package Summary - -## What's Included - -This package provides CMake helpers for the libhal ecosystem with a **granular + convenience wrapper** design philosophy. - -### Core Files - -1. **CMakeLists.txt** - Main package build file -2. **conanfile.py** - Conan package configuration -3. **LICENSE** - Apache 2.0 license - -### CMake Modules (cmake/) - -1. **LibhalBuildConfig.cmake** - Main entry point (`find_package(LibhalBuild)`) -2. **LibhalCompileOptions.cmake** - Interface targets for compile flags -3. **LibhalClangTidy.cmake** - Clang-tidy integration -4. **LibhalLibrary.cmake** - Library creation and installation helpers -5. **LibhalTesting.cmake** - Test setup helpers -6. **LibhalExecutable.cmake** - Demo/executable helpers - -### Documentation - -1. **README.md** - Complete API documentation with examples -2. **MIGRATION.md** - Guide for transitioning existing projects - -### Examples (examples/) - -1. **strong_ptr_CMakeLists.txt** - C++20 modules library example -2. **libhal-actuator_CMakeLists.txt** - Traditional source library example -3. **demos_CMakeLists.txt** - Demo applications example - -## Key Features - -### 1. Granular Building Blocks - -```cmake -libhal_project_init(my_lib) -libhal_add_library(my_lib MODULES modules/foo.cppm) -target_link_libraries(my_lib PRIVATE libhal::compile_options) -libhal_install_library(my_lib NAMESPACE libhal) -``` - -### 2. Opt-in Interface Targets - -```cmake -# Choose your compile options -target_link_libraries(my_lib PRIVATE libhal::compile_options) -target_link_libraries(my_lib PRIVATE libhal::asan) -``` - -### 3. Convenience Wrappers - -```cmake -# One-line library creation + installation -libhal_quick_library(my_lib - MODULES modules/foo.cppm - NAMESPACE libhal -) - -# Bulk demo builder -libhal_build_demos( - DEMOS demo1 demo2 demo3 - PACKAGES libhal-util - LINK_LIBRARIES libhal::util -) -``` - -### 4. Legacy Pattern Support - -```cmake -# Keeps existing monolithic pattern working -libhal_test_and_make_library( - LIBRARY_NAME my_lib - SOURCES src/foo.cpp - TEST_SOURCES tests/foo.test.cpp -) -``` - -## Usage in Your Projects - -### 1. Add to conanfile.py - -```python -def build_requirements(self): - self.tool_requires("libhal-cmake-helpers/[>=1.0.0]") -``` - -### 2. Update CMakeLists.txt - -**Minimal (175 lines → 25 lines):** -```cmake -cmake_minimum_required(VERSION 4.0) -find_package(LibhalBuild REQUIRED) - -libhal_project_init(my_lib) -libhal_quick_library(my_lib MODULES modules/foo.cppm NAMESPACE libhal) -libhal_add_tests(my_lib TEST_NAMES foo bar) -``` - -**Granular (full control):** -```cmake -cmake_minimum_required(VERSION 4.0) -find_package(LibhalBuild REQUIRED) - -libhal_project_init(my_lib) - -libhal_add_library(my_lib MODULES modules/foo.cppm) -target_link_libraries(my_lib PRIVATE libhal::compile_options) -libhal_install_library(my_lib NAMESPACE libhal) - -libhal_add_tests(my_lib TEST_NAMES foo bar) -``` - -## Benefits - -✅ **Reduces boilerplate** - 85% reduction in CMakeLists.txt size -✅ **Clear and explicit** - Comments show what comes from helpers -✅ **Single source of truth** - Update helpers package, all repos benefit -✅ **Flexible** - Choose granular or convenience based on needs -✅ **Opt-in everything** - Interface targets for compile options -✅ **Backward compatible** - Keeps existing patterns working -✅ **Well documented** - README + MIGRATION guide + examples - -## Next Steps - -1. **Review** the README.md for complete API documentation -2. **Check** examples/ for real-world usage patterns -3. **Read** MIGRATION.md for transition guidance -4. **Publish** to Conan repository -5. **Update** libhal repos to use the helpers - -## Questions? - -See README.md or file an issue at: -https://github.com/libhal/libhal-cmake-helpers/issues diff --git a/cmake/deprecated/build.cmake b/v4/cmake/build.cmake similarity index 100% rename from cmake/deprecated/build.cmake rename to v4/cmake/build.cmake diff --git a/cmake/deprecated/build_outputs.cmake b/v4/cmake/build_outputs.cmake similarity index 100% rename from cmake/deprecated/build_outputs.cmake rename to v4/cmake/build_outputs.cmake diff --git a/cmake/deprecated/clang-tidy.conf b/v4/cmake/clang-tidy.conf similarity index 100% rename from cmake/deprecated/clang-tidy.conf rename to v4/cmake/clang-tidy.conf diff --git a/cmake/deprecated/colors.cmake b/v4/cmake/colors.cmake similarity index 100% rename from cmake/deprecated/colors.cmake rename to v4/cmake/colors.cmake diff --git a/cmake/deprecated/optimize_debug_build.cmake b/v4/cmake/optimize_debug_build.cmake similarity index 100% rename from cmake/deprecated/optimize_debug_build.cmake rename to v4/cmake/optimize_debug_build.cmake diff --git a/conanfile.py b/v4/conanfile.py similarity index 72% rename from conanfile.py rename to v4/conanfile.py index 10046ee..b4752d3 100644 --- a/conanfile.py +++ b/v4/conanfile.py @@ -16,7 +16,6 @@ from conan.tools.files import copy from conan.tools.layout import basic_layout import os -from pathlib import Path required_conan_version = ">=2.0.6" @@ -57,43 +56,39 @@ def package(self): dst=self.package_folder) def package_info(self): - # ====================================================================== - # DEPRECATED part of package_info - # - # NOTE: It is not advised to continue using these cmake scripts and - # their functions, it is recommended to use the `LibhalBuild` package - # instead. - # ====================================================================== - # Add toolchain.cmake to user_toolchain configuration info to be used # by CMakeToolchain generator - OLD_CMAKE = Path(self.package_folder) / "cmake" / "deprecated" - BUILD_OUTPUTS_PATH = OLD_CMAKE / "build_outputs.cmake" - OPTIMIZE_DEBUG_BUILD_PATH = OLD_CMAKE / "optimize_debug_build.cmake" - BUILD_PATH = OLD_CMAKE / "build.cmake" - COLORS_PATH = OLD_CMAKE / "colors.cmake" - CLANG_TIDY_CONFIG_PATH = OLD_CMAKE / "clang-tidy.conf" + build_outputs_path = os.path.join( + self.package_folder, "cmake/build_outputs.cmake") + optimize_debug_build_path = os.path.join( + self.package_folder, "cmake/optimize_debug_build.cmake") + build_path = os.path.join( + self.package_folder, "cmake/build.cmake") + colors_path = os.path.join( + self.package_folder, "cmake/colors.cmake") + clang_tidy_config_path = os.path.join( + self.package_folder, "cmake/clang-tidy.conf") if self.options.add_build_outputs: self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - str(BUILD_OUTPUTS_PATH)) + build_outputs_path) if self.options.optimize_debug_build: self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - str(OPTIMIZE_DEBUG_BUILD_PATH)) + optimize_debug_build_path) self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - str(COLORS_PATH)) + colors_path) self.conf_info.append( "tools.cmake.cmaketoolchain:user_toolchain", - str(BUILD_PATH)) + build_path) self.output.info( - f"CLANG_TIDY_CONFIG_PATH: {CLANG_TIDY_CONFIG_PATH}") + f"clang_tidy_config_path: {clang_tidy_config_path}") self.output.info( f"add_build_outputs: {self.options.add_build_outputs}") self.output.info( diff --git a/CMakeLists.txt b/v5/CMakeLists.txt similarity index 97% rename from CMakeLists.txt rename to v5/CMakeLists.txt index 3cae309..879d7bc 100644 --- a/CMakeLists.txt +++ b/v5/CMakeLists.txt @@ -27,6 +27,7 @@ install( cmake/LibhalExecutable.cmake cmake/LibhalTesting.cmake cmake/LibhalClangTidy.cmake + cmake/LibhalBinUtils.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalBuild ) diff --git a/files/libhal-cmake-helpers/README.md b/v5/README.md similarity index 100% rename from files/libhal-cmake-helpers/README.md rename to v5/README.md diff --git a/cmake/LibhalBinUtils.cmake b/v5/cmake/LibhalBinUtils.cmake similarity index 100% rename from cmake/LibhalBinUtils.cmake rename to v5/cmake/LibhalBinUtils.cmake diff --git a/cmake/LibhalBuildConfig.cmake b/v5/cmake/LibhalBuildConfig.cmake similarity index 100% rename from cmake/LibhalBuildConfig.cmake rename to v5/cmake/LibhalBuildConfig.cmake diff --git a/cmake/LibhalClangTidy.cmake b/v5/cmake/LibhalClangTidy.cmake similarity index 100% rename from cmake/LibhalClangTidy.cmake rename to v5/cmake/LibhalClangTidy.cmake diff --git a/cmake/LibhalCompileOptions.cmake b/v5/cmake/LibhalCompileOptions.cmake similarity index 100% rename from cmake/LibhalCompileOptions.cmake rename to v5/cmake/LibhalCompileOptions.cmake diff --git a/cmake/LibhalExecutable.cmake b/v5/cmake/LibhalExecutable.cmake similarity index 100% rename from cmake/LibhalExecutable.cmake rename to v5/cmake/LibhalExecutable.cmake diff --git a/cmake/LibhalLibrary.cmake b/v5/cmake/LibhalLibrary.cmake similarity index 100% rename from cmake/LibhalLibrary.cmake rename to v5/cmake/LibhalLibrary.cmake diff --git a/cmake/LibhalTerminalColor.cmake b/v5/cmake/LibhalTerminalColor.cmake similarity index 100% rename from cmake/LibhalTerminalColor.cmake rename to v5/cmake/LibhalTerminalColor.cmake diff --git a/cmake/LibhalTesting.cmake b/v5/cmake/LibhalTesting.cmake similarity index 100% rename from cmake/LibhalTesting.cmake rename to v5/cmake/LibhalTesting.cmake diff --git a/files/libhal-cmake-helpers/conanfile.py b/v5/conanfile.py similarity index 88% rename from files/libhal-cmake-helpers/conanfile.py rename to v5/conanfile.py index b15380c..0ced7c3 100644 --- a/files/libhal-cmake-helpers/conanfile.py +++ b/v5/conanfile.py @@ -15,44 +15,43 @@ from conan import ConanFile from conan.tools.cmake import CMake, cmake_layout from conan.tools.files import copy +from pathlib import Path class LibhalCMakeHelpersConan(ConanFile): - name = "libhal-cmake-helpers" - version = "1.0.0" + name = "libhal-cmake-util" license = "Apache-2.0" author = "Khalil Estell and the libhal contributors" url = "https://github.com/libhal/libhal-cmake-helpers" description = "CMake helper functions and utilities for libhal projects" topics = ("cmake", "build-helpers", "libhal", "embedded") - settings = "os", "compiler", "build_type", "arch" - exports_sources = "cmake/*", "CMakeLists.txt", "LICENSE" - + def layout(self): cmake_layout(self) - + def build(self): cmake = CMake(self) cmake.configure() cmake.build() - + def package(self): cmake = CMake(self) cmake.install() - + # Also copy license - copy(self, "LICENSE", - src=self.source_folder, + copy(self, "LICENSE", + src=self.source_folder, dst=self.package_folder) - + def package_info(self): # This is a build-time only package self.cpp_info.set_property("cmake_find_mode", "both") self.cpp_info.set_property("cmake_file_name", "LibhalBuild") - self.cpp_info.builddirs = ["lib/cmake/LibhalBuild"] - + BUILD_DIR = str(Path("lib") / "cmake" / "LibhalBuild") + self.cpp_info.builddirs = [BUILD_DIR] + def package_id(self): # This is a header-only/build-tool package, no binary compatibility self.info.clear() diff --git a/examples/demos_CMakeLists.txt b/v5/examples/demos_CMakeLists.txt similarity index 100% rename from examples/demos_CMakeLists.txt rename to v5/examples/demos_CMakeLists.txt diff --git a/examples/libhal-actuator_CMakeLists.txt b/v5/examples/libhal-actuator_CMakeLists.txt similarity index 100% rename from examples/libhal-actuator_CMakeLists.txt rename to v5/examples/libhal-actuator_CMakeLists.txt diff --git a/examples/strong_ptr_CMakeLists.txt b/v5/examples/strong_ptr_CMakeLists.txt similarity index 100% rename from examples/strong_ptr_CMakeLists.txt rename to v5/examples/strong_ptr_CMakeLists.txt From 539f39ad21eded107b6a0f4d8291f3b4b67072cc Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Sun, 8 Feb 2026 16:03:50 -0800 Subject: [PATCH 3/6] Fix up and support both v4 and v5 --- .github/workflows/deploy.yml | 9 +- README.md | 548 +++++++++++------- v4/README.md | 272 +++++++++ v5/CMakeLists.txt | 12 +- v5/README.md | 350 ----------- ...nfig.cmake => LibhalCMakeUtilConfig.cmake} | 0 v5/conanfile.py | 43 +- 7 files changed, 647 insertions(+), 587 deletions(-) create mode 100644 v4/README.md delete mode 100644 v5/README.md rename v5/cmake/{LibhalBuildConfig.cmake => LibhalCMakeUtilConfig.cmake} (100%) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a712d88..5dbca53 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -54,8 +54,13 @@ jobs: echo "VERSION=latest" >> $GITHUB_ENV fi - - name: 📦 Create Conan Package version '${{ env.VERSION }}' - run: conan create . --version=${{ env.VERSION }} + - name: 📦 Create v4 Conan Package version '${{ env.VERSION }}' + if: startsWith(env.VERSION, '4') || env.VERSION == 'latest' + run: conan create v4/ --version=${{ env.VERSION }} + + - name: 📦 Create v5 Conan Package version '${{ env.VERSION }}' + if: startsWith(env.VERSION, '5') || env.VERSION == 'latest' + run: conan create v5/ --version=${{ env.VERSION }} - name: 📡 Sign into JFrog Artifactory if: startsWith(github.ref, 'refs/tags/') diff --git a/README.md b/README.md index f87c197..d8d307a 100644 --- a/README.md +++ b/README.md @@ -1,269 +1,409 @@ # libhal-cmake-util -Generic cmake utilities such as macros, functions, and toolchains for all -categories of libhal libraries. +CMake helper functions and utilities for libhal projects. Provides both granular building blocks and convenient wrapper functions for common patterns. -This package is a REQUIREMENT for libhal libraries using cmake. +> [NOTE] +> **Using v4?** The v4 API (toolchain injection style) is deprecated. See +> [v4/README.md](v4/README.md) for legacy documentation. Consider migrating +> to v5 for explicit `find_package()` integration and C++20 module support. -## Integration into Conan +## Installation -Add the following line to the `build_requirements()` method of your library or -applications `conanfile.py`: +Via Conan: ```python def build_requirements(self): - self.tool_requires("libhal-cmake-util/2.2.0") + self.tool_requires("libhal-cmake-util/[^5.0.0]") ``` -**NOTE**: the `tool_requires` line can also go in the `requirements()` method or -in the `tool_requires` attribute, but the standard way of doing this is in -libhal is a `build_requirements()` method. +## Quick Start -## Package Options +```cmake +cmake_minimum_required(VERSION 4.0) + +# Find the helpers package +find_package(LibhalCMakeUtil REQUIRED) + +# Initialize your project (required) +libhal_project_init(my_library) + +# Option 1: Use convenience wrapper +libhal_quick_library(my_library + SOURCES src/foo.cpp src/bar.cpp + MODULES modules/baz.cppm + NAMESPACE libhal +) + +# Option 2: Granular control +libhal_add_library(my_library + SOURCES src/foo.cpp + MODULES modules/bar.cppm +) +target_link_libraries(my_library PRIVATE libhal::compile_options) +libhal_install_library(my_library NAMESPACE libhal) + +# Add tests +libhal_add_tests(my_library + TEST_NAMES foo bar baz + MODULE tests/util.cppm +) +``` -This package comes with some options to customize its behavior. You can set -these options in the `tool_requires` function. +## Core Functions -```python -def build_requirements(self): - self.tool_requires("libhal-cmake-util/2.2.0", - options={ - "add_build_outputs": True, - "optimize_debug_build": True, - }) -``` +### `libhal_project_init(PROJECT_NAME)` -### Option: `add_build_outputs` (Default: `True`) +**Required** - Sets up project-level configuration. This is the only mandatory function. -When the `add_build_outputs` option is set to `True`, a range of post-build -utility functions are made available. These functions generate additional files -and information derived from the final binary or .elf file. These functions are -provided to your CMake build project via the Conan CMake toolchain file. If -you are using `conan build` along with the `CMakeToolchain` generator, then -these functions will be made available in your `CMakeLists.txt` build script. +What it does: -- `libhal_generate_intel_hex(elf_file)`: transforms the provided elf file into - an Intel HEX (.hex) file. +- Calls `project()` with C++ language +- Enables compile_commands.json export +- Checks for Ninja/Visual Studio generator (required for modules) +- Sets up clang-tidy if enabled +- Creates `libhal::compile_options` and `libhal::asan` interface targets +- Adds compile_commands.json copy target -- `libhal_generate_binary(elf_file)`: turns the provided elf file into a binary - (.bin) file, a format often used for device flashing. +```cmake +libhal_project_init(my_project) +``` -- `libhal_disassemble(elf_file)`: breaks down the elf file into assembly code, -- providing a platform for detailed analysis. +### Interface Targets -- `libhal_disassemble_with_source(elf_file)`: like the one above, disassembles - the elf file into assembly code for in-depth analysis. However, it also - integrates the source code with the disassembly, facilitating the mapping of - lines of code to the corresponding assembly instructions. +After `libhal_project_init()`, these targets are available: -- `libhal_print_size_info(elf_file)`: prints out the size - information for the different sections of the elf file. +#### `libhal::compile_options` -- `libhal_full_post_build(elf_file)`: executes a comprehensive post-build - process, which includes the following functions: - - `libhal_generate_intel_hex()` - - `libhal_generate_binary()` - - `libhal_disassemble()` - - `libhal_disassemble_with_source()` - - `libhal_print_size_info()` +Standard compile flags for libhal projects. **Opt-in** via linking: -- `libhal_post_build(elf_file)`: performs a selective post-build process, - executing the following functions: - - `libhal_generate_intel_hex()` - - `libhal_generate_binary()` - - `libhal_print_size_info()` +```cmake +target_link_libraries(my_lib PRIVATE libhal::compile_options) +``` -### Option: `optimize_debug_build` (Default: `True`) +Flags included: -Setting `optimize_debug_build` to `True` modifies the `Debug` build type -optimization level from `-O0 -g` to `-Og -g`. The `-Og` option offers a balance -similar to `-O1`, but with modifications designed to enhance the debugging -experience. However, these changes can increase the binary size compared to -`-O1`. +- GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -Wpedantic -fexceptions -fno-rtti` +- MSVC: `/W4 /WX /EHsc /permissive- /GR-` -This option is particularly crucial because the `-O0` setting (no optimization) -often leads to large binary sizes. These oversized binaries may not fit on a -device when a more robust debugging experience is required. +#### `libhal::asan` -Why does this matter? At higher optimization levels, much of the source code can -be optimized away, making step-through debugging appear erratic. You might -experience skipped lines of code, inlined constructors that are difficult to -interpret, and ephemeral stack variables that may disappear between lines of the -same scope due to optimization. These factors can significantly complicate -on-chip debugging. +AddressSanitizer support (non-Windows only): -**NOTE**: this field is **REQUIRED** by all open source libhal libraries in -order to reduce their library's binary size in debug mode. +```cmake +target_link_libraries(my_lib PRIVATE libhal::asan) +``` -## Build functions +## Library Functions -This package automatically injects libhal cmake utility functions: +### Granular Functions -### `libhal_test_and_make_library()` +#### `libhal_add_library(TARGET_NAME)` -Builds and tests a library. This function must be used in place of using -`libhal_unit_test` and `libhal_make_library` separately. +Creates a static library with optional sources and modules. ```cmake -libhal_test_and_make_library([LIBRARY_NAME ] - [SOURCES ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ]) +libhal_add_library(my_lib + SOURCES src/foo.cpp src/bar.cpp + MODULES modules/baz.cppm modules/qux.cppm +) ``` -- `LIBRARY_NAME` name of the library (e.g. libhal-lpc40, libhal-util) -- `SOURCES` is a list of source files to include in the package build and unit - tests. -- `TEST_SOURCES` is a list of source unit test source files used to build the - unit test executable. This will not be included in the library package build. -- `INCLUDES` is a list of include directories for the build process. Note that - the `include` and `src` directories are already included for you. -- `PACKAGES` list of packages to automatically find and make available for the - package build. -- `LINK_LIBRARIES` list of the libraries to link into the library. -- `TEST_PACKAGES` list of test packages to automatically find and make available - for the package build. -- `TEST_LINK_LIBRARIES` list of the libraries to link into the unit tests. These - libraries will be added to the library target. - -This function requires that Boost.UT unit testing framework to be available -as a package. In conan, add this to your `build_requirements()` method: +Arguments: -```python -def build_requirements(self): - self.tool_requires("libhal-cmake-util/2.2.0") - self.test_requires("boost-ext-ut/1.1.9") +- `SOURCES` - List of .cpp files +- `MODULES` - List of .cppm module files + +#### `libhal_install_library(TARGET_NAME)` + +Configures library installation with CMake config files. + +```cmake +libhal_install_library(my_lib NAMESPACE libhal) ``` -### `libhal_unit_test()` +Arguments: + +- `NAMESPACE` (optional) - Namespace for exported target (default: library name) + +### Convenience Functions -Builds and executes unit tests for libhal. Use this for header only libraries -that do not generate library files, but do have build-able unit tests. If -non-test source files are present, then the libhal package MUST use the -`libhal_test_and_make_library()` function. +#### `libhal_quick_library(TARGET_NAME)` + +One-shot library creation, configuration, and installation. ```cmake -libhal_unit_test([SOURCES ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ]) +libhal_quick_library(strong_ptr + MODULES modules/strong_ptr.cppm + NAMESPACE libhal +) ``` -- `SOURCES` is a list of source files to include in the build for the unit - test. The set of source files MUST include the project source files as - well as the unit test source files. -- `INCLUDES` is a list of include directires to be added to the executable. - Note that the `include`, `src`, and `test` directories are already - included for you. -- `PACKAGES` list of packages to automatically find and make available for the - unit test build. Packages needed by the package binary will also be needed - for unit tests, so supply them here. -- `LINK_LIBRARIES` list of the libraries to link into the unit test library. - Packages needed by the package binary will also be needed for unit tests, - so supply them here. DO NOT include the package/library that is being - tested here. This can cause linker errors because the same definition of - symbols will appear twice due to the files being compiled again in the - SOURCES directory. Omitting the source files causing the linker error will - cause those source files to be skipped during coverage. Clang and GCC both - need to add instrumentation to the source files during compilation to - enable coverage and package libraries are built without this - instrumentation. - -All libhal packages and projects must be compiled with this function to comply -with the libhal standards. This function does the following: - -1. Creates an target/executable named "unit_test" -2. Accepts SOURCES and INCLUDE directories and provides them to the target -3. Enables Address Sanitizer if the compiler supports it and issues warning if - it does not. -4. Applies clang-tidy checks to all files if the clang-tidy program is - available on the system (issues warning if it does not) -5. Add flags to enable code coverage for the unit test executable - -This function requires that Boost.UT unit testing framework to be available -as a package. In conan, add this to your `build_requirements()` method: +Automatically applies: -```python - def build_requirements(self): - self.tool_requires("libhal-cmake-util/2.2.0") - self.test_requires("boost-ext-ut/1.1.9") +- `libhal::compile_options` +- `libhal::asan` (when not cross-compiling) +- Installs with CMake config + +#### `libhal_test_and_make_library()` + +Monolithic function combining library and tests (legacy pattern). + +```cmake +libhal_test_and_make_library( + LIBRARY_NAME libhal-actuator + SOURCES src/rc_servo.cpp src/drc_v2.cpp + TEST_SOURCES tests/rc_servo.test.cpp tests/drc.test.cpp + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock +) ``` -### `libhal_make_library()` +## Testing Functions -Builds libhal libraries. Use this when unit tests are not available or necessary -but a package must be built. +### `libhal_add_tests(TARGET_NAME)` + +Adds unit tests for a library. Supports two modes: + +#### Mode 1: Explicit test file list** + +```cmake +libhal_add_tests(my_lib + TEST_SOURCES tests/foo.test.cpp tests/bar.test.cpp + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock +) +``` + +#### Mode 2: Generate filenames from names ```cmake -libhal_make_library([LIBRARY_NAME ] - [SOURCES ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ]) +libhal_add_tests(my_lib + TEST_NAMES foo bar baz + MODULE tests/util.cppm +) ``` -- `LIBRARY_NAME` name of the library (e.g. libhal-lpc40, libhal-util) -- `SOURCES` is a list of source files to include in the package build. -- `INCLUDES` is a list of include directories to be added to the executable. - Note that the `include` and `src` directories are already included for you. -- `PACKAGES` list of packages to automatically find and make available for the - package build. -- `LINK_LIBRARIES` list of the libraries to link into the library. -- `USE_CLANG_TIDY` use this option to enable clang tidy checks for libraries. +Looks for `tests/foo.test.cpp`, `tests/bar.test.cpp`, etc. + +#### Arguments + +Arguments: + +- `TEST_SOURCES` - Explicit list of test files +- `TEST_NAMES` - Generate test filenames (tests/NAME.test.cpp) +- `MODULE` - Optional utility module for tests +- `PACKAGES` - Additional packages to find +- `LINK_LIBRARIES` - Additional libraries to link + +## Executable/Demo Functions + +### `libhal_add_executable(TARGET_NAME)` + +Creates a single executable/demo. + +```cmake +libhal_add_executable(my_demo + SOURCES demos/my_demo.cpp src/common.cpp + INCLUDES include/ + PACKAGES libhal-util libhal-lpc40 + LINK_LIBRARIES libhal::util libhal::lpc40 +) +``` ### `libhal_build_demos()` -Builds a set of demos. - -For this function to work, the directory structure must fit the following: - -```tree -demos/ -├── CMakeLists.txt -├── applications -│ ├── adc.cpp -│ ├── blinker.cpp -│ ├── can.cpp -│ ├── gpio.cpp -│ ├── i2c.cpp -│ ├── interrupt_pin.cpp -│ ├── pwm.cpp -│ ├── spi.cpp -│ ├── ... -│ └── uart.cpp -└── main.cpp +Builds multiple demos at once. + +```cmake +libhal_build_demos( + DEMOS demo1 demo2 demo3 + SOURCES src/common.cpp + INCLUDES include/ + PACKAGES libhal-util libhal-expander + LINK_LIBRARIES libhal::util libhal::expander +) ``` -Where main contains the startup code and calls a common function that is -implemented across the demos in the `applications`` directory. +Looks for `demos/demo1.cpp`, `demos/demo2.cpp`, etc. + +## Firmware Output Functions + +For embedded targets, these functions generate additional output files: + +### `libhal_create_hex_file_from(TARGET_NAME)` + +Creates Intel HEX file from ELF executable. ```cmake -libhal_build_demos([LIBRARY_NAME ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ] - [LINK_FLAGS ] - DISABLE_CLANG_TIDY) +libhal_create_hex_file_from(my_firmware) +libhal_create_hex_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/artifacts") ``` -- `DEMOS` names of the demos in the `application/` directory. The names must - corrispond to the names of the `.cpp` files in the directory. For example, - a demo name of `adc` must have a `adc.cpp` file in the `application/` - directory. -- `INCLUDES` list of include directories. The list has no default and is - empty. -- `PACKAGES` list of packages to automatically find and make available for the - package build. -- `LINK_LIBRARIES` list of the libraries to link into the library. -- `LINK_FLAGS` linker flags for the demos. -- `DISABLE_CLANG_TIDY` option is used to disable clang-tidy checks for host - builds. +### `libhal_create_binary_file_from(TARGET_NAME)` -## Contributing +Creates raw binary file from ELF executable. -See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details. +```cmake +libhal_create_binary_file_from(my_firmware) +``` + +### `libhal_create_disassembly_from(TARGET_NAME)` + +Creates disassembly files (.S and .demangled.S). + +```cmake +libhal_create_disassembly_from(my_firmware) +``` + +### `libhal_create_disassembly_with_source_from(TARGET_NAME)` + +Creates listing file with source code interleaved (.lst). + +```cmake +libhal_create_disassembly_with_source_from(my_firmware) +``` + +### `libhal_print_size_of(TARGET_NAME)` + +Prints size information (text, data, bss sections). + +```cmake +libhal_print_size_of(my_firmware) +``` + +## Clang-tidy + +Enable via CMake options: + +```bash +# Enable clang-tidy checks +cmake -DLIBHAL_ENABLE_CLANG_TIDY=ON .. + +# Enable with automatic fixes +cmake -DLIBHAL_CLANG_TIDY_FIX=ON .. +``` + +## Complete Examples + +### Example 1: C++20 Modules Library (strong_ptr) + +```cmake +cmake_minimum_required(VERSION 4.0) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(strong_ptr) + +# Create library with modules +libhal_add_library(strong_ptr + MODULES modules/strong_ptr.cppm +) + +# Opt-in to compile options +target_link_libraries(strong_ptr PRIVATE libhal::compile_options) + +# 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 + MODULE tests/util.cppm +) +``` + +### Example 2: Traditional Source Library (libhal-actuator) + +```cmake +cmake_minimum_required(VERSION 3.15) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(libhal-actuator) + +# Quick library with auto-install +libhal_quick_library(libhal-actuator + SOURCES + src/rc_servo.cpp + src/smart_servo/rmd/drc_v2.cpp + src/smart_servo/rmd/mc_x_v2.cpp +) + +# Add tests +libhal_add_tests(libhal-actuator + TEST_SOURCES + tests/main.test.cpp + tests/rc_servo.test.cpp + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock +) +``` + +### Example 3: Demo Applications + +```cmake +cmake_minimum_required(VERSION 3.15) +find_package(LibhalBuild REQUIRED) + +libhal_project_init(demos) + +libhal_build_demos( + DEMOS + drc_v2 + mc_x_v2 + rc_servo + INCLUDES . + PACKAGES + libhal-actuator + libhal-expander + LINK_LIBRARIES + libhal::actuator + libhal::expander +) +``` + +### Example 4: Full Manual Control + +```cmake +cmake_minimum_required(VERSION 4.0) +find_package(LibhalBuild REQUIRED) + +# Initialize project +libhal_project_init(my_advanced_lib) + +# 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_link_libraries(my_advanced_lib PRIVATE + libhal::compile_options +) + +# Custom compile definitions +target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG) + +# Install +libhal_install_library(my_advanced_lib NAMESPACE mycompany) +``` + +## Philosophy + +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 interface targets +- **Explicit, not magical** - Clear what's happening +- **Composable** - Mix and match as needed ## License -Apache 2.0; see [`LICENSE`](LICENSE) for details. +Apache-2.0 + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/v4/README.md b/v4/README.md new file mode 100644 index 0000000..cd6f403 --- /dev/null +++ b/v4/README.md @@ -0,0 +1,272 @@ +# libhal-cmake-util v4 (Deprecated) + +> [!WARNING] +> **DEPRECATED:** This API is deprecated. Please migrate to v5 which uses +> explicit `find_package(LibhalBuild)` integration. See the +> [root README](../README.md) for the new API. + +--- + +Generic cmake utilities such as macros, functions, and toolchains for all +categories of libhal libraries. + +This package is a REQUIREMENT for libhal libraries using cmake. + +## Integration into Conan + +Add the following line to the `build_requirements()` method of your library or +applications `conanfile.py`: + +```python +def build_requirements(self): + self.tool_requires("libhal-cmake-util/[^4.0.0]") +``` + +**NOTE**: the `tool_requires` line can also go in the `requirements()` method or +in the `tool_requires` attribute, but the standard way of doing this is in +libhal is a `build_requirements()` method. + +## Package Options + +This package comes with some options to customize its behavior. You can set +these options in the `tool_requires` function. + +```python +def build_requirements(self): + self.tool_requires("libhal-cmake-util/[^4.0.0]", + options={ + "add_build_outputs": True, + "optimize_debug_build": True, + }) +``` + +### Option: `add_build_outputs` (Default: `True`) + +When the `add_build_outputs` option is set to `True`, a range of post-build +utility functions are made available. These functions generate additional files +and information derived from the final binary or .elf file. These functions are +provided to your CMake build project via the Conan CMake toolchain file. If +you are using `conan build` along with the `CMakeToolchain` generator, then +these functions will be made available in your `CMakeLists.txt` build script. + +- `libhal_generate_intel_hex(elf_file)`: transforms the provided elf file into + an Intel HEX (.hex) file. + +- `libhal_generate_binary(elf_file)`: turns the provided elf file into a binary + (.bin) file, a format often used for device flashing. + +- `libhal_disassemble(elf_file)`: breaks down the elf file into assembly code, +- providing a platform for detailed analysis. + +- `libhal_disassemble_with_source(elf_file)`: like the one above, disassembles + the elf file into assembly code for in-depth analysis. However, it also + integrates the source code with the disassembly, facilitating the mapping of + lines of code to the corresponding assembly instructions. + +- `libhal_print_size_info(elf_file)`: prints out the size + information for the different sections of the elf file. + +- `libhal_full_post_build(elf_file)`: executes a comprehensive post-build + process, which includes the following functions: + - `libhal_generate_intel_hex()` + - `libhal_generate_binary()` + - `libhal_disassemble()` + - `libhal_disassemble_with_source()` + - `libhal_print_size_info()` + +- `libhal_post_build(elf_file)`: performs a selective post-build process, + executing the following functions: + - `libhal_generate_intel_hex()` + - `libhal_generate_binary()` + - `libhal_print_size_info()` + +### Option: `optimize_debug_build` (Default: `True`) + +Setting `optimize_debug_build` to `True` modifies the `Debug` build type +optimization level from `-O0 -g` to `-Og -g`. The `-Og` option offers a balance +similar to `-O1`, but with modifications designed to enhance the debugging +experience. However, these changes can increase the binary size compared to +`-O1`. + +This option is particularly crucial because the `-O0` setting (no optimization) +often leads to large binary sizes. These oversized binaries may not fit on a +device when a more robust debugging experience is required. + +Why does this matter? At higher optimization levels, much of the source code can +be optimized away, making step-through debugging appear erratic. You might +experience skipped lines of code, inlined constructors that are difficult to +interpret, and ephemeral stack variables that may disappear between lines of the +same scope due to optimization. These factors can significantly complicate +on-chip debugging. + +**NOTE**: this field is **REQUIRED** by all open source libhal libraries in +order to reduce their library's binary size in debug mode. + +## Build functions + +This package automatically injects libhal cmake utility functions: + +### `libhal_test_and_make_library()` + +Builds and tests a library. This function must be used in place of using +`libhal_unit_test` and `libhal_make_library` separately. + +```cmake +libhal_test_and_make_library([LIBRARY_NAME ] + [SOURCES ] + [INCLUDES ] + [PACKAGES ] + [LINK_LIBRARIES ]) +``` + +- `LIBRARY_NAME` name of the library (e.g. libhal-lpc40, libhal-util) +- `SOURCES` is a list of source files to include in the package build and unit + tests. +- `TEST_SOURCES` is a list of source unit test source files used to build the + unit test executable. This will not be included in the library package build. +- `INCLUDES` is a list of include directories for the build process. Note that + the `include` and `src` directories are already included for you. +- `PACKAGES` list of packages to automatically find and make available for the + package build. +- `LINK_LIBRARIES` list of the libraries to link into the library. +- `TEST_PACKAGES` list of test packages to automatically find and make available + for the package build. +- `TEST_LINK_LIBRARIES` list of the libraries to link into the unit tests. These + libraries will be added to the library target. + +This function requires that Boost.UT unit testing framework to be available +as a package. In conan, add this to your `build_requirements()` method: + +```python +def build_requirements(self): + self.tool_requires("libhal-cmake-util/[^4.0.0]") + self.test_requires("boost-ext-ut/1.1.9") +``` + +### `libhal_unit_test()` + +Builds and executes unit tests for libhal. Use this for header only libraries +that do not generate library files, but do have build-able unit tests. If +non-test source files are present, then the libhal package MUST use the +`libhal_test_and_make_library()` function. + +```cmake +libhal_unit_test([SOURCES ] + [INCLUDES ] + [PACKAGES ] + [LINK_LIBRARIES ]) +``` + +- `SOURCES` is a list of source files to include in the build for the unit + test. The set of source files MUST include the project source files as + well as the unit test source files. +- `INCLUDES` is a list of include directires to be added to the executable. + Note that the `include`, `src`, and `test` directories are already + included for you. +- `PACKAGES` list of packages to automatically find and make available for the + unit test build. Packages needed by the package binary will also be needed + for unit tests, so supply them here. +- `LINK_LIBRARIES` list of the libraries to link into the unit test library. + Packages needed by the package binary will also be needed for unit tests, + so supply them here. DO NOT include the package/library that is being + tested here. This can cause linker errors because the same definition of + symbols will appear twice due to the files being compiled again in the + SOURCES directory. Omitting the source files causing the linker error will + cause those source files to be skipped during coverage. Clang and GCC both + need to add instrumentation to the source files during compilation to + enable coverage and package libraries are built without this + instrumentation. + +All libhal packages and projects must be compiled with this function to comply +with the libhal standards. This function does the following: + +1. Creates an target/executable named "unit_test" +2. Accepts SOURCES and INCLUDE directories and provides them to the target +3. Enables Address Sanitizer if the compiler supports it and issues warning if + it does not. +4. Applies clang-tidy checks to all files if the clang-tidy program is + available on the system (issues warning if it does not) +5. Add flags to enable code coverage for the unit test executable + +This function requires that Boost.UT unit testing framework to be available +as a package. In conan, add this to your `build_requirements()` method: + +```python + def build_requirements(self): + self.tool_requires("libhal-cmake-util/[^4.0.0]") + self.test_requires("boost-ext-ut/1.1.9") +``` + +### `libhal_make_library()` + +Builds libhal libraries. Use this when unit tests are not available or necessary +but a package must be built. + +```cmake +libhal_make_library([LIBRARY_NAME ] + [SOURCES ] + [INCLUDES ] + [PACKAGES ] + [LINK_LIBRARIES ]) +``` + +- `LIBRARY_NAME` name of the library (e.g. libhal-lpc40, libhal-util) +- `SOURCES` is a list of source files to include in the package build. +- `INCLUDES` is a list of include directories to be added to the executable. + Note that the `include` and `src` directories are already included for you. +- `PACKAGES` list of packages to automatically find and make available for the + package build. +- `LINK_LIBRARIES` list of the libraries to link into the library. +- `USE_CLANG_TIDY` use this option to enable clang tidy checks for libraries. + +### `libhal_build_demos()` + +Builds a set of demos. + +For this function to work, the directory structure must fit the following: + +```tree +demos/ +├── CMakeLists.txt +├── applications +│ ├── adc.cpp +│ ├── blinker.cpp +│ ├── can.cpp +│ ├── gpio.cpp +│ ├── i2c.cpp +│ ├── interrupt_pin.cpp +│ ├── pwm.cpp +│ ├── spi.cpp +│ ├── ... +│ └── uart.cpp +└── main.cpp +``` + +Where main contains the startup code and calls a common function that is +implemented across the demos in the `applications` directory. + +```cmake +libhal_build_demos([LIBRARY_NAME ] + [INCLUDES ] + [PACKAGES ] + [LINK_LIBRARIES ] + [LINK_FLAGS ] + DISABLE_CLANG_TIDY) +``` + +- `DEMOS` names of the demos in the `application/` directory. The names must + corrispond to the names of the `.cpp` files in the directory. For example, + a demo name of `adc` must have a `adc.cpp` file in the `application/` + directory. +- `INCLUDES` list of include directories. The list has no default and is + empty. +- `PACKAGES` list of packages to automatically find and make available for the + package build. +- `LINK_LIBRARIES` list of the libraries to link into the library. +- `LINK_FLAGS` linker flags for the demos. +- `DISABLE_CLANG_TIDY` option is used to disable clang-tidy checks for host + builds. + +## License + +Apache-2.0 diff --git a/v5/CMakeLists.txt b/v5/CMakeLists.txt index 879d7bc..27500da 100644 --- a/v5/CMakeLists.txt +++ b/v5/CMakeLists.txt @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 4.0) project(libhal-cmake-helpers LANGUAGES NONE) @@ -21,7 +21,7 @@ include(GNUInstallDirs) # Install all CMake modules install( FILES - cmake/LibhalBuildConfig.cmake + cmake/LibhalCMakeUtilConfig.cmake cmake/LibhalCompileOptions.cmake cmake/LibhalLibrary.cmake cmake/LibhalExecutable.cmake @@ -29,20 +29,20 @@ install( cmake/LibhalClangTidy.cmake cmake/LibhalBinUtils.cmake DESTINATION - ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalBuild + ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalCMakeUtil ) # Create a version file include(CMakePackageConfigHelpers) write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/LibhalBuildConfigVersion.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/LibhalCMakeUtilConfigVersion.cmake" VERSION 1.0.0 COMPATIBILITY SameMajorVersion ) install( FILES - "${CMAKE_CURRENT_BINARY_DIR}/LibhalBuildConfigVersion.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/LibhalCMakeUtilConfigVersion.cmake" DESTINATION - ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalBuild + ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalCMakeUtil ) diff --git a/v5/README.md b/v5/README.md deleted file mode 100644 index 4f95323..0000000 --- a/v5/README.md +++ /dev/null @@ -1,350 +0,0 @@ -# libhal-cmake-helpers - -CMake helper functions and utilities for libhal projects. Provides both granular building blocks and convenient wrapper functions for common patterns. - -## Installation - -Via Conan: - -```bash -conan install libhal-cmake-helpers/1.0.0@ -``` - -## Quick Start - -```cmake -cmake_minimum_required(VERSION 4.0) - -# Find the helpers package -# Source: https://github.com/libhal/libhal-cmake-helpers -find_package(LibhalBuild REQUIRED) - -# Initialize your project (required) -libhal_project_init(my_library) - -# Option 1: Use convenience wrapper -libhal_quick_library(my_library - SOURCES src/foo.cpp src/bar.cpp - MODULES modules/baz.cppm - NAMESPACE libhal -) - -# Option 2: Granular control -libhal_add_library(my_library - SOURCES src/foo.cpp - MODULES modules/bar.cppm -) -target_link_libraries(my_library PRIVATE libhal::compile_options) -libhal_install_library(my_library NAMESPACE libhal) - -# Add tests -libhal_add_tests(my_library - TEST_NAMES foo bar baz - UTIL_MODULE tests/util.cppm -) -``` - -## Core Functions - -### `libhal_project_init(PROJECT_NAME)` - -**Required** - Sets up project-level configuration. This is the only mandatory function. - -What it does: -- Calls `project()` with C++ language -- Enables compile_commands.json export -- Checks for Ninja/Visual Studio generator (required for modules) -- Sets up clang-tidy if enabled -- Creates `libhal::compile_options` and `libhal::asan` interface targets -- Adds compile_commands.json copy target - -```cmake -libhal_project_init(my_project) -``` - -### Interface Targets - -After `libhal_project_init()`, these targets are available: - -#### `libhal::compile_options` -Standard compile flags for libhal projects. **Opt-in** via linking: - -```cmake -target_link_libraries(my_lib PRIVATE libhal::compile_options) -``` - -Flags included: -- GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -Wpedantic -fexceptions -fno-rtti` -- MSVC: `/W4 /WX /EHsc /permissive- /GR-` - -#### `libhal::asan` -AddressSanitizer support (non-Windows only): - -```cmake -target_link_libraries(my_lib PRIVATE libhal::asan) -``` - -## Library Functions - -### Granular Functions - -#### `libhal_add_library(TARGET_NAME)` -Creates a static library with optional sources and modules. - -```cmake -libhal_add_library(my_lib - SOURCES src/foo.cpp src/bar.cpp - MODULES modules/baz.cppm modules/qux.cppm -) -``` - -Arguments: -- `SOURCES` - List of .cpp files -- `MODULES` - List of .cppm module files - -#### `libhal_install_library(TARGET_NAME)` -Configures library installation with CMake config files. - -```cmake -libhal_install_library(my_lib NAMESPACE libhal) -``` - -Arguments: -- `NAMESPACE` (optional) - Namespace for exported target (default: library name) - -### Convenience Functions - -#### `libhal_quick_library(TARGET_NAME)` -One-shot library creation, configuration, and installation. - -```cmake -libhal_quick_library(strong_ptr - MODULES modules/strong_ptr.cppm - NAMESPACE libhal -) -``` - -Automatically applies: -- `libhal::compile_options` -- `libhal::asan` (when not cross-compiling) -- Installs with CMake config - -#### `libhal_test_and_make_library()` -Monolithic function combining library and tests (legacy pattern). - -```cmake -libhal_test_and_make_library( - LIBRARY_NAME libhal-actuator - SOURCES src/rc_servo.cpp src/drc_v2.cpp - TEST_SOURCES tests/rc_servo.test.cpp tests/drc.test.cpp - PACKAGES libhal-mock - LINK_LIBRARIES libhal::mock -) -``` - -## Testing Functions - -### `libhal_add_tests(TARGET_NAME)` - -Adds unit tests for a library. Supports two modes: - -**Mode 1: Explicit test file list** -```cmake -libhal_add_tests(my_lib - TEST_SOURCES tests/foo.test.cpp tests/bar.test.cpp - PACKAGES libhal-mock - LINK_LIBRARIES libhal::mock -) -``` - -**Mode 2: Generate filenames from names** -```cmake -libhal_add_tests(my_lib - TEST_NAMES foo bar baz - UTIL_MODULE tests/util.cppm -) -``` -Looks for `tests/foo.test.cpp`, `tests/bar.test.cpp`, etc. - -Arguments: -- `TEST_SOURCES` - Explicit list of test files -- `TEST_NAMES` - Generate test filenames (tests/NAME.test.cpp) -- `UTIL_MODULE` - Optional utility module for tests -- `PACKAGES` - Additional packages to find -- `LINK_LIBRARIES` - Additional libraries to link - -## Executable/Demo Functions - -### `libhal_add_executable(TARGET_NAME)` - -Creates a single executable/demo. - -```cmake -libhal_add_executable(my_demo - SOURCES demos/my_demo.cpp src/common.cpp - INCLUDES include/ - PACKAGES libhal-util libhal-lpc40 - LINK_LIBRARIES libhal::util libhal::lpc40 -) -``` - -### `libhal_build_demos()` - -Builds multiple demos at once. - -```cmake -libhal_build_demos( - DEMOS demo1 demo2 demo3 - SOURCES src/common.cpp - INCLUDES include/ - PACKAGES libhal-util libhal-expander - LINK_LIBRARIES libhal::util libhal::expander -) -``` - -Looks for `demos/demo1.cpp`, `demos/demo2.cpp`, etc. - -## Clang-tidy - -Enable via CMake options: - -```bash -# Enable clang-tidy checks -cmake -DLIBHAL_ENABLE_CLANG_TIDY=ON .. - -# Enable with automatic fixes -cmake -DLIBHAL_CLANG_TIDY_FIX=ON .. -``` - -Or via Conan: - -```bash -conan install . -o enable_clang_tidy=True -conan install . -o clang_tidy_fix=True -``` - -## Complete Examples - -### Example 1: C++20 Modules Library (strong_ptr) - -```cmake -cmake_minimum_required(VERSION 4.0) -find_package(LibhalBuild REQUIRED) - -libhal_project_init(strong_ptr) - -# Create library with modules -libhal_add_library(strong_ptr - MODULES modules/strong_ptr.cppm -) - -# Opt-in to compile options -target_link_libraries(strong_ptr PRIVATE libhal::compile_options) - -# 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 - UTIL_MODULE tests/util.cppm -) -``` - -### Example 2: Traditional Source Library (libhal-actuator) - -```cmake -cmake_minimum_required(VERSION 3.15) -find_package(LibhalBuild REQUIRED) - -libhal_project_init(libhal-actuator) - -# Quick library with auto-install -libhal_quick_library(libhal-actuator - SOURCES - src/rc_servo.cpp - src/smart_servo/rmd/drc_v2.cpp - src/smart_servo/rmd/mc_x_v2.cpp -) - -# Add tests -libhal_add_tests(libhal-actuator - TEST_SOURCES - tests/main.test.cpp - tests/rc_servo.test.cpp - PACKAGES libhal-mock - LINK_LIBRARIES libhal::mock -) -``` - -### Example 3: Demo Applications - -```cmake -cmake_minimum_required(VERSION 3.15) -find_package(LibhalBuild REQUIRED) - -libhal_project_init(demos) - -libhal_build_demos( - DEMOS - drc_v2 - mc_x_v2 - rc_servo - INCLUDES . - PACKAGES - libhal-actuator - libhal-expander - LINK_LIBRARIES - libhal::actuator - libhal::expander -) -``` - -### Example 4: Full Manual Control - -```cmake -cmake_minimum_required(VERSION 4.0) -find_package(LibhalBuild REQUIRED) - -# Initialize project -libhal_project_init(my_advanced_lib) - -# 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_link_libraries(my_advanced_lib PRIVATE - libhal::compile_options - libhal::asan -) - -# Custom compile definitions -target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG) - -# Install -libhal_install_library(my_advanced_lib NAMESPACE mycompany) -``` - -## Philosophy - -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 interface targets -- **Explicit, not magical** - Clear what's happening -- **Composable** - Mix and match as needed - -## License - -Apache-2.0 - -## Contributing - -See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/v5/cmake/LibhalBuildConfig.cmake b/v5/cmake/LibhalCMakeUtilConfig.cmake similarity index 100% rename from v5/cmake/LibhalBuildConfig.cmake rename to v5/cmake/LibhalCMakeUtilConfig.cmake diff --git a/v5/conanfile.py b/v5/conanfile.py index 0ced7c3..d79820d 100644 --- a/v5/conanfile.py +++ b/v5/conanfile.py @@ -13,45 +13,38 @@ # limitations under the License. from conan import ConanFile -from conan.tools.cmake import CMake, cmake_layout from conan.tools.files import copy +from conan.tools.layout import basic_layout from pathlib import Path -class LibhalCMakeHelpersConan(ConanFile): +required_conan_version = ">=2.0.6" + + +class LibhalCMakeUtilConan(ConanFile): name = "libhal-cmake-util" license = "Apache-2.0" - author = "Khalil Estell and the libhal contributors" - url = "https://github.com/libhal/libhal-cmake-helpers" + homepage = "https://github.com/libhal/libhal-cmake-util" description = "CMake helper functions and utilities for libhal projects" topics = ("cmake", "build-helpers", "libhal", "embedded") - settings = "os", "compiler", "build_type", "arch" - exports_sources = "cmake/*", "CMakeLists.txt", "LICENSE" + exports_sources = "cmake/*", "LICENSE" + no_copy_source = True - def layout(self): - cmake_layout(self) + def package_id(self): + self.info.clear() - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() + def layout(self): + basic_layout(self) def package(self): - cmake = CMake(self) - cmake.install() - - # Also copy license copy(self, "LICENSE", + dst=str(Path(self.package_folder) / "licenses"), + src=self.source_folder) + copy(self, "cmake/*.cmake", src=self.source_folder, dst=self.package_folder) def package_info(self): - # This is a build-time only package - self.cpp_info.set_property("cmake_find_mode", "both") - self.cpp_info.set_property("cmake_file_name", "LibhalBuild") - BUILD_DIR = str(Path("lib") / "cmake" / "LibhalBuild") - self.cpp_info.builddirs = [BUILD_DIR] - - def package_id(self): - # This is a header-only/build-tool package, no binary compatibility - self.info.clear() + # Add cmake/ directory to builddirs so find_package(LibhalCMakeUtil) works + cmake_dir = str(Path(self.package_folder) / "cmake") + self.cpp_info.builddirs = [cmake_dir] From 08ec168148c14dd0381a1c4da15183e10739d51c Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Sun, 8 Feb 2026 17:06:03 -0800 Subject: [PATCH 4/6] Changes from experimentation with strong_ptr --- README.md | 159 ++++++++------------- v5/cmake/LibhalCMakeUtilConfig.cmake | 3 - v5/cmake/LibhalCompileOptions.cmake | 74 +++++----- v5/cmake/LibhalExecutable.cmake | 90 ++++++------ v5/cmake/LibhalLibrary.cmake | 93 ++---------- v5/cmake/LibhalTesting.cmake | 8 +- v5/examples/demos_CMakeLists.txt | 14 +- v5/examples/libhal-actuator_CMakeLists.txt | 72 ++-------- v5/examples/strong_ptr_CMakeLists.txt | 16 +-- 9 files changed, 169 insertions(+), 360 deletions(-) diff --git a/README.md b/README.md index d8d307a..a29701b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # libhal-cmake-util -CMake helper functions and utilities for libhal projects. Provides both granular building blocks and convenient wrapper functions for common patterns. +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 @@ -27,19 +28,17 @@ find_package(LibhalCMakeUtil REQUIRED) # Initialize your project (required) libhal_project_init(my_library) -# Option 1: Use convenience wrapper -libhal_quick_library(my_library - SOURCES src/foo.cpp src/bar.cpp - MODULES modules/baz.cppm - NAMESPACE libhal -) - -# Option 2: Granular control +# 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 ) -target_link_libraries(my_library PRIVATE libhal::compile_options) + +# Apply standard libhal compiler options +libhal_apply_compile_options(my_library) + +# Setup library installation info libhal_install_library(my_library NAMESPACE libhal) # Add tests @@ -61,23 +60,22 @@ What it does: - Enables compile_commands.json export - Checks for Ninja/Visual Studio generator (required for modules) - Sets up clang-tidy if enabled -- Creates `libhal::compile_options` and `libhal::asan` interface targets - Adds compile_commands.json copy target ```cmake libhal_project_init(my_project) ``` -### Interface Targets +### Standard Compile Option Functions -After `libhal_project_init()`, these targets are available: +After `libhal_project_init()`, these flag attachment files become available: -#### `libhal::compile_options` +#### `libhal_apply_compile_options(TARGET_NAME)` -Standard compile flags for libhal projects. **Opt-in** via linking: +Standard compile flags for libhal projects. ```cmake -target_link_libraries(my_lib PRIVATE libhal::compile_options) +libhal_apply_compile_options(my_lib) ``` Flags included: @@ -85,21 +83,24 @@ Flags included: - GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -Wpedantic -fexceptions -fno-rtti` - MSVC: `/W4 /WX /EHsc /permissive- /GR-` -#### `libhal::asan` +#### `libhal_apply_asan(TARGET_NAME)` AddressSanitizer support (non-Windows only): ```cmake -target_link_libraries(my_lib PRIVATE libhal::asan) +libhal_apply_asan(my_lib) ``` -## Library Functions +This API is safe to use on Windows. Executing it on a Windows machine does +nothing. -### Granular Functions +This is recommended only for unit and integration tests. -#### `libhal_add_library(TARGET_NAME)` +## Library Functions + +### `libhal_add_library(TARGET_NAME)` -Creates a static library with optional sources and modules. +Creates a static library target with optional sources and modules. ```cmake libhal_add_library(my_lib @@ -110,53 +111,27 @@ libhal_add_library(my_lib Arguments: +- 1st argument is the library name (my_lib in the above example) - `SOURCES` - List of .cpp files - `MODULES` - List of .cppm module files -#### `libhal_install_library(TARGET_NAME)` - -Configures library installation with CMake config files. +You may use the target `my_lib` elsewhere in the code with standard CMake commands like: ```cmake -libhal_install_library(my_lib NAMESPACE libhal) +target_compiler_options(my_lib PRIVATE -Wall) ``` -Arguments: - -- `NAMESPACE` (optional) - Namespace for exported target (default: library name) - -### Convenience Functions +### `libhal_install_library(TARGET_NAME)` -#### `libhal_quick_library(TARGET_NAME)` - -One-shot library creation, configuration, and installation. +Configures library installation with CMake config files. ```cmake -libhal_quick_library(strong_ptr - MODULES modules/strong_ptr.cppm - NAMESPACE libhal -) +libhal_install_library(my_lib NAMESPACE libhal) ``` -Automatically applies: - -- `libhal::compile_options` -- `libhal::asan` (when not cross-compiling) -- Installs with CMake config - -#### `libhal_test_and_make_library()` - -Monolithic function combining library and tests (legacy pattern). +Arguments: -```cmake -libhal_test_and_make_library( - LIBRARY_NAME libhal-actuator - SOURCES src/rc_servo.cpp src/drc_v2.cpp - TEST_SOURCES tests/rc_servo.test.cpp tests/drc.test.cpp - PACKAGES libhal-mock - LINK_LIBRARIES libhal::mock -) -``` +- `NAMESPACE` (optional) - Namespace for exported target (default: library name) ## Testing Functions @@ -171,6 +146,7 @@ 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 ) ``` @@ -179,7 +155,7 @@ libhal_add_tests(my_lib ```cmake libhal_add_tests(my_lib TEST_NAMES foo bar baz - MODULE tests/util.cppm + MODULES tests/util.cppm ) ``` @@ -191,7 +167,7 @@ Arguments: - `TEST_SOURCES` - Explicit list of test files - `TEST_NAMES` - Generate test filenames (tests/NAME.test.cpp) -- `MODULE` - Optional utility module for tests +- `MODULES` - Optional additional modules for tests - `PACKAGES` - Additional packages to find - `LINK_LIBRARIES` - Additional libraries to link @@ -199,24 +175,24 @@ Arguments: ### `libhal_add_executable(TARGET_NAME)` -Creates a single executable/demo. +Creates a single executable/app. ```cmake -libhal_add_executable(my_demo - SOURCES demos/my_demo.cpp src/common.cpp +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 ) ``` -### `libhal_build_demos()` +### `libhal_build_apps()` -Builds multiple demos at once. +Builds multiple apps at once. ```cmake -libhal_build_demos( - DEMOS demo1 demo2 demo3 +libhal_build_apps( + APPS app1 app2 app3 SOURCES src/common.cpp INCLUDES include/ PACKAGES libhal-util libhal-expander @@ -224,7 +200,7 @@ libhal_build_demos( ) ``` -Looks for `demos/demo1.cpp`, `demos/demo2.cpp`, etc. +Looks for `apps/app1.cpp`, `apps/app2.cpp`, etc. ## Firmware Output Functions @@ -236,17 +212,24 @@ Creates Intel HEX file from ELF executable. ```cmake libhal_create_hex_file_from(my_firmware) -libhal_create_hex_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/artifacts") +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`. + ### `libhal_create_binary_file_from(TARGET_NAME)` Creates raw binary file from ELF executable. ```cmake 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 `.hex`. + ### `libhal_create_disassembly_from(TARGET_NAME)` Creates disassembly files (.S and .demangled.S). @@ -257,7 +240,7 @@ libhal_create_disassembly_from(my_firmware) ### `libhal_create_disassembly_with_source_from(TARGET_NAME)` -Creates listing file with source code interleaved (.lst). +Creates a disassembly file with source code interleaved (.lst). ```cmake libhal_create_disassembly_with_source_from(my_firmware) @@ -299,7 +282,7 @@ libhal_add_library(strong_ptr ) # Opt-in to compile options -target_link_libraries(strong_ptr PRIVATE libhal::compile_options) +libhal_apply_compile_options(strong_ptr) # Install libhal_install_library(strong_ptr NAMESPACE libhal) @@ -311,41 +294,15 @@ libhal_add_tests(strong_ptr ) ``` -### Example 2: Traditional Source Library (libhal-actuator) - -```cmake -cmake_minimum_required(VERSION 3.15) -find_package(LibhalBuild REQUIRED) - -libhal_project_init(libhal-actuator) - -# Quick library with auto-install -libhal_quick_library(libhal-actuator - SOURCES - src/rc_servo.cpp - src/smart_servo/rmd/drc_v2.cpp - src/smart_servo/rmd/mc_x_v2.cpp -) - -# Add tests -libhal_add_tests(libhal-actuator - TEST_SOURCES - tests/main.test.cpp - tests/rc_servo.test.cpp - PACKAGES libhal-mock - LINK_LIBRARIES libhal::mock -) -``` - -### Example 3: Demo Applications +### Example 2: Demo Applications ```cmake cmake_minimum_required(VERSION 3.15) find_package(LibhalBuild REQUIRED) -libhal_project_init(demos) +libhal_project_init(apps) -libhal_build_demos( +libhal_build_apps( DEMOS drc_v2 mc_x_v2 @@ -360,7 +317,7 @@ libhal_build_demos( ) ``` -### Example 4: Full Manual Control +### Example 3: Full Manual Control ```cmake cmake_minimum_required(VERSION 4.0) @@ -379,9 +336,7 @@ target_sources(my_advanced_lib PUBLIC ) # Opt-in to compile options -target_link_libraries(my_advanced_lib PRIVATE - libhal::compile_options -) +target_link_libraries(my_advanced_lib PRIVATE LIBHAL_CXX_FLAGS) # Custom compile definitions target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG) diff --git a/v5/cmake/LibhalCMakeUtilConfig.cmake b/v5/cmake/LibhalCMakeUtilConfig.cmake index 5917c0d..6022769 100644 --- a/v5/cmake/LibhalCMakeUtilConfig.cmake +++ b/v5/cmake/LibhalCMakeUtilConfig.cmake @@ -43,9 +43,6 @@ function(libhal_project_init PROJECT_NAME) # Set up clang-tidy if enabled libhal_setup_clang_tidy() - # Create the interface targets for compile options - libhal_create_interface_targets() - # Add compile_commands.json copy target add_custom_target(copy_compile_commands ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/v5/cmake/LibhalCompileOptions.cmake b/v5/cmake/LibhalCompileOptions.cmake index 3a1cb42..762a56c 100644 --- a/v5/cmake/LibhalCompileOptions.cmake +++ b/v5/cmake/LibhalCompileOptions.cmake @@ -13,49 +13,43 @@ # limitations under the License. # Creates interface targets for compile options -# Users can opt-in via: target_link_libraries(my_lib PRIVATE libhal::compile_options) -function(libhal_create_interface_targets) - # Only create once (in case multiple projects use this) - if(TARGET libhal::compile_options) - return() - endif() +# Define the compile flags as a variable (users can inspect/print) +set(LIBHAL_CXX_FLAGS + $<$: + -g -Werror -Wall -Wextra -Wshadow -fexceptions -fno-rtti + -Wno-unused-command-line-argument -pedantic> + $<$: + /W4 /WX /EHsc /permissive- /GR-> + CACHE INTERNAL "libhal standard compile flags" +) - # Standard compile options for libhal libraries - add_library(libhal::compile_options INTERFACE IMPORTED GLOBAL) - target_compile_options(libhal::compile_options INTERFACE - $<$: - -g - -Werror - -Wall - -Wextra - -Wshadow - -Wno-unused-command-line-argument - -Wpedantic - -fexceptions - -fno-rtti - > - $<$: - /W4 - /WX - /EHsc - /permissive- - /GR- - > - ) +set(LIBHAL_ASAN_FLAGS + $<$>,$>: + -fsanitize=address> + CACHE INTERNAL "libhal AddressSanitizer flags" +) - # AddressSanitizer support (GCC/Clang on non-Windows only) - add_library(libhal::asan INTERFACE IMPORTED GLOBAL) - if(NOT WIN32) - target_compile_options(libhal::asan INTERFACE - $<$:-fsanitize=address> - ) - target_link_options(libhal::asan INTERFACE - $<$:-fsanitize=address> - ) +# Convenience function to apply flags without remembering variable names +function(libhal_apply_compile_options TARGET_NAME) + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") endif() - message(STATUS "Created libhal interface targets:") - message(STATUS " - libhal::compile_options (opt-in compile flags)") - message(STATUS " - libhal::asan (opt-in AddressSanitizer)") + target_compile_options(${TARGET_NAME} PRIVATE ${LIBHAL_CXX_FLAGS}) + message(STATUS "Applied libhal compile options to ${TARGET_NAME}") endfunction() + +function(libhal_apply_asan TARGET_NAME) + if(NOT TARGET ${TARGET_NAME}) + message(FATAL_ERROR "Target '${TARGET_NAME}' does not exist") + endif() + + if(NOT WIN32) + target_compile_options(${TARGET_NAME} PRIVATE ${LIBHAL_ASAN_FLAGS}) + target_link_options(${TARGET_NAME} PRIVATE ${LIBHAL_ASAN_FLAGS}) + message(STATUS "Applied AddressSanitizer to ${TARGET_NAME}") + else() + message(STATUS "AddressSanitizer not supported on Windows - skipping for ${TARGET_NAME}") + endif() +endfunction() \ No newline at end of file diff --git a/v5/cmake/LibhalExecutable.cmake b/v5/cmake/LibhalExecutable.cmake index c0ab011..925d8a9 100644 --- a/v5/cmake/LibhalExecutable.cmake +++ b/v5/cmake/LibhalExecutable.cmake @@ -12,114 +12,114 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Executable and demo helpers for libhal projects +# Executable and app helpers for libhal projects -# Add a single executable/demo +# Add a single executable/app # # Usage: -# libhal_add_executable(my_demo -# SOURCES demos/my_demo.cpp +# libhal_add_executable(my_app +# SOURCES apps/my_app.cpp # INCLUDES include/ # PACKAGES libhal-util libhal-lpc40 # LINK_LIBRARIES libhal::util libhal::lpc40 # ) function(libhal_add_executable TARGET_NAME) - cmake_parse_arguments(ARG - "" - "" - "SOURCES;INCLUDES;PACKAGES;LINK_LIBRARIES" + cmake_parse_arguments(ARG + "" + "" + "SOURCES;INCLUDES;PACKAGES;LINK_LIBRARIES" ${ARGN} ) - + if(NOT ARG_SOURCES) message(FATAL_ERROR "SOURCES is required for ${TARGET_NAME}") endif() - + # Create executable add_executable(${TARGET_NAME}) - + # Add sources target_sources(${TARGET_NAME} PRIVATE ${ARG_SOURCES}) - + # Add include directories if(ARG_INCLUDES) target_include_directories(${TARGET_NAME} PRIVATE ${ARG_INCLUDES}) endif() - + # Find packages foreach(PKG IN LISTS ARG_PACKAGES) find_package(${PKG} REQUIRED) endforeach() - + # Link libraries if(ARG_LINK_LIBRARIES) target_link_libraries(${TARGET_NAME} PRIVATE ${ARG_LINK_LIBRARIES}) endif() - + # Apply compile options - target_link_libraries(${TARGET_NAME} PRIVATE libhal::compile_options) - + target_link_libraries(${TARGET_NAME} PRIVATE ${LIBHAL_CXX_FLAGS}) + # Set C++ standard target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23) - + message(STATUS "Created executable: ${TARGET_NAME}") endfunction() -# Build multiple demos/executables at once +# Build multiple apps/executables at once # Keeps existing monolithic pattern for convenience # # Usage: -# libhal_build_demos( -# DEMOS demo1 demo2 demo3 +# libhal_build_apps( +# APPLICATIONS app1 app2 app3 # SOURCES src/common.cpp # Optional shared sources # INCLUDES include/ # Optional include directories # PACKAGES libhal-util libhal-lpc40 # LINK_LIBRARIES libhal::util libhal::lpc40 # ) # -# This will look for demos/demo1.cpp, demos/demo2.cpp, etc. -function(libhal_build_demos) - cmake_parse_arguments(ARG - "" - "" - "DEMOS;SOURCES;INCLUDES;PACKAGES;LINK_LIBRARIES" +# This will look for apps/app1.cpp, apps/app2.cpp, etc. +function(libhal_build_apps) + cmake_parse_arguments(ARG + "" + "" + "APPS;SOURCES;INCLUDES;PACKAGES;LINK_LIBRARIES" ${ARGN} ) - - if(NOT ARG_DEMOS) - message(FATAL_ERROR "DEMOS list is required") + + if(NOT ARG_APPS) + message(FATAL_ERROR "APPS list is required") endif() - - # Find packages once for all demos + + # Find packages once for all apps foreach(PKG IN LISTS ARG_PACKAGES) find_package(${PKG} REQUIRED) endforeach() - - message(STATUS "Building demos:") - - # Create each demo - foreach(DEMO_NAME IN LISTS ARG_DEMOS) - set(DEMO_FILE "demos/${DEMO_NAME}.cpp") - - # Check if demo file exists + + message(STATUS "Building apps:") + + # Create each app + foreach(DEMO_NAME IN LISTS ARG_APPS) + set(DEMO_FILE "apps/${DEMO_NAME}.cpp") + + # Check if app file exists if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DEMO_FILE}") message(WARNING "Demo file not found: ${DEMO_FILE}, skipping...") continue() endif() - - # Build the demo sources list + + # Build the app sources list set(DEMO_SOURCES ${DEMO_FILE}) if(ARG_SOURCES) list(APPEND DEMO_SOURCES ${ARG_SOURCES}) endif() - - # Create the demo using the granular function + + # Create the app using the granular function libhal_add_executable(${DEMO_NAME} SOURCES ${DEMO_SOURCES} INCLUDES ${ARG_INCLUDES} LINK_LIBRARIES ${ARG_LINK_LIBRARIES} ) - + message(STATUS " - ${DEMO_NAME}") endforeach() endfunction() diff --git a/v5/cmake/LibhalLibrary.cmake b/v5/cmake/LibhalLibrary.cmake index f1882c0..ca57625 100644 --- a/v5/cmake/LibhalLibrary.cmake +++ b/v5/cmake/LibhalLibrary.cmake @@ -23,16 +23,16 @@ include(GNUInstallDirs) # ) function(libhal_add_library TARGET_NAME) cmake_parse_arguments(ARG "" "" "SOURCES;MODULES" ${ARGN}) - + # Create the library add_library(${TARGET_NAME} STATIC) add_library(${TARGET_NAME}::${TARGET_NAME} ALIAS ${TARGET_NAME}) - + # Add sources if provided if(ARG_SOURCES) target_sources(${TARGET_NAME} PRIVATE ${ARG_SOURCES}) endif() - + # Add modules if provided if(ARG_MODULES) target_sources(${TARGET_NAME} PUBLIC @@ -41,10 +41,10 @@ function(libhal_add_library TARGET_NAME) FILES ${ARG_MODULES} ) endif() - + # Set C++23 standard for modules support target_compile_features(${TARGET_NAME} PUBLIC cxx_std_23) - + message(STATUS "Created library: ${TARGET_NAME}") endfunction() @@ -55,12 +55,12 @@ endfunction() # libhal_install_library(my_lib) # Uses library name as namespace function(libhal_install_library TARGET_NAME) cmake_parse_arguments(ARG "" "NAMESPACE" "" ${ARGN}) - + # Default namespace is the target name if(NOT ARG_NAMESPACE) set(ARG_NAMESPACE ${TARGET_NAME}) endif() - + # Install the library and its module files install( TARGETS ${TARGET_NAME} @@ -70,7 +70,7 @@ function(libhal_install_library TARGET_NAME) ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" CXX_MODULES_BMI DESTINATION "${CMAKE_INSTALL_LIBDIR}/bmi" ) - + # Install the CMake config files install( EXPORT ${TARGET_NAME}_targets @@ -79,81 +79,6 @@ function(libhal_install_library TARGET_NAME) DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME}" CXX_MODULES_DIRECTORY "cxx-modules" ) - - message(STATUS "Configured install for: ${TARGET_NAME} (namespace: ${ARG_NAMESPACE}::)") -endfunction() - -# Convenience function: Create and install a library in one call -# -# Usage: -# libhal_quick_library(my_lib -# SOURCES src/foo.cpp -# MODULES modules/bar.cppm -# NAMESPACE libhal -# ) -function(libhal_quick_library TARGET_NAME) - cmake_parse_arguments(ARG "" "NAMESPACE" "SOURCES;MODULES" ${ARGN}) - - # Create the library - libhal_add_library(${TARGET_NAME} - SOURCES ${ARG_SOURCES} - MODULES ${ARG_MODULES} - ) - - # Auto-apply compile options and asan - target_link_libraries(${TARGET_NAME} PRIVATE - libhal::compile_options - $<$>:libhal::asan> - ) - - # Install with namespace - if(ARG_NAMESPACE) - libhal_install_library(${TARGET_NAME} NAMESPACE ${ARG_NAMESPACE}) - else() - libhal_install_library(${TARGET_NAME}) - endif() -endfunction() -# Monolithic convenience function (keeps existing pattern) -# Combines library creation, testing, and installation -# -# Usage: -# libhal_test_and_make_library( -# LIBRARY_NAME libhal-actuator -# SOURCES src/rc_servo.cpp src/drc_v2.cpp -# TEST_SOURCES tests/rc_servo.test.cpp tests/drc.test.cpp -# PACKAGES libhal-mock -# LINK_LIBRARIES libhal::mock -# ) -function(libhal_test_and_make_library) - cmake_parse_arguments(ARG - "" - "LIBRARY_NAME" - "SOURCES;TEST_SOURCES;PACKAGES;LINK_LIBRARIES" - ${ARGN} - ) - - if(NOT ARG_LIBRARY_NAME) - message(FATAL_ERROR "LIBRARY_NAME is required") - endif() - - # Create library - libhal_add_library(${ARG_LIBRARY_NAME} - SOURCES ${ARG_SOURCES} - ) - - # Apply compile options - target_link_libraries(${ARG_LIBRARY_NAME} PRIVATE libhal::compile_options) - - # Install library - libhal_install_library(${ARG_LIBRARY_NAME}) - - # Add tests if provided - if(ARG_TEST_SOURCES) - libhal_add_tests(${ARG_LIBRARY_NAME} - TEST_SOURCES ${ARG_TEST_SOURCES} - PACKAGES ${ARG_PACKAGES} - LINK_LIBRARIES ${ARG_LINK_LIBRARIES} - ) - endif() + message(STATUS "Configured install for: ${TARGET_NAME} (namespace: ${ARG_NAMESPACE}::)") endfunction() diff --git a/v5/cmake/LibhalTesting.cmake b/v5/cmake/LibhalTesting.cmake index 7fde62c..1804754 100644 --- a/v5/cmake/LibhalTesting.cmake +++ b/v5/cmake/LibhalTesting.cmake @@ -72,6 +72,7 @@ function(libhal_add_tests TARGET_NAME) find_package(${PKG} REQUIRED) endforeach() + message(STATUS "LIBHAL: List of Tests to Build:") # Create test executable for each test file foreach(TEST_FILE IN LISTS TEST_LIST) # Extract test name from file path @@ -101,11 +102,14 @@ function(libhal_add_tests TARGET_NAME) target_link_libraries(${TEST_TARGET} PRIVATE Boost::ut ${TARGET_NAME} - libhal::compile_options - libhal::asan ${ARG_LINK_LIBRARIES} ) + # Add CXX and ASAN Flags + target_compile_options(${TEST_TARGET} PRIVATE ${LIBHAL_CXX_FLAGS} + ${LIBHAL_ASAN_FLAGS}) + target_link_options(${TEST_TARGET} PRIVATE ${LIBHAL_ASAN_FLAGS}) + # Register with CTest add_test(NAME ${TEST_TARGET} COMMAND ${TEST_TARGET}) diff --git a/v5/examples/demos_CMakeLists.txt b/v5/examples/demos_CMakeLists.txt index 4249fac..be4f80c 100644 --- a/v5/examples/demos_CMakeLists.txt +++ b/v5/examples/demos_CMakeLists.txt @@ -26,8 +26,8 @@ libhal_project_init(demos) # ============================================================================== # From libhal-cmake-helpers -libhal_build_demos( - DEMOS +libhal_build_apps( + APPLICATIONS drc_v2 drc_v2_resources mc_x_v2 @@ -35,14 +35,14 @@ libhal_build_demos( mc_x_v2_resources rc_servo rc_servo16 - + INCLUDES . - + PACKAGES libhal-actuator libhal-expander - + LINK_LIBRARIES libhal::actuator libhal::expander @@ -59,12 +59,12 @@ libhal_build_demos( # PACKAGES libhal-actuator libhal-expander # LINK_LIBRARIES libhal::actuator libhal::expander # ) -# +# # libhal_add_executable(mc_x_v2 # SOURCES demos/mc_x_v2.cpp # INCLUDES . # PACKAGES libhal-actuator libhal-expander # LINK_LIBRARIES libhal::actuator libhal::expander # ) -# +# # # ... etc for other demos diff --git a/v5/examples/libhal-actuator_CMakeLists.txt b/v5/examples/libhal-actuator_CMakeLists.txt index 638a463..dcb2271 100644 --- a/v5/examples/libhal-actuator_CMakeLists.txt +++ b/v5/examples/libhal-actuator_CMakeLists.txt @@ -18,77 +18,25 @@ cmake_minimum_required(VERSION 3.15) # Source: https://github.com/libhal/libhal-cmake-helpers find_package(LibhalBuild REQUIRED) -# From libhal-cmake-helpers libhal_project_init(libhal-actuator) -# ============================================================================== -# Library - Option 1: Use the monolithic convenience function -# ============================================================================== - -# From libhal-cmake-helpers -libhal_test_and_make_library( - LIBRARY_NAME libhal-actuator - +libhal_add_library(libhal-actuator SOURCES src/rc_servo.cpp src/smart_servo/rmd/drc_v2.cpp src/smart_servo/rmd/mc_x_v2.cpp - +) + +libhal_apply_compile_options(libhal-actuator) + +libhal_install_library(libhal-actuator) + +libhal_add_tests(libhal-actuator TEST_SOURCES tests/main.test.cpp tests/rc_servo.test.cpp tests/smart_servo/rmd/drc.test.cpp tests/smart_servo/rmd/mc_x.test.cpp - - PACKAGES - libhal-mock - - LINK_LIBRARIES - libhal::mock + PACKAGES libhal-mock + LINK_LIBRARIES libhal::mock ) - -# ============================================================================== -# OR Library - Option 2: Granular approach -# ============================================================================== - -# From libhal-cmake-helpers -# libhal_add_library(libhal-actuator -# SOURCES -# src/rc_servo.cpp -# src/smart_servo/rmd/drc_v2.cpp -# src/smart_servo/rmd/mc_x_v2.cpp -# ) -# -# target_link_libraries(libhal-actuator PRIVATE libhal::compile_options) -# -# libhal_install_library(libhal-actuator) -# -# libhal_add_tests(libhal-actuator -# TEST_SOURCES -# tests/main.test.cpp -# tests/rc_servo.test.cpp -# tests/smart_servo/rmd/drc.test.cpp -# tests/smart_servo/rmd/mc_x.test.cpp -# PACKAGES libhal-mock -# LINK_LIBRARIES libhal::mock -# ) - -# ============================================================================== -# OR Library - Option 3: Quick convenience -# ============================================================================== - -# From libhal-cmake-helpers -# libhal_quick_library(libhal-actuator -# SOURCES -# src/rc_servo.cpp -# src/smart_servo/rmd/drc_v2.cpp -# src/smart_servo/rmd/mc_x_v2.cpp -# ) -# -# libhal_add_tests(libhal-actuator -# TEST_SOURCES -# tests/main.test.cpp -# tests/rc_servo.test.cpp -# PACKAGES libhal-mock -# LINK_LIBRARIES libhal::mock -# ) diff --git a/v5/examples/strong_ptr_CMakeLists.txt b/v5/examples/strong_ptr_CMakeLists.txt index 416c440..9db64d6 100644 --- a/v5/examples/strong_ptr_CMakeLists.txt +++ b/v5/examples/strong_ptr_CMakeLists.txt @@ -22,33 +22,19 @@ find_package(LibhalBuild REQUIRED) # From libhal-cmake-helpers libhal_project_init(strong_ptr) -# ============================================================================== -# Library - Option 1: Granular approach (full control) -# ============================================================================== - # From libhal-cmake-helpers libhal_add_library(strong_ptr MODULES modules/strong_ptr.cppm ) # Opt-in to libhal compile options -target_link_libraries(strong_ptr PRIVATE libhal::compile_options) +libhal_apply_compile_options(libhal-strong_ptr) # From libhal-cmake-helpers libhal_install_library(strong_ptr NAMESPACE libhal ) -# ============================================================================== -# OR Library - Option 2: Convenience wrapper (opinionated) -# ============================================================================== - -# From libhal-cmake-helpers - combines the above into one call -# libhal_quick_library(strong_ptr -# MODULES modules/strong_ptr.cppm -# NAMESPACE libhal -# ) - # ============================================================================== # Unit Testing # ============================================================================== From 9f3e50ee545cccd3d83bf8fe5094d1d0cb5b4f79 Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Sun, 8 Feb 2026 19:32:13 -0800 Subject: [PATCH 5/6] Fix unit testing --- v5/cmake/LibhalTesting.cmake | 37 +++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/v5/cmake/LibhalTesting.cmake b/v5/cmake/LibhalTesting.cmake index 1804754..0cb79e9 100644 --- a/v5/cmake/LibhalTesting.cmake +++ b/v5/cmake/LibhalTesting.cmake @@ -32,25 +32,31 @@ # MODULES tests/util.cppm # PACKAGES libhal-mock # ) -function(libhal_add_tests TARGET_NAME) - cmake_parse_arguments(ARG - "" - "MODULES" - "TEST_SOURCES;TEST_NAMES;PACKAGES;LINK_LIBRARIES" - ${ARGN} - ) - +# Public macro that enables testing in caller's scope, then delegates to internal function +macro(libhal_add_tests TARGET_NAME) # Skip tests when cross-compiling if(CMAKE_CROSSCOMPILING) message(STATUS "Cross-compiling, skipping tests for ${TARGET_NAME}") - return() + else() + # Enable testing in caller's scope (must be in macro, not function) + include(CTest) + enable_testing() + + # Delegate to internal function for proper variable scoping + _libhal_add_tests_impl(${TARGET_NAME} ${ARGN}) endif() +endmacro() - message(STATUS "Adding tests for ${TARGET_NAME}") +# Internal function to add tests (keeps variables scoped) +function(_libhal_add_tests_impl TARGET_NAME) + cmake_parse_arguments(ARG + "" + "" + "TEST_SOURCES;TEST_NAMES;PACKAGES;LINK_LIBRARIES;MODULES" + ${ARGN} + ) - # Enable testing - include(CTest) - enable_testing() + message(STATUS "Adding tests for ${TARGET_NAME}") # Find boost-ut for testing find_package(ut REQUIRED) @@ -106,8 +112,9 @@ function(libhal_add_tests TARGET_NAME) ) # Add CXX and ASAN Flags - target_compile_options(${TEST_TARGET} PRIVATE ${LIBHAL_CXX_FLAGS} - ${LIBHAL_ASAN_FLAGS}) + target_compile_options( + ${TEST_TARGET} + PRIVATE ${LIBHAL_CXX_FLAGS} ${LIBHAL_ASAN_FLAGS}) target_link_options(${TEST_TARGET} PRIVATE ${LIBHAL_ASAN_FLAGS}) # Register with CTest From a4b5c0d3b1834a17c370628f417193df117b950d Mon Sep 17 00:00:00 2001 From: Khalil Estell Date: Sun, 8 Feb 2026 19:46:43 -0800 Subject: [PATCH 6/6] Removed project form project_init & aligned readme --- README.md | 50 +++++++++++++++++++--------- v5/cmake/LibhalCMakeUtilConfig.cmake | 2 -- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a29701b..60931e2 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ def build_requirements(self): ```cmake cmake_minimum_required(VERSION 4.0) +project(my_project LANGUAGES CXX) + # Find the helpers package find_package(LibhalCMakeUtil REQUIRED) @@ -44,7 +46,7 @@ libhal_install_library(my_library NAMESPACE libhal) # Add tests libhal_add_tests(my_library TEST_NAMES foo bar baz - MODULE tests/util.cppm + MODULES tests/util.cppm ) ``` @@ -56,7 +58,6 @@ libhal_add_tests(my_library What it does: -- Calls `project()` with C++ language - Enables compile_commands.json export - Checks for Ninja/Visual Studio generator (required for modules) - Sets up clang-tidy if enabled @@ -80,7 +81,7 @@ libhal_apply_compile_options(my_lib) Flags included: -- GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -Wpedantic -fexceptions -fno-rtti` +- GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -pedantic -fexceptions -fno-rtti -Wno-unused-command-line-argument` - MSVC: `/W4 /WX /EHsc /permissive- /GR-` #### `libhal_apply_asan(TARGET_NAME)` @@ -228,7 +229,7 @@ libhal_create_binary_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/app.h ``` Without the `OUTPUT_DIR` parameter, the file will have the same name as the -target but with the extension `.hex`. +target but with the extension `.bin`. ### `libhal_create_disassembly_from(TARGET_NAME)` @@ -272,13 +273,17 @@ cmake -DLIBHAL_CLANG_TIDY_FIX=ON .. ```cmake cmake_minimum_required(VERSION 4.0) -find_package(LibhalBuild REQUIRED) + +project(strong_ptr LANGUAGES CXX) + +find_package(LibhalCMakeUtil REQUIRED) libhal_project_init(strong_ptr) # Create library with modules libhal_add_library(strong_ptr - MODULES modules/strong_ptr.cppm + MODULES + modules/strong_ptr.cppm ) # Opt-in to compile options @@ -289,21 +294,31 @@ 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 - MODULE tests/util.cppm + TEST_NAMES + enable_from_this + strong_ptr + mixins + weak_ptr + optional_ptr + + MODULES + tests/util.cppm ) ``` ### Example 2: Demo Applications ```cmake -cmake_minimum_required(VERSION 3.15) -find_package(LibhalBuild REQUIRED) +cmake_minimum_required(VERSION 4.0) + +project(my_app LANGUAGES CXX) + +find_package(LibhalCMakeUtil REQUIRED) libhal_project_init(apps) libhal_build_apps( - DEMOS + APPS drc_v2 mc_x_v2 rc_servo @@ -321,7 +336,10 @@ libhal_build_apps( ```cmake cmake_minimum_required(VERSION 4.0) -find_package(LibhalBuild REQUIRED) + +project(my_advanced_lib LANGUAGES CXX) + +find_package(LibhalCMakeUtil REQUIRED) # Initialize project libhal_project_init(my_advanced_lib) @@ -331,12 +349,14 @@ 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 + FILES + modules/foo.cppm + modules/bar.cppm PRIVATE src/impl.cpp ) # Opt-in to compile options -target_link_libraries(my_advanced_lib PRIVATE LIBHAL_CXX_FLAGS) +target_compile_options(my_advanced_lib PRIVATE ${LIBHAL_CXX_FLAGS}) # Custom compile definitions target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG) @@ -351,7 +371,7 @@ This package provides **granular building blocks with optional convenience wrapp - **Granular functions** - Full control for complex needs - **Convenience wrappers** - Sensible defaults for common patterns -- **Opt-in compile options** - Via interface targets +- **Opt-in compile options** - Via apply functions & `LIBHAL_*_FLAG` variables. - **Explicit, not magical** - Clear what's happening - **Composable** - Mix and match as needed diff --git a/v5/cmake/LibhalCMakeUtilConfig.cmake b/v5/cmake/LibhalCMakeUtilConfig.cmake index 6022769..6075117 100644 --- a/v5/cmake/LibhalCMakeUtilConfig.cmake +++ b/v5/cmake/LibhalCMakeUtilConfig.cmake @@ -33,8 +33,6 @@ function(libhal_project_init PROJECT_NAME) set(CMAKE_COLOR_DIAGNOSTICS ON PARENT_SCOPE) set(CMAKE_CXX_SCAN_FOR_MODULES ON PARENT_SCOPE) - project(${PROJECT_NAME} LANGUAGES CXX) - # Require Ninja or Visual Studio for C++20 modules if(NOT CMAKE_GENERATOR MATCHES "Ninja|Visual Studio") message(FATAL_ERROR "C++20 modules require Ninja or Visual Studio generator")