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..60931e2 100644 --- a/README.md +++ b/README.md @@ -1,269 +1,384 @@ # 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 convenient +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) -This package comes with some options to customize its behavior. You can set -these options in the `tool_requires` function. +project(my_project LANGUAGES CXX) -```python -def build_requirements(self): - self.tool_requires("libhal-cmake-util/2.2.0", - options={ - "add_build_outputs": True, - "optimize_debug_build": True, - }) +# Find the helpers package +find_package(LibhalCMakeUtil REQUIRED) + +# Initialize your project (required) +libhal_project_init(my_library) + +# Create a library target named `my_library` with the following source and +# module files. +libhal_add_library(my_library + SOURCES src/foo.cpp + MODULES modules/bar.cppm +) + +# Apply standard libhal compiler options +libhal_apply_compile_options(my_library) + +# Setup library installation info +libhal_install_library(my_library NAMESPACE libhal) + +# Add tests +libhal_add_tests(my_library + TEST_NAMES foo bar baz + MODULES tests/util.cppm +) ``` -### Option: `add_build_outputs` (Default: `True`) +## Core Functions + +### `libhal_project_init(PROJECT_NAME)` + +**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. +- Enables compile_commands.json export +- Checks for Ninja/Visual Studio generator (required for modules) +- Sets up clang-tidy if enabled +- Adds compile_commands.json copy target + +```cmake +libhal_project_init(my_project) +``` -- `libhal_generate_binary(elf_file)`: turns the provided elf file into a binary - (.bin) file, a format often used for device flashing. +### Standard Compile Option Functions -- `libhal_disassemble(elf_file)`: breaks down the elf file into assembly code, -- providing a platform for detailed analysis. +After `libhal_project_init()`, these flag attachment files become available: -- `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_apply_compile_options(TARGET_NAME)` -- `libhal_print_size_info(elf_file)`: prints out the size - information for the different sections of the elf file. +Standard compile flags for libhal projects. -- `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()` +```cmake +libhal_apply_compile_options(my_lib) +``` -- `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()` +Flags included: -### Option: `optimize_debug_build` (Default: `True`) +- GCC/Clang: `-g -Werror -Wall -Wextra -Wshadow -pedantic -fexceptions -fno-rtti -Wno-unused-command-line-argument` +- MSVC: `/W4 /WX /EHsc /permissive- /GR-` -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`. +#### `libhal_apply_asan(TARGET_NAME)` -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. +AddressSanitizer support (non-Windows only): -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. +```cmake +libhal_apply_asan(my_lib) +``` -**NOTE**: this field is **REQUIRED** by all open source libhal libraries in -order to reduce their library's binary size in debug mode. +This API is safe to use on Windows. Executing it on a Windows machine does +nothing. -## Build functions +This is recommended only for unit and integration tests. -This package automatically injects libhal cmake utility functions: +## Library 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 target 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") +- 1st argument is the library name (my_lib in the above example) +- `SOURCES` - List of .cpp files +- `MODULES` - List of .cppm module files + +You may use the target `my_lib` elsewhere in the code with standard CMake commands like: + +```cmake +target_compiler_options(my_lib PRIVATE -Wall) ``` -### `libhal_unit_test()` +### `libhal_install_library(TARGET_NAME)` -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. +Configures library installation with CMake config files. ```cmake -libhal_unit_test([SOURCES ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ]) +libhal_install_library(my_lib 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: +Arguments: -```python - def build_requirements(self): - self.tool_requires("libhal-cmake-util/2.2.0") - self.test_requires("boost-ext-ut/1.1.9") +- `NAMESPACE` (optional) - Namespace for exported target (default: library name) + +## 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 + MODULES tests/util.cppm +) +``` + +#### Mode 2: Generate filenames from names + +```cmake +libhal_add_tests(my_lib + TEST_NAMES foo bar baz + MODULES tests/util.cppm +) +``` + +Looks for `tests/foo.test.cpp`, `tests/bar.test.cpp`, etc. + +#### Arguments + +Arguments: + +- `TEST_SOURCES` - Explicit list of test files +- `TEST_NAMES` - Generate test filenames (tests/NAME.test.cpp) +- `MODULES` - Optional additional modules 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/app. + +```cmake +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_make_library()` +### `libhal_build_apps()` -Builds libhal libraries. Use this when unit tests are not available or necessary -but a package must be built. +Builds multiple apps at once. ```cmake -libhal_make_library([LIBRARY_NAME ] - [SOURCES ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ]) +libhal_build_apps( + APPS app1 app2 app3 + SOURCES src/common.cpp + INCLUDES include/ + PACKAGES libhal-util libhal-expander + LINK_LIBRARIES libhal::util libhal::expander +) ``` -- `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 +Looks for `apps/app1.cpp`, `apps/app2.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_create_hex_file_from(my_firmware) +libhal_create_hex_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/app.hex") ``` -Where main contains the startup code and calls a common function that is -implemented across the demos in the `applications`` directory. +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_build_demos([LIBRARY_NAME ] - [INCLUDES ] - [PACKAGES ] - [LINK_LIBRARIES ] - [LINK_FLAGS ] - DISABLE_CLANG_TIDY) +libhal_create_binary_file_from(my_firmware) +libhal_create_binary_file_from(my_firmware OUTPUT_DIR "${CMAKE_BINARY_DIR}/app.hex") ``` -- `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. +Without the `OUTPUT_DIR` parameter, the file will have the same name as the +target but with the extension `.bin`. -## Contributing +### `libhal_create_disassembly_from(TARGET_NAME)` -See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details. +Creates disassembly files (.S and .demangled.S). + +```cmake +libhal_create_disassembly_from(my_firmware) +``` + +### `libhal_create_disassembly_with_source_from(TARGET_NAME)` + +Creates a disassembly 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) + +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 +) + +# Opt-in to compile options +libhal_apply_compile_options(strong_ptr) + +# Install +libhal_install_library(strong_ptr NAMESPACE libhal) + +# Add tests +libhal_add_tests(strong_ptr + TEST_NAMES + enable_from_this + strong_ptr + mixins + weak_ptr + optional_ptr + + MODULES + tests/util.cppm +) +``` + +### Example 2: Demo Applications + +```cmake +cmake_minimum_required(VERSION 4.0) + +project(my_app LANGUAGES CXX) + +find_package(LibhalCMakeUtil REQUIRED) + +libhal_project_init(apps) + +libhal_build_apps( + APPS + drc_v2 + mc_x_v2 + rc_servo + INCLUDES . + PACKAGES + libhal-actuator + libhal-expander + LINK_LIBRARIES + libhal::actuator + libhal::expander +) +``` + +### Example 3: Full Manual Control + +```cmake +cmake_minimum_required(VERSION 4.0) + +project(my_advanced_lib LANGUAGES CXX) + +find_package(LibhalCMakeUtil 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_compile_options(my_advanced_lib PRIVATE ${LIBHAL_CXX_FLAGS}) + +# Custom compile definitions +target_compile_definitions(my_advanced_lib PUBLIC MY_CUSTOM_FLAG) + +# Install +libhal_install_library(my_advanced_lib NAMESPACE mycompany) +``` + +## 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 apply functions & `LIBHAL_*_FLAG` variables. +- **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/cmake/build.cmake b/v4/cmake/build.cmake similarity index 99% rename from cmake/build.cmake rename to v4/cmake/build.cmake index 7eb2ff4..76510ee 100644 --- a/cmake/build.cmake +++ b/v4/cmake/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/v4/cmake/build_outputs.cmake similarity index 100% rename from cmake/build_outputs.cmake rename to v4/cmake/build_outputs.cmake diff --git a/cmake/clang-tidy.conf b/v4/cmake/clang-tidy.conf similarity index 100% rename from cmake/clang-tidy.conf rename to v4/cmake/clang-tidy.conf diff --git a/cmake/colors.cmake b/v4/cmake/colors.cmake similarity index 100% rename from cmake/colors.cmake rename to v4/cmake/colors.cmake diff --git a/cmake/optimize_debug_build.cmake b/v4/cmake/optimize_debug_build.cmake similarity index 100% rename from cmake/optimize_debug_build.cmake rename to v4/cmake/optimize_debug_build.cmake diff --git a/conanfile.py b/v4/conanfile.py similarity index 100% rename from conanfile.py rename to v4/conanfile.py diff --git a/v5/CMakeLists.txt b/v5/CMakeLists.txt new file mode 100644 index 0000000..27500da --- /dev/null +++ b/v5/CMakeLists.txt @@ -0,0 +1,48 @@ +# 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) + +project(libhal-cmake-helpers LANGUAGES NONE) + +include(GNUInstallDirs) + +# Install all CMake modules +install( + FILES + cmake/LibhalCMakeUtilConfig.cmake + cmake/LibhalCompileOptions.cmake + cmake/LibhalLibrary.cmake + cmake/LibhalExecutable.cmake + cmake/LibhalTesting.cmake + cmake/LibhalClangTidy.cmake + cmake/LibhalBinUtils.cmake + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalCMakeUtil +) + +# Create a version file +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/LibhalCMakeUtilConfigVersion.cmake" + VERSION 1.0.0 + COMPATIBILITY SameMajorVersion +) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/LibhalCMakeUtilConfigVersion.cmake" + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/LibhalCMakeUtil +) diff --git a/v5/cmake/LibhalBinUtils.cmake b/v5/cmake/LibhalBinUtils.cmake new file mode 100644 index 0000000..075e3a8 --- /dev/null +++ b/v5/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/v5/cmake/LibhalCMakeUtilConfig.cmake b/v5/cmake/LibhalCMakeUtilConfig.cmake new file mode 100644 index 0000000..6075117 --- /dev/null +++ b/v5/cmake/LibhalCMakeUtilConfig.cmake @@ -0,0 +1,55 @@ +# 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) + + # 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() + + # 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/v5/cmake/LibhalClangTidy.cmake b/v5/cmake/LibhalClangTidy.cmake new file mode 100644 index 0000000..74251f6 --- /dev/null +++ b/v5/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/v5/cmake/LibhalCompileOptions.cmake b/v5/cmake/LibhalCompileOptions.cmake new file mode 100644 index 0000000..762a56c --- /dev/null +++ b/v5/cmake/LibhalCompileOptions.cmake @@ -0,0 +1,55 @@ +# 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 + +# 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" +) + +set(LIBHAL_ASAN_FLAGS + $<$>,$>: + -fsanitize=address> + CACHE INTERNAL "libhal AddressSanitizer flags" +) + +# 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() + + 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 new file mode 100644 index 0000000..925d8a9 --- /dev/null +++ b/v5/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 app helpers for libhal projects + +# Add a single executable/app +# +# Usage: +# 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" + ${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_CXX_FLAGS}) + + # Set C++ standard + target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23) + + message(STATUS "Created executable: ${TARGET_NAME}") +endfunction() + +# Build multiple apps/executables at once +# Keeps existing monolithic pattern for convenience +# +# Usage: +# 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 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_APPS) + message(FATAL_ERROR "APPS list is required") + endif() + + # Find packages once for all apps + foreach(PKG IN LISTS ARG_PACKAGES) + find_package(${PKG} REQUIRED) + endforeach() + + 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 app sources list + set(DEMO_SOURCES ${DEMO_FILE}) + if(ARG_SOURCES) + list(APPEND DEMO_SOURCES ${ARG_SOURCES}) + endif() + + # 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 new file mode 100644 index 0000000..ca57625 --- /dev/null +++ b/v5/cmake/LibhalLibrary.cmake @@ -0,0 +1,84 @@ +# 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() diff --git a/v5/cmake/LibhalTerminalColor.cmake b/v5/cmake/LibhalTerminalColor.cmake new file mode 100644 index 0000000..0cf6fda --- /dev/null +++ b/v5/cmake/LibhalTerminalColor.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/v5/cmake/LibhalTesting.cmake b/v5/cmake/LibhalTesting.cmake new file mode 100644 index 0000000..0cb79e9 --- /dev/null +++ b/v5/cmake/LibhalTesting.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. + +# 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 +# ) +# 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}") + 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() + +# 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} + ) + + message(STATUS "Adding tests for ${TARGET_NAME}") + + # 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() + + 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 + 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} + ${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}) + + message(STATUS " - ${TEST_TARGET}") + endforeach() +endfunction() diff --git a/v5/conanfile.py b/v5/conanfile.py new file mode 100644 index 0000000..d79820d --- /dev/null +++ b/v5/conanfile.py @@ -0,0 +1,50 @@ +# 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.files import copy +from conan.tools.layout import basic_layout +from pathlib import Path + + +required_conan_version = ">=2.0.6" + + +class LibhalCMakeUtilConan(ConanFile): + name = "libhal-cmake-util" + license = "Apache-2.0" + homepage = "https://github.com/libhal/libhal-cmake-util" + description = "CMake helper functions and utilities for libhal projects" + topics = ("cmake", "build-helpers", "libhal", "embedded") + exports_sources = "cmake/*", "LICENSE" + no_copy_source = True + + def package_id(self): + self.info.clear() + + def layout(self): + basic_layout(self) + + def package(self): + 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): + # Add cmake/ directory to builddirs so find_package(LibhalCMakeUtil) works + cmake_dir = str(Path(self.package_folder) / "cmake") + self.cpp_info.builddirs = [cmake_dir] diff --git a/v5/examples/demos_CMakeLists.txt b/v5/examples/demos_CMakeLists.txt new file mode 100644 index 0000000..be4f80c --- /dev/null +++ b/v5/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_apps( + APPLICATIONS + 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/v5/examples/libhal-actuator_CMakeLists.txt b/v5/examples/libhal-actuator_CMakeLists.txt new file mode 100644 index 0000000..dcb2271 --- /dev/null +++ b/v5/examples/libhal-actuator_CMakeLists.txt @@ -0,0 +1,42 @@ +# 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) + +libhal_project_init(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 +) diff --git a/v5/examples/strong_ptr_CMakeLists.txt b/v5/examples/strong_ptr_CMakeLists.txt new file mode 100644 index 0000000..9db64d6 --- /dev/null +++ b/v5/examples/strong_ptr_CMakeLists.txt @@ -0,0 +1,52 @@ +# 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) + +# From libhal-cmake-helpers +libhal_add_library(strong_ptr + MODULES modules/strong_ptr.cppm +) + +# Opt-in to libhal compile options +libhal_apply_compile_options(libhal-strong_ptr) + +# From libhal-cmake-helpers +libhal_install_library(strong_ptr + 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 +)