diff --git a/.circleci/config.yml b/.circleci/config.yml index d714b6b74..78108e94e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,7 +22,7 @@ commands: jobs: build-linux-gcc: machine: - image: ubuntu-2204:2022.04.2 #https://circleci.com/developer/machine/image/ubuntu-2204 pick LTS + image: ubuntu-2204:2024.04.4 #https://circleci.com/developer/machine/image/ubuntu-2204 pick LTS steps: - checkout - install_dependencies @@ -32,7 +32,7 @@ jobs: build-linux-clang: machine: - image: ubuntu-2204:2022.04.2 #https://circleci.com/developer/machine/image/ubuntu-2204 pick LTS + image: ubuntu-2204:2024.04.4 #https://circleci.com/developer/machine/image/ubuntu-2204 pick LTS steps: - checkout - install_dependencies: @@ -44,8 +44,8 @@ jobs: build-mac-gcc: macos: - xcode: 12.5.1 # https://circleci.com/docs/using-macos/#supported-xcode-versions https://en.wikipedia.org/wiki/MacOS_version_history#Releases - resource_class: macos.x86.medium.gen2 + xcode: 16.4.0 # https://circleci.com/docs/using-macos/#supported-xcode-versions https://en.wikipedia.org/wiki/MacOS_version_history#Releases + resource_class: macos.m1.medium.gen1 steps: - checkout - install_dependencies @@ -55,8 +55,8 @@ jobs: build-mac-clang: macos: - xcode: 12.5.1 # https://circleci.com/docs/using-macos/#supported-xcode-versions https://en.wikipedia.org/wiki/MacOS_version_history#Releases - resource_class: macos.x86.medium.gen2 + xcode: 16.4.0 # https://circleci.com/docs/using-macos/#supported-xcode-versions https://en.wikipedia.org/wiki/MacOS_version_history#Releases + resource_class: macos.m1.medium.gen1 steps: - checkout - install_dependencies: @@ -73,10 +73,10 @@ jobs: steps: - checkout - run: powershell.exe .\install_dependencies.ps1 - - run: .\mingw64\bin\mingw32-make all - - run: .\mingw64\opt\bin\python3.exe .\test\run_unit_tests.py -m"..\\..\\mingw64\\bin\\mingw32-make" - # - store_artifacts: - # path: ./Release/MINGW64/erpcgen/erpcgen.exe + - run: C:\Users\circleci\project\cmake_installation\bin\cmake.exe --preset mingw64 -B ./build + - run: C:\Users\circleci\project\cmake_installation\bin\cmake.exe --build ./build --target erpcgen + - store_artifacts: + path: ./build/erpcgen/erpcgen.exe build-windows-VS: executor: diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 95f017fdc..9f37eafef 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -11,5 +11,12 @@ jobs: - uses: DoozyX/clang-format-lint-action@v0.16.2 with: source: '.' - exclude: './examples/zephyr/matrix_multiply_rpmsglite/remote/src/service ./examples/zephyr/matrix_multiply_rpmsglite/src/service ./examples/zephyr/matrix_multiply_uart/src/service' + exclude: './examples/zephyr/matrix_multiply_mbox/remote/src/service + ./examples/zephyr/matrix_multiply_mbox/src/service + ./examples/zephyr/matrix_multiply_rpmsglite/remote/src/service + ./examples/zephyr/matrix_multiply_rpmsglite/src/service + ./examples/zephyr/matrix_multiply_tcp/src/service + ./examples/zephyr/matrix_multiply_uart/src/service + ./examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service + ./examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service' clangFormatVersion: 16 diff --git a/.gitignore b/.gitignore index 09fc80409..1b0ce80fa 100644 --- a/.gitignore +++ b/.gitignore @@ -100,7 +100,6 @@ kds iar mdk lpcx -mcux tags format-files-to-commit.rb diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..2a4845069 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,379 @@ +# Changelog eRPC + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Fixed +- Python code of the eRPC infrastructure was updated to match the proper python code style, add type annotations and improve readability. + +## [1.14.0] + +### Added + +- Added Cmake/Kconfig support. +- Made java code jdk11 compliant, GitHub PR #432. +- Added imxrt1186 support into mu transport layer. +- erpcgen: Added assert for listType before usage, GitHub PR #406. + +### Fixed + +- eRPC: Sources reformatted. +- erpc: Fixed typo in semaphore get (mutex -> semaphore), and write it can fail in case of timeout, GitHub PR #446. +- erpc: Free the arbitrated client token from client manager, GitHub PR #444. +- erpc: Fixed Makefile, install the erpc_simple_server header, GitHub PR #447. +- erpc_python: Fixed possible AttributeError and OSError on calling TCPTransport.close(), GitHub PR #438. +- Examples and tests consolidated. + +## [1.13.0] + +### Added + +- erpc: Add BSD-3 license to endianness agnostic files, GitHub PR #417. +- eRPC: Add new Zephyr-related transports (zephyr_uart, zephyr_mbox). +- eRPC: Add new Zephyr-related examples. + +### Fixed + +- eRPC,erpcgen: Fixing/improving markdown files, GitHub PR #395. +- eRPC: Fix Python client TCPTransports not being able to close, GitHub PR #390. +- eRPC,erpcgen: Align switch brackets, GitHub PR #396. +- erpc: Fix zephyr uart transport, GitHub PR #410. +- erpc: UART ZEPHYR Transport stop to work after a few transactions when using USB-CDC resolved, GitHub PR #420. + +### Removed + +- eRPC,erpcgen: Remove cstbool library, GitHub PR #403. + +## [1.12.0] + +### Added + +- eRPC: Add dynamic/static option for transport init, GitHub PR #361. +- eRPC,erpcgen: Winsock2 support, GitHub PR #365. +- eRPC,erpcgen: Feature/support multiple clients, GitHub PR #271. +- eRPC,erpcgen: Feature/buffer head - Framed transport header data stored in MessageBuffer, GitHub PR #378. +- eRPC,erpcgen: Add experimental Java support. + +### Fixed + +- eRPC: Fix receive error value for spidev, GitHub PR #363. +- eRPC: UartTransport::init adaptation to changed driver. +- eRPC: Fix typo in assert, GitHub PR #371. +- eRPC,erpcgen: Move enums to enum classes, GitHub PR #379. +- eRPC: Fixed rpmsg tty transport to work with serial transport, GitHub PR #373. + +## [1.11.0] + +### Fixed + +- eRPC: Makefiles update, GitHub PR #301. +- eRPC: Resolving warnings in Python, GitHub PR #325. +- eRPC: Python3.8 is not ready for usage of typing.Any type, GitHub PR #325. +- eRPC: Improved codec function to use reference instead of address, GitHub PR #324. +- eRPC: Fix NULL check for pending client creation, GitHub PR #341. +- eRPC: Replace sprintf with snprintf, GitHub PR #343. +- eRPC: Use MU_SendMsg blocking call in MU transport. +- eRPC: New LPSPI and LPI2C transport layers. +- eRPC: Freeing static objects, GitHub PR #353. +- eRPC: Fixed casting in deinit functions, GitHub PR #354. +- eRPC: Align LIBUSBSIO.GetNumPorts API use with libusbsio python module v. 2.1.11. +- erpcgen: Renamed temp variable to more generic one, GitHub PR #321. +- erpcgen: Add check that string read is not more than max length, GitHub PR #328. +- erpcgen: Move to g++ in pytest, GitHub PR #335. +- erpcgen: Use build=release for make, GitHub PR #334. +- erpcgen: Removed boost dependency, GitHub PR #346. +- erpcgen: Mingw support, GitHub PR #344. +- erpcgen: VS build update, GitHub PR #347. +- erpcgen: Modified name for common types macro scope, GitHub PR #337. +- erpcgen: Fixed memcpy for template, GitHub PR #352. +- eRPC,erpcgen: Change default build target to release + adding artefacts, GitHub PR #334. +- eRPC,erpcgen: Remove redundant includes, GitHub PR #338. +- eRPC,erpcgen: Many minor code improvements, GitHub PR #323. + +## [1.10.0] + +### Fixed + +- eRPC: MU transport layer switched to blocking MU_SendMsg() API use. + +## [1.10.0] + +### Added + +- eRPC: Add TCP_NODELAY option to python, GitHub PR #298. + +### Fixed + +- eRPC: MUTransport adaptation to new supported SoCs. +- eRPC: Simplifying CI with installing dependencies using shell script, GitHub PR #267. +- eRPC: Using event for waiting for sock connection in TCP python server, formatting python code, C specific includes, GitHub PR #269. +- eRPC: Endianness agnostic update, GitHub PR #276. +- eRPC: Assertion added for functions which are returning status on freeing memory, GitHub PR #277. +- eRPC: Fixed closing arbitrator server in unit tests, GitHub PR #293. +- eRPC: Makefile updated to reflect the correct header names, GitHub PR #295. +- eRPC: Compare value length to used length() in reading data from message buffer, GitHub PR #297. +- eRPC: Replace EXPECT_TRUE with EXPECT_EQ in unit tests, GitHub PR #318. +- eRPC: Adapt rpmsg_lite based transports to changed rpmsg_lite_wait_for_link_up() API parameters. +- eRPC, erpcgen: Better distuingish which file can and cannot by linked by C linker, GitHub PR #266. +- eRPC, erpcgen: Stop checking if pointer is NULL before sending it to the erpc_free function, GitHub PR #275. +- eRPC, erpcgen: Changed api to count with more interfaces, GitHub PR #304. +- erpcgen: Check before reading from heap the buffer boundaries, GitHub PR #287. +- erpcgen: Several fixes for tests and CI, GitHub PR #289. +- erpcgen: Refactoring erpcgen code, GitHub PR #302. +- erpcgen: Fixed assigning const value to enum, GitHub PR #309. +- erpcgen: Enable runTesttest_enumErrorCode_allDirection, serialize enums as int32 instead of uint32. + +## [1.9.1] + +### Fixed + +- eRPC: Construct the USB CDC transport, rather than a client, GitHub PR #220. +- eRPC: Fix premature import of package, causing failure when attempting installation of Python library in a clean environment, GitHub PR #38, #226. +- eRPC: Improve python detection in make, GitHub PR #225. +- eRPC: Fix several warnings with deprecated call in pytest, GitHub PR #227. +- eRPC: Fix freeing union members when only default need be freed, GitHub PR #228. +- eRPC: Fix making test under Linux, GitHub PR #229. +- eRPC: Assert costumizing, GitHub PR #148. +- eRPC: Fix corrupt clientList bug in TransportArbitrator, GitHub PR #199. +- eRPC: Fix build issue when invoking g++ with -Wno-error=free-nonheap-object, GitHub PR #233. +- eRPC: Fix inout cases, GitHub PR #237. +- eRPC: Remove ERPC_PRE_POST_ACTION dependency on return type, GitHub PR #238. +- eRPC: Adding NULL to ptr when codec function failed, fixing memcpy when fail is present during deserialization, GitHub PR #253. +- eRPC: MessageBuffer usage improvement, GitHub PR #258. +- eRPC: Get rid for serial and enum34 dependency (enum34 is in python3 since 3.4 (from 2014)), GitHub PR #247. +- eRPC: Several MISRA violations addressed. +- eRPC: Fix timeout for Freertos semaphore, GitHub PR #251. +- eRPC: Use of rpmsg_lite_wait_for_link_up() in rpmsg_lite based transports, GitHub PR #223. +- eRPC: Fix codec nullptr dereferencing, GitHub PR #264. +- erpcgen: Fix two syntax errors in erpcgen Python output related to non-encapsulated unions, improved test for union, GitHub PR #206, #224. +- erpcgen: Fix serialization of list/binary types, GitHub PR #240. +- erpcgen: Fix empty list parsing, GitHub PR #72. +- erpcgen: Fix templates for malloc errors, GitHub PR #110. +- erpcgen: Get rid of encapsulated union declarations in global scale, improve enum usage in unions, GitHub PR #249, #250. +- erpcgen: Fix compile error:UniqueIdChecker.cpp:156:104:'sort' was not declared, GitHub PR #265. + +## [1.9.0] + +### Added + +- eRPC: Allow used LIBUSBSIO device index being specified from the Python command line argument. + +### Fixed + +- eRPC: Improving template usage, GitHub PR #153. +- eRPC: run_clang_format.py cleanup, GitHub PR #177. +- eRPC: Build TCP transport setup code into liberpc, GitHub PR #179. +- eRPC: Fix multiple definitions of g_client error, GitHub PR #180. +- eRPC: Fix memset past end of buffer in erpc_setup_mbf_static.cpp, GitHub PR #184. +- eRPC: Fix deprecated error with newer pytest version, GitHub PR #203. +- eRPC, erpcgen: Static allocation support and usage of rpmsg static FreeRTOSs related APi, GitHub PR #168, #169. +- erpcgen: Remove redundant module imports in erpcgen, GitHub PR #196. + +## [1.8.1] + +### Added + +- eRPC: New i2c_slave_transport trasnport introduced. + +### Fixed + +- eRPC: Fix misra erpc c, GitHub PR #158. +- eRPC: Allow conditional compilation of message_loggers and pre_post_action. +- eRPC: (D)SPI slave transports updated to avoid busy loops in rtos environments. +- erpcgen: Re-implement EnumMember::hasValue(), GitHub PR #159. +- erpcgen: Fixing several misra issues in shim code, erpcgen and unit tests updated, GitHub PR #156. +- erpcgen: Fix bison file, GitHub PR #156. + +## [1.8.0] + +### Added + +- eRPC: Support win32 thread, GitHub PR #108. +- eRPC: Add mbed support for malloc() and free(), GitHub PR #92. +- eRPC: Introduced pre and post callbacks for eRPC call, GitHub PR #131. +- eRPC: Introduced new USB CDC transport. +- eRPC: Introduced new Linux spidev-based transport. +- eRPC: Added formatting extension for VSC, GitHub PR #134. +- erpcgen: Introduce ustring type for unsigned char and force cast to char*, GitHub PR #125. + +### Fixed + +- eRPC: Update makefile. +- eRPC: Fixed warnings and error with using MessageLoggers, GitHub PR #127. +- eRPC: Extend error msg for python server service handle function, GitHub PR #132. +- eRPC: Update CMSIS UART transport layer to avoid busy loops in rtos environments, introduce semaphores. +- eRPC: SPI transport update to allow usage without handshaking GPIO. +- eRPC: Native _WIN32 erpc serial transport and threading. +- eRPC: Arbitrator deadlock fix, TCP transport updated, TCP setup functions introduced, GitHub PR #121. +- eRPC: Update of matrix_multiply.py example: Add --serial and --baud argument, GitHub PR #137. +- eRPC: Update of .clang-format, GitHub PR #140. +- eRPC: Update of erpc_framed_transport.cpp: return error if received message has zero length, GitHub PR #141. +- eRPC, erpcgen: Fixed error messages produced by -Wall -Wextra -Wshadow -pedantic-errors compiler flags, GitHub PR #136, #139. +- eRPC, erpcgen: Core re-formatted using Clang version 10. +- erpcgen: Enable deallocation in server shim code when callback/function pointer used as out parameter in IDL. +- erpcgen: Removed '$' character from generated symbol name in '_$union' suffix, GitHub PR #103. +- erpcgen: Resolved mismatch between C++ and Python for callback index type, GitHub PR #111. +- erpcgen: Python generator improvements, GitHub PR #100, #118. +- erpcgen: Fixed error messages produced by -Wall -Wextra -Wshadow -pedantic-errors compiler flags, GitHub PR #136. + + +## [1.7.4] + +### Added + +- eRPC: Support MU transport unit testing. +- eRPC: Adding mbed os support. + +### Fixed + +- eRPC: Unit test code updated to handle service add and remove operations. +- eRPC: Several MISRA issues in rpmsg-based transports addressed. +- eRPC: Fixed Linux/TCP acceptance tests in release target. +- eRPC: Minor documentation updates, code formatting. +- erpcgen: Whitespace removed from C common header template. + +## [1.7.3] + +### Fixed + +- eRPC: Improved the test_callbacks logic to be more understandable and to allow requested callback execution on the server side. +- eRPC: TransportArbitrator::prepareClientReceive modified to avoid incorrect return value type. +- eRPC: The ClientManager and the ArbitratedClientManager updated to avoid performing client requests when the previous serialization phase fails. +- erpcgen: Generate the shim code for destroy of statically allocated services. + +## [1.7.2] + +### Added + +- eRPC: Add missing doxygen comments for transports. + +### Fixed + +- eRPC: Improved support of const types. +- eRPC: Fixed Mac build. +- eRPC: Fixed serializing python list. +- eRPC: Documentation update. + +## [1.7.1] + +### Fixed + +- eRPC: Fixed semaphore in static message buffer factory. +- erpcgen: Fixed MU received error flag. +- erpcgen: Fixed tcp transport. + +## [1.7.0] + +### Added + +- eRPC: List names are based on their types. Names are more deterministic. +- eRPC: Service objects are as a default created as global static objects. +- eRPC: Added missing doxygen comments. +- eRPC: Added support for 64bit numbers. +- eRPC: Added support of program language specific annotations. + +### Fixed + +- eRPC: Improved code size of generated code. +- eRPC: Generating crc value is optional. +- eRPC: Fixed CMSIS Uart driver. Removed dependency on KSDK. +- eRPC: Forbid users use reserved words. +- eRPC: Removed outByref for function parameters. +- eRPC: Optimized code style of callback functions. + +## [1.6.0] + +### Added + +- eRPC: Added \@nullable support for scalar types. + +### Fixed + +- eRPC: Improved code size of generated code. +- eRPC: Improved eRPC nested calls. +- eRPC: Improved eRPC list length variable serialization. + +## [1.5.0] + +### Added + +- eRPC: Added support for unions type non-wrapped by structure. +- eRPC: Added callbacks support. +- eRPC: Added support \@external annotation for functions. +- eRPC: Added support \@name annotation. +- eRPC: Added Messaging Unit transport layer. +- eRPC: Added RPMSG Lite RTOS TTY transport layer. +- eRPC: Added version verification and IDL version verification between eRPC code and eRPC generated shim code. +- eRPC: Added support of shared memory pointer. +- eRPC: Added annotation to forbid generating const keyword for function parameters. +- eRPC: Added python matrix multiply example. +- eRPC: Added nested call support. +- eRPC: Added struct member "byref" option support. +- eRPC: Added support of forward declarations of structures +- eRPC: Added Python RPMsg Multiendpoint kernel module support +- eRPC: Added eRPC sniffer tool + +## [1.4.0] + +### Added + +- eRPC: New RPMsg-Lite Zero Copy (RPMsgZC) transport layer. + +### Fixed + +- eRPC: win_flex_bison.zip for windows updated. +- eRPC: Use one codec (instead of inCodec outCodec). + +## [1.3.0] + +### Added + +- eRPC: New annotation types introduced (\@length, \@max_length, ...). +- eRPC: Support for running both erpc client and erpc server on one side. +- eRPC: New transport layers for (LP)UART, (D)SPI. +- eRPC: Error handling support. + +## [1.2.0] + +### Added + +- eRPC source directory organization changed. +- Many eRPC improvements. + +## [1.1.0] + +### Added + +- Multicore SDK 1.1.0 ported to KSDK 2.0.0. + +## [1.0.0] + +### Added + +- Initial Release + + +[unreleased]: https://github.com/EmbeddedRPC/erpc/compare/1.14.0...HEAD +[1.14.0]: https://github.com/EmbeddedRPC/erpc/compare/1.13.0...1.14.0 +[1.13.0]: https://github.com/EmbeddedRPC/erpc/compare/1.12.0...1.13.0 +[1.12.0]: https://github.com/EmbeddedRPC/erpc/compare/1.11.0...1.12.0 +[1.11.0]: https://github.com/EmbeddedRPC/erpc/compare/1.10.0...1.11.0 +[1.10.0]: https://github.com/EmbeddedRPC/erpc/compare/1.9.1...1.10.0 +[1.9.1]: https://github.com/EmbeddedRPC/erpc/compare/1.9.0...1.9.1 +[1.9.0]: https://github.com/EmbeddedRPC/erpc/compare/1.8.1...1.9.0 +[1.8.1]: https://github.com/EmbeddedRPC/erpc/compare/1.8.0...1.8.1 +[1.8.0]: https://github.com/EmbeddedRPC/erpc/compare/1.7.4...1.8.0 +[1.7.4]: https://github.com/EmbeddedRPC/erpc/compare/1.7.3...1.7.4 +[1.7.3]: https://github.com/EmbeddedRPC/erpc/compare/1.7.2...1.7.3 +[1.7.2]: https://github.com/EmbeddedRPC/erpc/compare/1.7.1...1.7.2 +[1.7.1]: https://github.com/EmbeddedRPC/erpc/compare/1.7.0...1.7.1 +[1.7.0]: https://github.com/EmbeddedRPC/erpc/compare/1.6.0...1.7.0 +[1.6.0]: https://github.com/EmbeddedRPC/erpc/compare/1.5.0...1.6.0 +[1.5.0]: https://github.com/EmbeddedRPC/erpc/compare/1.4.0...1.5.0 +[1.4.0]: https://github.com/EmbeddedRPC/erpc/releases/tag/1.4.0 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..f902777f3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,58 @@ +# +# Copyright 2024 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +cmake_minimum_required(VERSION 3.20) +project(eRPC) + +############################################################################### +# Define basic variables +############################################################################### +set(ERPC_BASE ${CMAKE_CURRENT_LIST_DIR}) + +set(ERPC_ERPCGEN ${ERPC_BASE}/erpcgen) +set(ERPC_C ${ERPC_BASE}/erpc_c) +set(ERPC_TEST ${ERPC_BASE}/test) +set(ERPC_EXAMPLES ${ERPC_BASE}/examples) + +############################################################################### +# Includes required modules +############################################################################### +include(${ERPC_BASE}/cmake/extensions.cmake) +include(${ERPC_BASE}/cmake/python.cmake) +include(${ERPC_BASE}/cmake/kconfig.cmake) +include(${ERPC_BASE}/cmake/erpc_utils.cmake) + +############################################################################### +# Add components base on Kconfig +############################################################################### + +if(CONFIG_REQUIRE_ERPCGEN) + if(CONFIG_ERPC_GENERATOR) + set(ERPCGEN_EXECUTABLE $) + else() + find_program(ERPCGEN_EXECUTABLE NAMES erpcgen) + + if(NOT ERPCGEN_EXECUTABLE) + message(FATAL_ERROR "It was not possible to find the erpcgen executable. Enable ERPC_GENERATOR in Kconfig to build erpcgen from source or add 'erpcgen' to PATH.") + endif() + endif() +endif() + +if(CONFIG_ERPC_GENERATOR) + add_subdirectory(${ERPC_ERPCGEN}) +endif() + +if(CONFIG_ERPC_LIB) + add_subdirectory(${ERPC_C}) +endif() + +if(CONFIG_ERPC_TESTS) + add_subdirectory(${ERPC_TEST}) +endif() + +if(CONFIG_ERPC_EXAMPLES) + add_subdirectory(${ERPC_EXAMPLES}) +endif() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..1933242e6 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,24 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "mingw64", + "displayName": "Mingw64 eRPC config", + "description": "Default Mingw64 preset for eRPC", + "generator": "MinGW Makefiles", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" + }, + "environment": { + "PATH": "${sourceDir}/mingw64/bin;$penv{PATH}" + } + } + ] +} \ No newline at end of file diff --git a/Kconfig b/Kconfig new file mode 100644 index 000000000..adc03ba1f --- /dev/null +++ b/Kconfig @@ -0,0 +1,177 @@ +# +# Copyright 2024 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +config ERPC_GENERATOR + bool "Build eRPC Generator" + default y + +config REQUIRE_ERPCGEN + bool + default n + +config ERPC_LIB + bool "Build eRPC C Library" + default y + +menuconfig ERPC_TESTS + bool "Build eRPC Tests" + default n + select REQUIRE_ERPCGEN + +if ERPC_TESTS + menu "Test transport" + menuconfig ERPC_TESTS.transport.tcp + bool "Test transport TCP" + default y + + if ERPC_TESTS.transport.tcp + config ERPC_TESTS.transport.tcp.host + string "TCP Host" + default "localhost" + + config ERPC_TESTS.transport.tcp.port + int "TCP Port" + default 12345 + endif + + menuconfig ERPC_TESTS.transport.serial + bool "Test transport Serial" + default n + + if ERPC_TESTS.transport.serial + config ERPC_TESTS.transport.serial.port + string "Serial port" + default "localhost" + + config ERPC_TESTS.transport.serial.baud + int "Baud" + default 115200 + endif + endmenu + + config ERPC_TESTS.client + bool "Build test's client side" + default y + + config ERPC_TESTS.server + bool "Build test's server side" + default y + + menu "Test cases" + config ERPC_TESTS.testcase.test_annotations + bool "Build test_annotations" + default y + config ERPC_TESTS.testcase.test_arbitrator + bool "Build test_arbitrator" + default y + config ERPC_TESTS.testcase.test_arrays + bool "Build test_arrays" + default y + config ERPC_TESTS.testcase.test_binary + bool "Build test_binary" + default y + config ERPC_TESTS.testcase.test_builtin + bool "Build test_builtin" + default y + config ERPC_TESTS.testcase.test_callbacks + bool "Build test_callbacks" + default y + config ERPC_TESTS.testcase.test_const + bool "Build test_const" + default y + config ERPC_TESTS.testcase.test_enums + bool "Build test_enums" + default y + config ERPC_TESTS.testcase.test_lists + bool "Build test_lists" + default y + config ERPC_TESTS.testcase.test_shared + bool "Build test_shared" + default y + config ERPC_TESTS.testcase.test_struct + bool "Build test_struct" + default y + config ERPC_TESTS.testcase.test_typedef + bool "Build test_typedef" + default y + config ERPC_TESTS.testcase.test_unions + bool "Build test_unions" + default y + endmenu + +endif + +menuconfig ERPC_EXAMPLES + bool "Build eRPC Examples" + default n + select REQUIRE_ERPCGEN + +if ERPC_EXAMPLES + menuconfig ERPC_MATRIX_MULTIPLY_TCP + bool "Matrix Multiply TCP example" + default n + select REQUIRE_ERPCGEN + + if ERPC_MATRIX_MULTIPLY_TCP + config ERPC_MATRIX_MULTIPLY_TCP.host + string "TCP Host" + default "localhost" + + config ERPC_MATRIX_MULTIPLY_TCP.port + int "TCP Port" + default 8811 + + config ERPC_MATRIX_MULTIPLY_TCP.c + bool "Build C version of the demo" + default y + config ERPC_MATRIX_MULTIPLY_TCP.cpp + bool "Build C++ version of the demo" + default y + endif + + menuconfig ERPC_MATRIX_MULTIPLY_UART + bool "Matrix Multiply UART example" + default n + select REQUIRE_ERPCGEN + + if ERPC_MATRIX_MULTIPLY_UART + config ERPC_MATRIX_MULTIPLY_UART.serial + string "Serial port" + default "localhost" + + config ERPC_MATRIX_MULTIPLY_UART.baud + int "Baud" + default 115200 + + config ERPC_MATRIX_MULTIPLY_UART.c + bool "Build C version of the demo" + default y + config ERPC_MATRIX_MULTIPLY_UART.cpp + bool "Build C++ version of the demo" + default y + endif + + menuconfig ERPC_HELLO_WORLD + bool "Hello World example" + default n + select REQUIRE_ERPCGEN + + if ERPC_HELLO_WORLD + config ERPC_HELLO_WORLD.c + bool "Build C version of the demo" + default y + config ERPC_HELLO_WORLD.cpp + bool "Build C++ version of the demo" + default y + config ERPC_HELLO_WORLD.python + bool "Generate shim code for Python" + default y + config ERPC_HELLO_WORLD.java + bool "Generate shim code for Java" + default y + endif +endif + diff --git a/README.md b/README.md index ad8aabce9..ac91af0a1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,21 @@ +# MCUXpresso SDK : mcuxsdk-middleware-erpc + +## Overview +This repository is for MCUXpresso SDK eRPC middleware delivery and it contains eRPC component officially provided in NXP MCUXpresso SDK. This repository is part of the MCUXpresso SDK overall delivery which is composed of several sub-repositories/projects. Navigate to the top/parent repository [mcuxsdk](https://github.com/nxp-mcuxpresso/mcuxsdk-manifests/) for the complete delivery of MCUXpresso SDK to be able to build and run eRPC examples that are based on mcux-sdk-middleware-erpc component. + +## Documentation +Overall details can be reviewed here: [MCUXpresso SDK Online Documentation](https://mcuxpresso.nxp.com/mcuxsdk/latest/html/introduction/README.html) + +Visit [eRPC - Documentation](https://mcuxpresso.nxp.com/mcuxsdk/latest/html/middleware/multicore/erpc/README.html) to review details on the contents in this sub-repo. + +## Setup +Instructions on how to install the MCUXpresso SDK provided from GitHub via west manifest [Getting Started with SDK - Detailed Installation Instructions](https://mcuxpresso.nxp.com/mcuxsdk/latest/html/gsd/installation.html#installation) + +## Contribution +We welcome and encourage the community to submit patches directly to the eRPC project placed on github. Contributing can be managed via pull-requests. Before a pull-request is created the code should be tested and properly formatted. + +--------------------------------- + # eRPC ![GitHub all releases](https://img.shields.io/github/downloads/EmbeddedRPC/erpc/total) @@ -16,23 +34,29 @@ [![Issues](https://img.shields.io/github/issues/EmbeddedRPC/erpc)](https://github.com/EmbeddedRPC/erpc/issues) [![PRs Welcome](https://img.shields.io/github/issues-pr/EmbeddedRPC/erpc)](https://github.com/EmbeddedRPC/erpc/pulls) -* [eRPC](#erpc) - * [About](#about) - * [Releases](#releases) - * [Edge releases](#edge-releases) - * [Documentation](#documentation) - * [Examples](#examples) - * [References](#references) - * [Directories](#directories) - * [Building and installing](#building-and-installing) - * [Requirements](#requirements) - * [Windows](#windows) - * [Linux](#linux) - * [Mac OS X](#mac-os-x) - * [Building](#building) - * [Installing for Python](#installing-for-python) - * [Known issues and limitations](#known-issues-and-limitations) - * [Code providing](#code-providing) +- [MCUXpresso SDK : mcuxsdk-middleware-erpc](#mcuxpresso-sdk--mcuxsdk-middleware-erpc) + - [Overview](#overview) + - [Documentation](#documentation) + - [Setup](#setup) + - [Contribution](#contribution) +- [eRPC](#erpc) + - [About](#about) + - [Releases](#releases) + - [Edge releases](#edge-releases) + - [Documentation](#documentation-1) + - [Examples](#examples) + - [References](#references) + - [Directories](#directories) + - [Building and installing](#building-and-installing) + - [Requirements](#requirements) + - [Windows](#windows) + - [Mac OS X](#mac-os-x) + - [Building](#building) + - [CMake and KConfig](#cmake-and-kconfig) + - [Make](#make) + - [Installing for Python](#installing-for-python) + - [Known issues and limitations](#known-issues-and-limitations) + - [Code providing](#code-providing) ## About @@ -281,7 +305,15 @@ This section provides links to interesting erpc-based projects, articles, blogs These build instructions apply to host PCs and embedded Linux. For bare metal or RTOS embedded environments, you should copy the [erpc_c](/erpc_c) directory into your application sources. -The primary build system is makefile based. It builds a static library of the eRPC C/C++ infrastructure, the `erpcgen` executable, and optionally the unit tests. +**CMake and KConfig build**: + +It builds a static library of the eRPC C/C++ infrastructure, the `erpcgen` executable, and optionally the unit tests and examples. + +CMake is compatible with gcc and clang. On Windows local MingGW downloaded by [script](./install_dependencies.ps1) can be used. + +**Make build**: + +It builds a static library of the eRPC C/C++ infrastructure, the `erpcgen` executable, and optionally the unit tests. The makefiles are compatible with gcc or clang on Linux, OS X, and Cygwin. A Windows build of erpcgen using Visual Studio is also available in the [erpcgen/VisualStudio_v14](erpcgen/VisualStudio_v14) directory. @@ -289,10 +321,29 @@ There is also an Xcode project file in the [erpcgen](/erpcgen) directory, which ### Requirements +eRPC now support building **erpcgen**, **erpc_lib**, **tests** and **C examples** using CMake. + +Requirements when using CMake: +* **CMake** (minimal version 3.20.0) +* Generator - **Make**, **Ninja**, ... +* **C/C++ compiler** - **GCC**, **CLANG**, ... +* **Binson** - https://www.gnu.org/software/bison/ +* **Flex** - https://github.com/westes/flex/ + +Requirements when using Make: +* **Make** +* **C/C++ compiler** - **GCC**, **CLANG**, ... +* **Binson** - https://www.gnu.org/software/bison/ +* **Flex** - https://github.com/westes/flex/ + + #### Windows +Related steps to build **erpcgen** using **Visual Studio** are described in [`erpcgen/VisualStudio_v14/readme_erpcgen.txt`](erpcgen/VisualStudio_v14/readme_erpcgen.txt). -* Related to Visual Studio: steps are described in [`erpcgen/VisualStudio_v14/readme_erpcgen.txt`](erpcgen/VisualStudio_v14/readme_erpcgen.txt). -* mingw compilation can be used too +To install MinGW, Bison, Flex locally on Windows: +```bash +./install_dependencies.ps1 +* ``` #### Linux @@ -312,6 +363,25 @@ Mandatory for case, when build for different architecture is needed ### Building +#### CMake and KConfig +eRPC use CMake and KConfig to configurate and build eRPC related targets. KConfig can be edited by [prj.conf](./prj.conf) or _menuconfig_ when building. + +Generate project, config and build. In [erpc/](./) execute: +```bash +cmake -B ./build # in erpc/build generate cmake project +cmake --build ./build --target menuconfig # Build menuconfig and configurate erpcgen, erpc_lib, tests and examples +cmake --build ./build # Build all selected target from prj.conf/menuconfig +``` +**CMake will use the system's default compilers and generator + +If you want to use Windows and locally installed MinGW, use [CMake preset](./CMakePresets.json) : +```bash +cmake --preset mingw64 # Generate project in ./build using mingw64's make and compilers +cmake --build ./build --target menuconfig # Build menuconfig and configurate erpcgen, erpc_lib, tests and examples +cmake --build ./build # Build all selected target from prj.conf/menuconfig +``` + +#### Make To build the library and erpcgen, run from the repo root directory: ```sh @@ -358,4 +428,4 @@ Repository on Github contains two main branches: __main__ and __develop__. Code --- Copyright 2014-2016 Freescale Semiconductor, Inc. -Copyright 2016-2024 NXP +Copyright 2016-2025 NXP diff --git a/SBOM.spdx.json b/SBOM.spdx.json new file mode 100644 index 000000000..b5003ae37 --- /dev/null +++ b/SBOM.spdx.json @@ -0,0 +1,114 @@ +{ + "SPDXID": "SPDXRef-DOCUMENT", + "spdxVersion": "SPDX-2.3", + "creationInfo": { + "created": "2025-05-30T06:44:48Z", + "creators": [ + "Organization: NXP" + ], + "licenseListVersion": "3.20" + }, + "name": "erpc-1.14.0", + "dataLicense": "CC0-1.0", + "documentNamespace": "https://nxp.com/spdx/1dfe3ec4-b29f-4e7b-afd3-36278a134a93", + "packages": [ + { + "SPDXID": "SPDXRef-package-1dfe3ec4-b29f-4e7b-afd3-36278a134a93", + "name": "erpc", + "versionInfo": "1.14.0", + "licenseConcluded": "(BSD-3-Clause)", + "licenseDeclared": "(BSD-3-Clause)", + "downloadLocation": "https://github.com/EmbeddedRPC/erpc", + "originator": "Organization: NXP", + "supplier": "NOASSERTION", + "externalRefs": [], + "filesAnalyzed": false + }, + { + "SPDXID": "SPDXRef-package-f919933c-33b3-4b0a-950f-83c11a3d4b39", + "name": "BusPirate/Bus_Pirate", + "versionInfo": "v6.1", + "licenseConcluded": "CC0-1.0", + "licenseDeclared": "CC0-1.0", + "downloadLocation": "NOASSERTION", + "originator": "Organization: NXP", + "supplier": "NOASSERTION", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceLocator": "pkg:github/BusPirate/Bus_Pirate@v6.1", + "referenceType": "purl" + }, + { + "referenceCategory": "OTHER", + "referenceLocator": "3a28d3d0-0713-4113-92c5-9a8b53e12e05", + "referenceType": "BlackDuck-ComponentOrigin" + } + ], + "filesAnalyzed": false + }, + { + "SPDXID": "SPDXRef-package-d83f2de3-93ed-46e7-9472-401fe64df55d", + "name": "Kconfiglib", + "versionInfo": "14.1.0", + "licenseConcluded": "ISC", + "licenseDeclared": "ISC", + "homepage": "https://github.com/ulfalizer/Kconfiglib", + "downloadLocation": "NOASSERTION", + "externalRefs": [], + "filesAnalyzed": false, + "supplier": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-package-9069e7c0-e11a-4d17-aa04-153e5fd93c3c", + "name": "zephyr", + "versionInfo": "v3.6.0", + "licenseConcluded": "Apache-2.0", + "licenseDeclared": "Apache-2.0", + "homepage": "https://zephyrproject.org/doc", + "downloadLocation": "https://github.com/zephyrproject-rtos/zephyr", + "originator": "Organization: NXP", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:zephyrproject:zephyr:3.6.0:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + }, + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:o:zephyrproject:zephyr:3.6.0:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + }, + { + "referenceCategory": "SECURITY", + "referenceLocator": "cpe:2.3:a:zephyrproject_rtos:zephyr:3.6.0:*:*:*:*:*:*:*", + "referenceType": "cpe23Type" + } + ], + "filesAnalyzed": false, + "supplier": "NOASSERTION" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES", + "relatedSpdxElement": "SPDXRef-package-1dfe3ec4-b29f-4e7b-afd3-36278a134a93" + }, + { + "spdxElementId": "SPDXRef-package-1dfe3ec4-b29f-4e7b-afd3-36278a134a93", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-package-d83f2de3-93ed-46e7-9472-401fe64df55d" + }, + { + "spdxElementId": "SPDXRef-package-1dfe3ec4-b29f-4e7b-afd3-36278a134a93", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-package-9069e7c0-e11a-4d17-aa04-153e5fd93c3c" + }, + { + "spdxElementId": "SPDXRef-package-1dfe3ec4-b29f-4e7b-afd3-36278a134a93", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-package-f919933c-33b3-4b0a-950f-83c11a3d4b39" + } + ] +} \ No newline at end of file diff --git a/SCR.txt b/SCR.txt new file mode 100644 index 000000000..fe526f3de --- /dev/null +++ b/SCR.txt @@ -0,0 +1,78 @@ +Release Name: eRPC +Release Version: 1.14.0 +Package License: BSD-3-Clause + +the_bus_pirate Name: The Bus Pirate + Version: NA + Outgoing License: Open Source - CC0 (Public Domain + Dedication License) + License File: http://code.google.com/p/the-bus-pirate/ + Format: source code + Description: OS independent serial interface + Location: + erpc_c/port/erpc_serial.h, + erpc_c/port/erpc_serial.cpp + Origin: Community + Url: http://code.google.com/p/the-bus-pirate/ + +cpp_template Name: CPP Template + Version: NA + Outgoing License: Open Source - MIT + License File: + erpcgen/src/cpptemplate/LICENSE.txt + Format: source code + Description: CPP Template + Location: erpcgen/src/cpptemplate + Origin: Ryan Ginstrom & Martinho Fernandes + +cpp_option_parser Name: C++ option-parser + Version: NA + Outgoing License: Brad Appleton's license + License File: http://www.bradapp.com/ftp/src/libs/C++/Options.tar.gz + , see README file + Format: Plain Text + Description: C++ option-parser + Location: erpcgen/src/options.cpp + Origin: Brad Appleton bradapp@enteract.com + Url: http://www.bradapp.com/ftp/src/libs/C++/Options.html + +kconfig_scripts Name: A flexible Python 2/3 Kconfig implementation and library + Version: NA + Outgoing License: ISC + License File: https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt + Format: source code + Description: A flexible Python 2/3 Kconfig implementation and library + Location: scripts/kconfig/kconfiglib.py, + scripts/kconfig/guiconfig.py, scripts/kconfig/menuconfig.py + Origin: Ulf Magnusson + Url: https://github.com/ulfalizer/Kconfiglib + +hardenconfig_script Name: Zephyr RTOS scripts + Version: 3.6 + Outgoing License: Apache 2.0 + License File: https://github.com/zephyrproject-rtos/zephyr/blob/main/LICENSE + Format: source code + Description: Zephyr RTOS scripts + Location: scripts/kconfig/hardenconfig.py + Origin: Intel Corporation + Url: https://github.com/zephyrproject-rtos/zephyr + +kconfigfunctions_script Name: Zephyr RTOS scripts + Version: 3.6 + Outgoing License: Apache 2.0 + License File: https://github.com/zephyrproject-rtos/zephyr/blob/main/LICENSE + Format: source code + Description: Zephyr RTOS scripts + Location: scripts/kconfig/kconfigfunctions.py + Origin: Nordic Semiconductor ASA, Linaro + Url: https://github.com/zephyrproject-rtos/zephyr + +lint_script Name: Zephyr RTOS scripts + Version: 3.6 + Outgoing License: Apache 2.0 + License File: https://github.com/zephyrproject-rtos/zephyr/blob/main/LICENSE + Format: source code + Description: Zephyr RTOS scripts + Location: scripts/kconfig/lint.py + Origin: Nordic Semiconductor ASA + Url: https://github.com/zephyrproject-rtos/zephyr diff --git a/SW-Content-Register.txt b/SW-Content-Register.txt deleted file mode 100644 index aafc9df8c..000000000 --- a/SW-Content-Register.txt +++ /dev/null @@ -1,37 +0,0 @@ -Release Name: eRPC -Release Version: 1.12.0 -Package License: BSD-3-Clause - -the_bus_pirate Name: The Bus Pirate - Version: NA - Outgoing License: Open Source - CC0 (Public Domain - Dedication License) - License File: http://code.google.com/p/the-bus-pirate/ - Format: source code - Description: OS independent serial interface - Location: - erpc_c/port/erpc_serial.h, - erpc_c/port/erpc_serial.cpp - Origin: Community - Url: http://code.google.com/p/the-bus-pirate/ - -cpp_template Name: CPP Template - Version: NA - Outgoing License: Open Source - MIT - License File: - erpcgen/src/cpptemplate/LICENSE.txt - Format: source code - Description: CPP Template - Location: erpcgen/src/cpptemplate - Origin: Ryan Ginstrom & Martinho Fernandes - -cpp_option_parser Name: C++ option-parser - Version: NA - Outgoing License: Brad Appleton's license - License File: http://www.bradapp.com/ftp/src/libs/C++/Options.tar.gz - , see README file - Format: Plain Text - Description: C++ option-parser - Location: erpcgen/src/options.cpp - Origin: Brad Appleton bradapp@enteract.com - Url: http://www.bradapp.com/ftp/src/libs/C++/Options.html \ No newline at end of file diff --git a/cmake/erpc_utils.cmake b/cmake/erpc_utils.cmake new file mode 100644 index 000000000..d07185a0b --- /dev/null +++ b/cmake/erpc_utils.cmake @@ -0,0 +1,57 @@ +# +# Copyright 2024 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# + +# To optimize build process, for each erpc_config.h file used, generate only one library. +# This function compute MD5 of given config file and base on this hash check if target library exist, +# otherwise create new library with hash postfix +function(erpc_link_c_library) + cmake_parse_arguments(_ "" "TARGET;CONFIG_DIR;TRANSPORT" "" ${ARGN}) + + + include(${ERPC_C}/erpc_c_sources.cmake) + + if(NOT __TARGET) + message(FATAL_ERROR "Not target specified.") + endif() + + + if(NOT __CONFIG_DIR) + set(__CONFIG_DIR ${ERPC_C}/config) + endif() + + file(MD5 ${__CONFIG_DIR}/erpc_config.h CONFIG_HASH) + + if(NOT TARGET erpc_c_lib_${CONFIG_HASH}) + add_library(erpc_c_lib_${CONFIG_HASH} ${ERPC_C_SOURCES}) + target_include_directories(erpc_c_lib_${CONFIG_HASH} PUBLIC ${ERPC_C_INCLUDES} ${__CONFIG_DIR}) + + # Required for TCP transport + if(WIN32) + target_link_libraries(erpc_c_lib_${CONFIG_HASH} PUBLIC wsock32 ws2_32) + endif() + + target_link_libraries(erpc_c_lib_${CONFIG_HASH} PUBLIC Threads::Threads) + endif() + + find_package(Threads REQUIRED) + target_link_libraries(${__TARGET} PRIVATE erpc_c_lib_${CONFIG_HASH}) +endfunction() + +function(erpc_generate_shim_code) + cmake_parse_arguments(_ "" "IDL_FILE;OUTPUT_DIR;LANGUAGE;WORKING_DIRECTORY" "OUTPUT_FILES" ${ARGN}) + + if(NOT ERPCGEN_EXECUTABLE) + message(FATAL_ERROR "erpcgen executable not found. Enable CONFIG_ERPC_GENERATOR in Kconfig or provide erpcgen in PATH.") + endif() + + add_custom_command( + OUTPUT ${__OUTPUT_FILES} + COMMAND ${ERPCGEN_EXECUTABLE} -g c -o ${__OUTPUT_DIR} ${__IDL_FILE} + WORKING_DIRECTORY ${__WORKING_DIRECTORY} + DEPENDS ${__IDL_FILE} + COMMENT "erpcgen: ${ERPCGEN_EXECUTABLE} -g c -o ${__OUTPUT_DIR} ${__IDL_FILE}" + ) +endfunction() \ No newline at end of file diff --git a/cmake/extensions.cmake b/cmake/extensions.cmake new file mode 100644 index 000000000..8778cafa5 --- /dev/null +++ b/cmake/extensions.cmake @@ -0,0 +1,5531 @@ +# Copyright 2024 NXP +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +include_guard(GLOBAL) + +######################################################## +# Table of contents +######################################################## +# 1. Zephyr-aware extensions +# 1.1. zephyr_* +# 1.2. zephyr_library_* +# 1.2.1 zephyr_interface_library_* +# 1.3. generate_inc_* +# 1.4. board_* +# 1.5. Misc. +# 2. Kconfig-aware extensions +# 2.1 Misc +# 3. CMake-generic extensions +# 3.1. *_ifdef +# 3.2. *_ifndef +# 3.3. *_option compiler compatibility checks +# 3.3.1 Toolchain integration +# 3.4. Debugging CMake +# 3.5. File system management +# 4. Devicetree extensions +# 4.1 dt_* +# 4.2. *_if_dt_node +# 4.3 zephyr_dt_* +# 5. Zephyr linker functions +# 5.1. zephyr_linker* +# 6 Function helper macros +# 7 Linkable loadable extensions (llext) +# 7.1 llext_* configuration functions +# 7.2 add_llext_* build control functions + +######################################################## +# 1. Zephyr-aware extensions +######################################################## +# 1.1. zephyr_* +# +# The following methods are for modifying the CMake library[0] called +# "zephyr". zephyr is a catch-all CMake library for source files that +# can be built purely with the include paths, defines, and other +# compiler flags that all zephyr source files use. +# [0] https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html +# +# Example usage: +# zephyr_sources( +# random_esp32.c +# utils.c +# ) +# +# Is short for: +# target_sources(zephyr PRIVATE +# ${CMAKE_CURRENT_SOURCE_DIR}/random_esp32.c +# ${CMAKE_CURRENT_SOURCE_DIR}/utils.c +# ) +# +# As a very high-level introduction here are two call graphs that are +# purposely minimalistic and incomplete. +# +# zephyr_library_cc_option() +# | +# v +# zephyr_library_compile_options() --> target_compile_options() +# +# +# zephyr_cc_option() ---> target_cc_option() +# | +# v +# zephyr_cc_option_fallback() ---> target_cc_option_fallback() +# | +# v +# zephyr_compile_options() ---> target_compile_options() +# + + +# https://cmake.org/cmake/help/latest/command/target_sources.html +function(zephyr_sources) + foreach(arg ${ARGV}) + if(IS_DIRECTORY ${arg}) + message(FATAL_ERROR "zephyr_sources() was called on a directory") + endif() + target_sources(zephyr PRIVATE ${arg}) + endforeach() +endfunction() + +# https://cmake.org/cmake/help/latest/command/target_include_directories.html +function(zephyr_include_directories) + target_include_directories(zephyr_interface INTERFACE ${ARGV}) +endfunction() + +# https://cmake.org/cmake/help/latest/command/target_include_directories.html +function(zephyr_system_include_directories) + target_include_directories(zephyr_interface SYSTEM INTERFACE ${ARGV}) +endfunction() + +# https://cmake.org/cmake/help/latest/command/target_compile_definitions.html +function(zephyr_compile_definitions) + target_compile_definitions(zephyr_interface INTERFACE ${ARGV}) +endfunction() + +# https://cmake.org/cmake/help/latest/command/target_compile_options.html +function(zephyr_compile_options) + target_compile_options(zephyr_interface INTERFACE ${ARGV}) +endfunction() + +# https://cmake.org/cmake/help/latest/command/target_link_libraries.html +function(zephyr_link_libraries) + target_link_libraries(zephyr_interface INTERFACE ${ARGV}) +endfunction() + +function(zephyr_libc_link_libraries) + set_property(TARGET zephyr_interface APPEND PROPERTY LIBC_LINK_LIBRARIES ${ARGV}) +endfunction() + +# See this file section 3.1. target_cc_option +function(zephyr_cc_option) + foreach(arg ${ARGV}) + target_cc_option(zephyr_interface INTERFACE ${arg}) + endforeach() +endfunction() + +function(zephyr_cc_option_fallback option1 option2) + target_cc_option_fallback(zephyr_interface INTERFACE ${option1} ${option2}) +endfunction() + +function(zephyr_ld_options) + target_ld_options(zephyr_interface INTERFACE ${ARGV}) +endfunction() + +# Getter functions for extracting build information from +# zephyr_interface. Returning lists, and strings is supported, as is +# requesting specific categories of build information (defines, +# includes, options). +# +# The naming convention follows: +# zephyr_get_${build_information}_for_lang${format}(lang x [STRIP_PREFIX]) +# Where +# the argument 'x' is written with the result +# and +# ${build_information} can be one of +# - include_directories # -I directories +# - system_include_directories # -isystem directories +# - compile_definitions # -D'efines +# - compile_options # misc. compiler flags +# and +# ${format} can be +# - the empty string '', signifying that it should be returned as a list +# - _as_string signifying that it should be returned as a string +# and +# ${lang} can be one of +# - C +# - CXX +# - ASM +# +# STRIP_PREFIX +# +# By default the result will be returned ready to be passed directly +# to a compiler, e.g. prefixed with -D, or -I, but it is possible to +# omit this prefix by specifying 'STRIP_PREFIX' . This option has no +# effect for 'compile_options'. +# +# e.g. +# zephyr_get_include_directories_for_lang(ASM x) +# writes "-Isome_dir;-Isome/other/dir" to x + +function(zephyr_get_include_directories_for_lang_as_string lang i) + zephyr_get_include_directories_for_lang(${lang} list_of_flags DELIMITER " " ${ARGN}) + + convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags) + + set(${i} ${str_of_flags} PARENT_SCOPE) +endfunction() + +function(zephyr_get_system_include_directories_for_lang_as_string lang i) + zephyr_get_system_include_directories_for_lang(${lang} list_of_flags DELIMITER " " ${ARGN}) + + convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags) + + set(${i} ${str_of_flags} PARENT_SCOPE) +endfunction() + +function(zephyr_get_compile_definitions_for_lang_as_string lang i) + zephyr_get_compile_definitions_for_lang(${lang} list_of_flags DELIMITER " " ${ARGN}) + + convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags) + + set(${i} ${str_of_flags} PARENT_SCOPE) +endfunction() + +function(zephyr_get_compile_options_for_lang_as_string lang i) + zephyr_get_compile_options_for_lang(${lang} list_of_flags DELIMITER " ") + + convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags) + + set(${i} ${str_of_flags} PARENT_SCOPE) +endfunction() + +function(zephyr_get_include_directories_for_lang lang i) + zephyr_get_parse_args(args ${ARGN}) + get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + + process_flags(${lang} flags output_list) + string(REPLACE ";" "$" genexp_output_list "${output_list}") + + if(NOT ARGN) + set(result_output_list "-I$-I>") + elseif(args_STRIP_PREFIX) + # The list has no prefix, so don't add it. + set(result_output_list ${output_list}) + elseif(args_DELIMITER) + set(result_output_list "-I$") + endif() + set(${i} ${result_output_list} PARENT_SCOPE) +endfunction() + +function(zephyr_get_system_include_directories_for_lang lang i) + zephyr_get_parse_args(args ${ARGN}) + get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) + + process_flags(${lang} flags output_list) + string(REPLACE ";" "$" genexp_output_list "${output_list}") + + set_ifndef(args_DELIMITER "$") + set(result_output_list "$<$:-isystem$>") + + set(${i} ${result_output_list} PARENT_SCOPE) +endfunction() + +function(zephyr_get_compile_definitions_for_lang lang i) + zephyr_get_parse_args(args ${ARGN}) + get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS) + + process_flags(${lang} flags output_list) + string(REPLACE ";" "$" genexp_output_list "${output_list}") + + set_ifndef(args_DELIMITER "$") + set(result_output_list "-D$") + + set(${i} ${result_output_list} PARENT_SCOPE) +endfunction() + +function(zephyr_get_compile_options_for_lang lang i) + zephyr_get_parse_args(args ${ARGN}) + get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_OPTIONS) + + process_flags(${lang} flags output_list) + string(REPLACE ";" "$" genexp_output_list "${output_list}") + + set_ifndef(args_DELIMITER "$") + set(result_output_list "$") + + set(${i} ${result_output_list} PARENT_SCOPE) +endfunction() + +# This function writes a dict to it's output parameter +# 'return_dict'. The dict has information about the parsed arguments, +# +# Usage: +# zephyr_get_parse_args(foo ${ARGN}) +# print(foo_STRIP_PREFIX) # foo_STRIP_PREFIX might be set to 1 +function(zephyr_get_parse_args return_dict) + foreach(x ${ARGN}) + if(DEFINED single_argument) + set(${single_argument} ${x} PARENT_SCOPE) + unset(single_argument) + else() + if(x STREQUAL STRIP_PREFIX) + set(${return_dict}_STRIP_PREFIX 1 PARENT_SCOPE) + elseif(x STREQUAL NO_SPLIT) + set(${return_dict}_NO_SPLIT 1 PARENT_SCOPE) + elseif(x STREQUAL DELIMITER) + set(single_argument ${return_dict}_DELIMITER) + endif() + endif() + endforeach() +endfunction() + +function(process_flags lang input output) + # The flags might contains compile language generator expressions that + # look like this: + # $<$:-fno-exceptions> + # $<$:$> + # + # Flags that don't specify a language like this apply to all + # languages. + # + # See COMPILE_LANGUAGE in + # https://cmake.org/cmake/help/v3.3/manual/cmake-generator-expressions.7.html + # + # To deal with this, we apply a regex to extract the flag and also + # to find out if the language matches. + # + # If this doesn't work out we might need to ban the use of + # COMPILE_LANGUAGE and instead partition C, CXX, and ASM into + # different libraries + set(languages C CXX ASM) + + set(tmp_list "") + + foreach(flag ${${input}}) + set(is_compile_lang_generator_expression 0) + foreach(l ${languages}) + if(flag MATCHES ":([^>]+)>") + set(updated_flag ${CMAKE_MATCH_1}) + set(is_compile_lang_generator_expression 1) + if(${l} STREQUAL ${lang}) + # This test will match in case there are more generator expressions in the flag. + # As example: $<$:$> + # $<$:something>> + string(REGEX MATCH "(\\\$<)[^\\\$]*(\\\$<)[^\\\$]*(\\\$<)" IGNORE_RESULT ${flag}) + if(CMAKE_MATCH_2) + # Nested generator expressions are used, just substitute `$` to `1` + string(REGEX REPLACE "\\\$" "1" updated_flag ${flag}) + endif() + list(APPEND tmp_list ${updated_flag}) + break() + endif() + endif() + endforeach() + + if(NOT is_compile_lang_generator_expression) + # SHELL is used to avoid de-duplication, but when process flags + # then this tag must be removed to return real compile/linker flags. + if(flag MATCHES "SHELL:[ ]*(.*)") + separate_arguments(flag UNIX_COMMAND ${CMAKE_MATCH_1}) + endif() + # Flags may be placed inside generator expression, therefore any flag + # which is not already a generator expression must have commas converted. + if(NOT flag MATCHES "\\\$<.*>") + string(REPLACE "," "$" flag "${flag}") + endif() + list(APPEND tmp_list ${flag}) + endif() + endforeach() + + set(${output} ${tmp_list} PARENT_SCOPE) +endfunction() + +function(convert_list_of_flags_to_string_of_flags ptr_list_of_flags string_of_flags) + # Convert the list to a string so we can do string replace + # operations on it and replace the ";" list separators with a + # whitespace so the flags are spaced out + string(REPLACE ";" " " locally_scoped_string_of_flags "${${ptr_list_of_flags}}") + + # Set the output variable in the parent scope + set(${string_of_flags} ${locally_scoped_string_of_flags} PARENT_SCOPE) +endfunction() + +macro(get_property_and_add_prefix result target property prefix) + zephyr_get_parse_args(args ${ARGN}) + + if(args_STRIP_PREFIX) + set(maybe_prefix "") + else() + set(maybe_prefix ${prefix}) + endif() + + get_property(target_property TARGET ${target} PROPERTY ${property}) + foreach(x ${target_property}) + list(APPEND ${result} ${maybe_prefix}${x}) + endforeach() +endmacro() + +# 1.2 zephyr_library_* +# +# Zephyr libraries use CMake's library concept and a set of +# assumptions about how zephyr code is organized to cut down on +# boilerplate code. +# +# A Zephyr library can be constructed by the function zephyr_library +# or zephyr_library_named. The constructors create a CMake library +# with a name accessible through the variable ZEPHYR_CURRENT_LIBRARY. +# +# The variable ZEPHYR_CURRENT_LIBRARY should seldom be needed since +# the zephyr libraries have methods that modify the libraries. These +# methods have the signature: zephyr_library_ +# +# The methods are wrappers around the CMake target_* functions. See +# https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for +# documentation on the underlying target_* functions. +# +# The methods modify the CMake target_* API to reduce boilerplate; +# PRIVATE is assumed +# The target is assumed to be ZEPHYR_CURRENT_LIBRARY +# +# When a flag that is given through the zephyr_* API conflicts with +# the zephyr_library_* API then precedence will be given to the +# zephyr_library_* API. In other words, local configuration overrides +# global configuration. + +# Constructor with a directory-inferred name +macro(zephyr_library) + zephyr_library_get_current_dir_lib_name(${ZEPHYR_BASE} lib_name) + zephyr_library_named(${lib_name}) +endmacro() + +# Determines what the current directory's lib name would be according to the +# provided base and writes it to the argument "lib_name" +macro(zephyr_library_get_current_dir_lib_name base lib_name) + # Remove the prefix (/home/sebo/zephyr/driver/serial/CMakeLists.txt => driver/serial/CMakeLists.txt) + file(RELATIVE_PATH name ${base} ${CMAKE_CURRENT_LIST_FILE}) + + # Remove the filename (driver/serial/CMakeLists.txt => driver/serial) + get_filename_component(name ${name} DIRECTORY) + + # Replace / with __ (driver/serial => driver__serial) + string(REGEX REPLACE "/" "__" name ${name}) + + # Replace : with __ (C:/zephyrproject => C____zephyrproject) + string(REGEX REPLACE ":" "__" name ${name}) + + set(${lib_name} ${name}) +endmacro() + +# Constructor with an explicitly given name. +macro(zephyr_library_named name) + # This is a macro because we need add_library() to be executed + # within the scope of the caller. + set(ZEPHYR_CURRENT_LIBRARY ${name}) + add_library(${name} STATIC "") + + zephyr_append_cmake_library(${name}) + + target_link_libraries(${name} PUBLIC zephyr_interface) +endmacro() + +# Provides amend functionality to a Zephyr library for out-of-tree usage. +# +# When called from a Zephyr module, the corresponding zephyr library defined +# within Zephyr will be looked up. +# +# Note, in order to ensure correct library when amending, the folder structure in the +# Zephyr module must resemble the structure used in Zephyr, as example: +# +# Example: to amend the zephyr library created in +# ZEPHYR_BASE/drivers/entropy/CMakeLists.txt +# add the following file: +# ZEPHYR_MODULE/drivers/entropy/CMakeLists.txt +# with content: +# zephyr_library_amend() +# zephyr_library_sources(...) +# +# It is also possible to use generator expression when amending to Zephyr +# libraries. +# +# For example, in case it is required to expose the Zephyr library's folder as +# include path then the following is possible: +# zephyr_library_amend() +# zephyr_library_include_directories($) +# +# See the CMake documentation for more target properties or generator +# expressions. +# +macro(zephyr_library_amend) + # This is a macro because we need to ensure the ZEPHYR_CURRENT_LIBRARY and + # following zephyr_library_* calls are executed within the scope of the + # caller. + if(NOT ZEPHYR_CURRENT_MODULE_DIR) + message(FATAL_ERROR "Function only available for Zephyr modules.") + endif() + + zephyr_library_get_current_dir_lib_name(${ZEPHYR_CURRENT_MODULE_DIR} lib_name) + + set(ZEPHYR_CURRENT_LIBRARY ${lib_name}) +endmacro() + +function(zephyr_link_interface interface) + target_link_libraries(${interface} INTERFACE zephyr_interface) +endfunction() + +# +# zephyr_library versions of normal CMake target_ functions +# Note, paths passed to this function must be relative in order +# to support the library relocation feature of zephyr_code_relocate +# +function(zephyr_library_sources source) + target_sources(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${source} ${ARGN}) +endfunction() + +function(zephyr_library_include_directories) + target_include_directories(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${ARGN}) +endfunction() + +function(zephyr_library_link_libraries item) + target_link_libraries(${ZEPHYR_CURRENT_LIBRARY} PUBLIC ${item} ${ARGN}) +endfunction() + +function(zephyr_library_compile_definitions item) + target_compile_definitions(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${item} ${ARGN}) +endfunction() + +function(zephyr_library_compile_options item) + # The compiler is relied upon for sane behaviour when flags are in + # conflict. Compilers generally give precedence to flags given late + # on the command line. So to ensure that zephyr_library_* flags are + # placed late on the command line we create a dummy interface + # library and link with it to obtain the flags. + # + # Linking with a dummy interface library will place flags later on + # the command line than the the flags from zephyr_interface because + # zephyr_interface will be the first interface library that flags + # are taken from. + + string(MD5 uniqueness "${ARGV}") + set(lib_name options_interface_lib_${uniqueness}) + + if (NOT TARGET ${lib_name}) + # Create the unique target only if it doesn't exist. + add_library( ${lib_name} INTERFACE) + target_compile_options(${lib_name} INTERFACE ${item} ${ARGN}) + endif() + + target_link_libraries(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${lib_name}) +endfunction() + +function(zephyr_library_cc_option) + foreach(option ${ARGV}) + string(MAKE_C_IDENTIFIER check${option} check) + zephyr_check_compiler_flag(C ${option} ${check}) + + if(${${check}}) + zephyr_library_compile_options(${option}) + endif() + endforeach() +endfunction() + +function(zephyr_library_add_dependencies) + add_dependencies(${ZEPHYR_CURRENT_LIBRARY} ${ARGN}) +endfunction() + +# Add the existing CMake library 'library' to the global list of +# Zephyr CMake libraries. This is done automatically by the +# constructor but must be called explicitly on CMake libraries that do +# not use a zephyr library constructor. +function(zephyr_append_cmake_library library) + if(TARGET zephyr_prebuilt) + message(WARNING + "zephyr_library() or zephyr_library_named() called in Zephyr CMake " + "application mode. `${library}` will not be treated as a Zephyr library." + "To create a Zephyr library in Zephyr CMake kernel mode consider " + "creating a Zephyr module. See more here: " + "https://docs.zephyrproject.org/latest/guides/modules.html" + ) + endif() + set_property(GLOBAL APPEND PROPERTY ZEPHYR_LIBS ${library}) +endfunction() + +# Add the imported library 'library_name', located at 'library_path' to the +# global list of Zephyr CMake libraries. +function(zephyr_library_import library_name library_path) + add_library(${library_name} STATIC IMPORTED GLOBAL) + set_target_properties(${library_name} + PROPERTIES IMPORTED_LOCATION + ${library_path} + ) + zephyr_append_cmake_library(${library_name}) +endfunction() + +# Place the current zephyr library in the application memory partition. +# +# The partition argument is the name of the partition where the library shall +# be placed. +# +# Note: Ensure the given partition has been defined using +# K_APPMEM_PARTITION_DEFINE in source code. +function(zephyr_library_app_memory partition) + set_property(TARGET zephyr_property_target + APPEND PROPERTY COMPILE_OPTIONS + "-l" $ "${partition}") +endfunction() + +# Configure a Zephyr library specific property. +# +# Usage: +# zephyr_library_property( ) +# +# Current Zephyr library specific properties that are supported: +# ALLOW_EMPTY : Allow a Zephyr library to be empty. +# An empty Zephyr library will generate a CMake +# configure time warning unless `ALLOW_EMPTY` is TRUE. +function(zephyr_library_property) + set(single_args "ALLOW_EMPTY") + cmake_parse_arguments(LIB_PROP "" "${single_args}" "" ${ARGN}) + + if(LIB_PROP_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "zephyr_library_property(${ARGV0} ...) given unknown arguments: ${FILE_UNPARSED_ARGUMENTS}") + endif() + + foreach(arg ${single_args}) + if(DEFINED LIB_PROP_${arg}) + set_property(TARGET ${ZEPHYR_CURRENT_LIBRARY} PROPERTY ${arg} ${LIB_PROP_${arg}}) + endif() + endforeach() +endfunction() + +# 1.2.1 zephyr_interface_library_* +# +# A Zephyr interface library is a thin wrapper over a CMake INTERFACE +# library. The most important responsibility of this abstraction is to +# ensure that when a user KConfig-enables a library then the header +# files of this library will be accessible to the 'app' library. +# +# This is done because when a user uses Kconfig to enable a library he +# expects to be able to include its header files and call its +# functions out-of-the box. +# +# A Zephyr interface library should be used when there exists some +# build information (include directories, defines, compiler flags, +# etc.) that should be applied to a set of Zephyr libraries and 'app' +# might be one of these libraries. +# +# Zephyr libraries must explicitly call +# zephyr_library_link_libraries() to use this build +# information. 'app' is treated as a special case for usability +# reasons; a Kconfig option (CONFIG_APP_LINK_WITH_) +# should exist for each interface_library and will determine if 'app' +# links with the interface_library. +# +# This API has a constructor like the zephyr_library API has, but it +# does not have wrappers over the other cmake target functions. +macro(zephyr_interface_library_named name) + add_library(${name} INTERFACE) + set_property(GLOBAL APPEND PROPERTY ZEPHYR_INTERFACE_LIBS ${name}) +endmacro() + +# 1.3 generate_inc_* + +# These functions are useful if there is a need to generate a file +# that can be included into the application at build time. The file +# can also be compressed automatically when embedding it. +# +# See tests/application_development/gen_inc_file for an example of +# usage. +function(generate_inc_file + source_file # The source file to be converted to hex + generated_file # The generated file + ) + add_custom_command( + OUTPUT ${generated_file} + COMMAND + ${PYTHON_EXECUTABLE} + ${ZEPHYR_BASE}/scripts/build/file2hex.py + ${ARGN} # Extra arguments are passed to file2hex.py + --file ${source_file} + > ${generated_file} # Does pipe redirection work on Windows? + DEPENDS ${source_file} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endfunction() + +function(generate_inc_file_for_gen_target + target # The cmake target that depends on the generated file + source_file # The source file to be converted to hex + generated_file # The generated file + gen_target # The generated file target we depend on + # Any additional arguments are passed on to file2hex.py + ) + generate_inc_file(${source_file} ${generated_file} ${ARGN}) + + # Ensure 'generated_file' is generated before 'target' by creating a + # dependency between the two targets + + add_dependencies(${target} ${gen_target}) +endfunction() + +function(generate_inc_file_for_target + target # The cmake target that depends on the generated file + source_file # The source file to be converted to hex + generated_file # The generated file + # Any additional arguments are passed on to file2hex.py + ) + # Ensure 'generated_file' is generated before 'target' by creating a + # 'custom_target' for it and setting up a dependency between the two + # targets + + # But first create a unique name for the custom target + generate_unique_target_name_from_filename(${generated_file} generated_target_name) + + add_custom_target(${generated_target_name} DEPENDS ${generated_file}) + generate_inc_file_for_gen_target(${target} ${source_file} ${generated_file} ${generated_target_name} ${ARGN}) +endfunction() + +# 1.4. board_* +# +# This section is for extensions related to Zephyr board handling. +# +# Zephyr board extensions current contains: +# - Board runners +# - Board revision + +# Zephyr board runners: +# Zephyr board runner extension functions control Zephyr's board runners +# from the build system. The Zephyr build system has targets for +# flashing and debugging supported boards. These are wrappers around a +# "runner" Python subpackage that is part of Zephyr's "west" tool. +# +# This section provides glue between CMake and the Python code that +# manages the runners. + +function(_board_check_runner_type type) # private helper + if (NOT (("${type}" STREQUAL "FLASH") OR ("${type}" STREQUAL "DEBUG"))) + message(FATAL_ERROR "invalid type ${type}; should be FLASH or DEBUG") + endif() +endfunction() + +# This function sets the runner for the board unconditionally. It's +# meant to be used from application CMakeLists.txt files. +# +# NOTE: Usually board_set_xxx_ifnset() is best in board.cmake files. +# This lets the user set the runner at cmake time, or in their +# own application's CMakeLists.txt. +# +# Usage: +# board_set_runner(FLASH pyocd) +# +# This would set the board's flash runner to "pyocd". +# +# In general, "type" is FLASH or DEBUG, and "runner" is the name of a +# runner. +function(board_set_runner type runner) + _board_check_runner_type(${type}) + if (DEFINED BOARD_${type}_RUNNER) + message(STATUS "overriding ${type} runner ${BOARD_${type}_RUNNER}; it's now ${runner}") + endif() + set(BOARD_${type}_RUNNER ${runner} PARENT_SCOPE) +endfunction() + +# This macro is like board_set_runner(), but will only make a change +# if that runner is currently not set. +# +# See also board_set_flasher_ifnset() and board_set_debugger_ifnset(). +macro(board_set_runner_ifnset type runner) + _board_check_runner_type(${type}) + # This is a macro because set_ifndef() works at parent scope. + # If this were a function, that would be this function's scope, + # which wouldn't work. + set_ifndef(BOARD_${type}_RUNNER ${runner}) +endmacro() + +# A convenience macro for board_set_runner(FLASH ${runner}). +macro(board_set_flasher runner) + board_set_runner(FLASH ${runner}) +endmacro() + +# A convenience macro for board_set_runner(DEBUG ${runner}). +macro(board_set_debugger runner) + board_set_runner(DEBUG ${runner}) +endmacro() + +# A convenience macro for board_set_runner_ifnset(FLASH ${runner}). +macro(board_set_flasher_ifnset runner) + board_set_runner_ifnset(FLASH ${runner}) +endmacro() + +# A convenience macro for board_set_runner_ifnset(DEBUG ${runner}). +macro(board_set_debugger_ifnset runner) + board_set_runner_ifnset(DEBUG ${runner}) +endmacro() + +# This function is intended for board.cmake files and application +# CMakeLists.txt files. +# +# Usage from board.cmake files: +# board_runner_args(runner "--some-arg=val1" "--another-arg=val2") +# +# The build system will then ensure the command line used to +# create the runner contains: +# --some-arg=val1 --another-arg=val2 +# +# Within application CMakeLists.txt files, ensure that all calls to +# board_runner_args() are part of a macro named app_set_runner_args(), +# like this, which is defined before calling 'find_package(Zephyr)': +# macro(app_set_runner_args) +# board_runner_args(runner "--some-app-setting=value") +# endmacro() +# +# The build system tests for the existence of the macro and will +# invoke it at the appropriate time if it is defined. +# +# Any explicitly provided settings given by this function override +# defaults provided by the build system. +function(board_runner_args runner) + string(MAKE_C_IDENTIFIER ${runner} runner_id) + # Note the "_EXPLICIT_" here, and see below. + set_property(GLOBAL APPEND PROPERTY BOARD_RUNNER_ARGS_EXPLICIT_${runner_id} ${ARGN}) +endfunction() + +# This function is intended for internal use by +# boards/common/runner.board.cmake files. +# +# Basic usage: +# board_finalize_runner_args(runner) +# +# This ensures the build system captures all arguments added in any +# board_runner_args() calls, and otherwise finishes registering a +# runner for use. +# +# Extended usage: +# board_runner_args(runner "--some-arg=default-value") +# +# This provides common or default values for arguments. These are +# placed before board_runner_args() calls, so they generally take +# precedence, except for arguments which can be given multiple times +# (use these with caution). +function(board_finalize_runner_args runner) + # If the application provided a macro to add additional runner + # arguments, handle them. + if(COMMAND app_set_runner_args) + app_set_runner_args() + endif() + + # Retrieve the list of explicitly set arguments. + string(MAKE_C_IDENTIFIER ${runner} runner_id) + get_property(explicit GLOBAL PROPERTY "BOARD_RUNNER_ARGS_EXPLICIT_${runner_id}") + + # Note no _EXPLICIT_ here. This property contains the final list. + set_property(GLOBAL APPEND PROPERTY BOARD_RUNNER_ARGS_${runner_id} + # Default arguments from the common runner file come first. + ${ARGN} + # Arguments explicitly given with board_runner_args() come + # next, so they take precedence over the common runner file. + ${explicit} + # Arguments given via the CMake cache come last of all. Users + # can provide variables in this way from the CMake command line. + ${BOARD_RUNNER_ARGS_${runner_id}} + ) + + # Add the finalized runner to the global property list. + set_property(GLOBAL APPEND PROPERTY ZEPHYR_RUNNERS ${runner}) +endfunction() + +function(board_set_rimage_target target) + set(RIMAGE_TARGET ${target} CACHE STRING "rimage target") + zephyr_check_cache(RIMAGE_TARGET) +endfunction() + +# Zephyr board revision: +# +# This section provides a function for revision checking. + +# Usage: +# board_check_revision(FORMAT +# [EXACT] +# [DEFAULT_REVISION ] +# [HIGHEST_REVISION ] +# ) +# +# Zephyr board extension function. +# +# This function can be used in `boards//revision.cmake` to check a user +# requested revision against available board revisions. +# +# The function will check the revision from `-DBOARD=@` that +# is provided by the user according to the arguments. +# When `EXACT` is not specified, this function will set the Zephyr build system +# variable `ACTIVE_BOARD_REVISION` with the selected revision. +# +# FORMAT : Specify the revision format. +# LETTER: Revision format is a single letter from A - Z. +# NUMBER: Revision format is a single integer number. +# MAJOR.MINOR.PATCH: Revision format is three numbers, separated by `.`, +# `x.y.z`. Trailing zeroes may be omitted on the +# command line, which means: +# 1.0.0 == 1.0 == 1 +# +# OPTIONAL: Revision specifier is optional. If revision is not provided the base +# board will be used. If both `EXACT` and `OPTIONAL` are given, then +# specifying the revision is optional, but if it is given then the +# `EXACT` requirements apply. Mutually exclusive with `DEFAULT_REVISION`. +# +# EXACT: Revision is required to be an exact match. As example, available revisions are: +# 0.1.0 and 0.3.0, and user provides 0.2.0, then an error is reported +# when `EXACT` is given. +# If `EXACT` is not provided, then closest lower revision will be selected +# as the active revision, which in the example will be `0.1.0`. +# +# DEFAULT_REVISION: Provides a default revision to use when user has not selected +# a revision number. If no default revision is provided then +# user will be printed with an error if no revision is given +# on the command line. +# +# HIGHEST_REVISION: Allows to specify highest valid revision for a board. +# This can be used to ensure that a newer board cannot be used +# with an older Zephyr. As example, if current board supports +# revisions 0.x.0-0.99.99 and 1.0.0-1.99.99, and it is expected +# that current board implementation will not work with board +# revision 2.0.0, then HIGHEST_REVISION can be set to 1.99.99, +# and user will be printed with an error if using +# `@2.0.0` or higher. +# This field is not needed when `EXACT` is used. +# +# VALID_REVISIONS: A list of valid revisions for this board. +# If this argument is not provided, then each Kconfig fragment +# of the form ``_.conf`` in the board folder +# will be used as a valid revision for the board. +# +function(board_check_revision) + set(options OPTIONAL EXACT) + set(single_args FORMAT DEFAULT_REVISION HIGHEST_REVISION) + set(multi_args VALID_REVISIONS) + cmake_parse_arguments(BOARD_REV "${options}" "${single_args}" "${multi_args}" ${ARGN}) + + string(TOUPPER ${BOARD_REV_FORMAT} BOARD_REV_FORMAT) + + if(DEFINED BOARD_REV_DEFAULT_REVISION AND BOARD_REV_OPTIONAL) + message(FATAL_ERROR "Arguments BOARD_REVISION and OPTIONAL are mutually exclusive") + endif() + + if(NOT DEFINED BOARD_REVISION) + if(BOARD_REV_OPTIONAL) + return() + elseif(DEFINED BOARD_REV_DEFAULT_REVISION) + set(BOARD_REVISION ${BOARD_REV_DEFAULT_REVISION}) + set(BOARD_REVISION ${BOARD_REVISION} PARENT_SCOPE) + else() + message(FATAL_ERROR "No board revision specified, Board: `${BOARD}` \ + requires a revision. Please use: `-DBOARD=${BOARD}@`") + endif() + endif() + + if(DEFINED BOARD_REV_HIGHEST_REVISION) + if(((BOARD_REV_FORMAT STREQUAL LETTER) AND + (BOARD_REVISION STRGREATER BOARD_REV_HIGHEST_REVISION)) OR + ((BOARD_REV_FORMAT STREQUAL NUMBER) AND + (BOARD_REVISION GREATER BOARD_REV_HIGHEST_REVISION)) OR + ((BOARD_REV_FORMAT MATCHES "^MAJOR\.MINOR\.PATCH$") AND + (BOARD_REVISION VERSION_GREATER BOARD_REV_HIGHEST_REVISION)) + ) + message(FATAL_ERROR "Board revision `${BOARD_REVISION}` greater than \ + highest supported revision `${BOARD_REV_HIGHEST_REVISION}`. \ + Please specify a valid board revision.") + endif() + endif() + + if(BOARD_REV_FORMAT STREQUAL LETTER) + set(revision_regex "([A-Z])") + elseif(BOARD_REV_FORMAT STREQUAL NUMBER) + set(revision_regex "([0-9]+)") + elseif(BOARD_REV_FORMAT MATCHES "^MAJOR\.MINOR\.PATCH$") + set(revision_regex "((0|[1-9][0-9]*)(\.[0-9]+)(\.[0-9]+))") + # We allow loose @ typing on command line. + # so append missing zeroes. + if(BOARD_REVISION MATCHES "((0|[1-9][0-9]*)(\.[0-9]+)?(\.[0-9]+)?)") + if(NOT CMAKE_MATCH_3) + set(BOARD_REVISION ${BOARD_REVISION}.0) + set(BOARD_REVISION ${BOARD_REVISION} PARENT_SCOPE) + endif() + if(NOT CMAKE_MATCH_4) + set(BOARD_REVISION ${BOARD_REVISION}.0) + set(BOARD_REVISION ${BOARD_REVISION} PARENT_SCOPE) + endif() + endif() + else() + message(FATAL_ERROR "Invalid format specified for \ + `board_check_revision(FORMAT )`") + endif() + + if(NOT (BOARD_REVISION MATCHES "^${revision_regex}$")) + message(FATAL_ERROR "Invalid revision format used for `${BOARD_REVISION}`. \ + Board `${BOARD}` uses revision format: ${BOARD_REV_FORMAT}.") + endif() + + if(NOT DEFINED BOARD_REV_VALID_REVISIONS) + file(GLOB revision_candidates LIST_DIRECTORIES false RELATIVE ${BOARD_DIR} + ${BOARD_DIR}/${BOARD}_*.conf + ) + string(REPLACE "." "_" underscore_revision_regex ${revision_regex}) + set(file_revision_regex "${BOARD}_${underscore_revision_regex}.conf") + foreach(candidate ${revision_candidates}) + if(${candidate} MATCHES "${file_revision_regex}") + string(REPLACE "_" "." FOUND_BOARD_REVISION ${CMAKE_MATCH_1}) + list(APPEND BOARD_REV_VALID_REVISIONS ${FOUND_BOARD_REVISION}) + endif() + endforeach() + endif() + + if(${BOARD_REVISION} IN_LIST BOARD_REV_VALID_REVISIONS) + # Found exact match. + return() + endif() + + if(NOT BOARD_REV_EXACT) + foreach(TEST_REVISION ${BOARD_REV_VALID_REVISIONS}) + if((BOARD_REV_FORMAT MATCHES "^MAJOR\.MINOR\.PATCH$") AND + (${BOARD_REVISION} VERSION_GREATER_EQUAL ${TEST_REVISION}) AND + (${TEST_REVISION} VERSION_GREATER_EQUAL "${ACTIVE_BOARD_REVISION}") + ) + set(ACTIVE_BOARD_REVISION ${TEST_REVISION}) + elseif((BOARD_REV_FORMAT STREQUAL LETTER) AND + (${BOARD_REVISION} STRGREATER ${TEST_REVISION}) AND + (${TEST_REVISION} STRGREATER "${ACTIVE_BOARD_REVISION}") + ) + set(ACTIVE_BOARD_REVISION ${TEST_REVISION}) + elseif((BOARD_REV_FORMAT STREQUAL NUMBER) AND + (${BOARD_REVISION} GREATER ${TEST_REVISION}) AND + (${TEST_REVISION} GREATER "${ACTIVE_BOARD_REVISION}") + ) + set(ACTIVE_BOARD_REVISION ${TEST_REVISION}) + endif() + endforeach() + endif() + + if(BOARD_REV_EXACT OR NOT DEFINED ACTIVE_BOARD_REVISION) + message(FATAL_ERROR "Board revision `${BOARD_REVISION}` for board \ + `${BOARD}` not found. Please specify a valid board revision.") + endif() + + set(ACTIVE_BOARD_REVISION ${ACTIVE_BOARD_REVISION} PARENT_SCOPE) +endfunction() + +# 1.5. Misc. + +# zephyr_check_compiler_flag is a part of Zephyr's toolchain +# infrastructure. It should be used when testing toolchain +# capabilities and it should normally be used in place of the +# functions: +# +# check_compiler_flag +# check_c_compiler_flag +# check_cxx_compiler_flag +# +# See check_compiler_flag() for API documentation as it has the same +# API. +# +# It is implemented as a wrapper on top of check_compiler_flag, which +# again wraps the CMake-builtin's check_c_compiler_flag and +# check_cxx_compiler_flag. +# +# It takes time to check for compatibility of flags against toolchains +# so we cache the capability test results in USER_CACHE_DIR (This +# caching comes in addition to the caching that CMake does in the +# build folder's CMakeCache.txt) +function(zephyr_check_compiler_flag lang option check) + # Check if the option is covered by any hardcoded check before doing + # an automated test. + zephyr_check_compiler_flag_hardcoded(${lang} "${option}" _${check} exists) + if(exists) + set(${check} ${_${check}} PARENT_SCOPE) + return() + endif() + + # Locate the cache directory + set_ifndef( + ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR + ${USER_CACHE_DIR}/ToolchainCapabilityDatabase + ) + + # The toolchain capability database/cache is maintained as a + # directory of files. The filenames in the directory are keys, and + # the file contents are the values in this key-value store. + + # We need to create a unique key wrt. testing the toolchain + # capability. This key must include everything that can affect the + # toolchain test. + # + # Also, to fit the key into a filename we calculate the MD5 sum of + # the key. + + # The 'cacheformat' must be bumped if a bug in the caching mechanism + # is detected and all old keys must be invalidated. + set(cacheformat 3) + + set(key_string "") + set(key_string "${key_string}${cacheformat}_") + set(key_string "${key_string}${TOOLCHAIN_SIGNATURE}_") + set(key_string "${key_string}${lang}_") + set(key_string "${key_string}${option}_") + set(key_string "${key_string}${CMAKE_REQUIRED_FLAGS}_") + + string(MD5 key "${key_string}") + + # Check the cache + set(key_path ${ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR}/${key}) + if(EXISTS ${key_path}) + file(READ + ${key_path} # File to be read + key_value # Output variable + LIMIT 1 # Read at most 1 byte ('0' or '1') + ) + + set(${check} ${key_value} PARENT_SCOPE) + return() + endif() + + # Flags that start with -Wno- can not be tested by + # check_compiler_flag, they will always pass, but -W can be + # tested, so to test -Wno- flags we test -W + # instead. + if("${option}" MATCHES "-Wno-(.*)") + string(REPLACE "-Wno-" "-W" possibly_translated_option "${option}") + else() + set(possibly_translated_option ${option}) + endif() + + check_compiler_flag(${lang} "${possibly_translated_option}" inner_check) + + set(${check} ${inner_check} PARENT_SCOPE) + + # Populate the cache + if(NOT (EXISTS ${key_path})) + + # This is racy. As often with race conditions, this one can easily be + # made worse and demonstrated with a simple delay: + # execute_process(COMMAND "sleep" "5") + # Delete the cache, add the sleep above and run twister with a + # large number of JOBS. Once it's done look at the log.txt file + # below and you will see that concurrent cmake processes created the + # same files multiple times. + + # While there are a number of reasons why this race seems both very + # unlikely and harmless, let's play it safe anyway and write to a + # private, temporary file first. All modern filesystems seem to + # support at least one atomic rename API and cmake's file(RENAME + # ...) officially leverages that. + string(RANDOM LENGTH 8 tempsuffix) + + file( + WRITE + "${key_path}_tmp_${tempsuffix}" + ${inner_check} + ) + file( + RENAME + "${key_path}_tmp_${tempsuffix}" "${key_path}" + ) + + # Populate a metadata file (only intended for trouble shooting) + # with information about the hash, the toolchain capability + # result, and the toolchain test. + file( + APPEND + ${ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR}/log.txt + "${inner_check} ${key} ${key_string}\n" + ) + endif() +endfunction() + +function(zephyr_check_compiler_flag_hardcoded lang option check exists) + # Various flags that are not supported for CXX may not be testable + # because they would produce a warning instead of an error during + # the test. Exclude them by toolchain-specific blocklist. + if((${lang} STREQUAL CXX) AND ("${option}" IN_LIST CXX_EXCLUDED_OPTIONS)) + set(${check} 0 PARENT_SCOPE) + set(${exists} 1 PARENT_SCOPE) + else() + # There does not exist a hardcoded check for this option. + set(${exists} 0 PARENT_SCOPE) + endif() +endfunction(zephyr_check_compiler_flag_hardcoded) + +# zephyr_linker_sources( [SORT_KEY ] ) +# +# is one or more .ld formatted files whose contents will be +# copied/included verbatim into the given in the global linker.ld. +# Preprocessor directives work inside . Relative paths are resolved +# relative to the calling file, like zephyr_sources(). +# Subsequent calls to zephyr_linker_sources with the same file(s) will remove +# these from the original location. Only the last call is considered. +# is one of +# NOINIT Inside the noinit output section. +# RWDATA Inside the data output section. +# RODATA Inside the rodata output section. +# ROM_START Inside the first output section of the image. This option is +# currently only available on ARM Cortex-M, ARM Cortex-R, +# x86, ARC, openisa_rv32m1, and RISC-V. +# RAM_SECTIONS Inside the RAMABLE_REGION GROUP, not initialized. +# DATA_SECTIONS Inside the RAMABLE_REGION GROUP, initialized. +# RAMFUNC_SECTION Inside the RAMFUNC RAMABLE_REGION GROUP, not initialized. +# NOCACHE_SECTION Inside the NOCACHE section +# ITCM_SECTION Inside the itcm section. +# DTCM_SECTION Inside the dtcm data section. +# SECTIONS Near the end of the file. Don't use this when linking into +# RAMABLE_REGION, use RAM_SECTIONS instead. +# PINNED_RODATA Similar to RODATA but pinned in memory. +# PINNED_RAM_SECTIONS +# Similar to RAM_SECTIONS but pinned in memory. +# PINNED_DATA_SECTIONS +# Similar to DATA_SECTIONS but pinned in memory. +# is an optional key to sort by inside of each location. The key must +# be alphanumeric, and the keys are sorted alphabetically. If no key is +# given, the key 'default' is used. Keys are case-sensitive. +# +# Use NOINIT, RWDATA, and RODATA unless they don't work for your use case. +# +# When placing into NOINIT, RWDATA, RODATA, ROM_START, RAMFUNC_SECTION, +# NOCACHE_SECTION, DTCM_SECTION or ITCM_SECTION the contents of the files will +# be placed inside an output section, so assume the section definition is +# already present, e.g.: +# _mysection_start = .; +# KEEP(*(.mysection)); +# _mysection_end = .; +# _mysection_size = ABSOLUTE(_mysection_end - _mysection_start); +# +# When placing into SECTIONS, RAM_SECTIONS or DATA_SECTIONS, the files must +# instead define their own output sections to achieve the same thing: +# SECTION_PROLOGUE(.mysection,,) +# { +# _mysection_start = .; +# KEEP(*(.mysection)) +# _mysection_end = .; +# } GROUP_LINK_IN(ROMABLE_REGION) +# _mysection_size = _mysection_end - _mysection_start; +# +# Note about the above examples: If the first example was used with RODATA, and +# the second with SECTIONS, the two examples do the same thing from a user +# perspective. +# +# Friendly reminder: Beware of the different ways the location counter ('.') +# behaves inside vs. outside section definitions. +function(zephyr_linker_sources location) + # Set up the paths to the destination files. These files are #included inside + # the global linker.ld. + set(snippet_base "${__build_dir}/include/generated") + set(sections_path "${snippet_base}/snippets-sections.ld") + set(ram_sections_path "${snippet_base}/snippets-ram-sections.ld") + set(data_sections_path "${snippet_base}/snippets-data-sections.ld") + set(rom_start_path "${snippet_base}/snippets-rom-start.ld") + set(noinit_path "${snippet_base}/snippets-noinit.ld") + set(rwdata_path "${snippet_base}/snippets-rwdata.ld") + set(rodata_path "${snippet_base}/snippets-rodata.ld") + set(ramfunc_path "${snippet_base}/snippets-ramfunc-section.ld") + set(nocache_path "${snippet_base}/snippets-nocache-section.ld") + set(itcm_path "${snippet_base}/snippets-itcm-section.ld") + set(dtcm_path "${snippet_base}/snippets-dtcm-section.ld") + + set(pinned_ram_sections_path "${snippet_base}/snippets-pinned-ram-sections.ld") + set(pinned_data_sections_path "${snippet_base}/snippets-pinned-data-sections.ld") + set(pinned_rodata_path "${snippet_base}/snippets-pinned-rodata.ld") + + # Clear destination files if this is the first time the function is called. + get_property(cleared GLOBAL PROPERTY snippet_files_cleared) + if (NOT DEFINED cleared) + file(WRITE ${sections_path} "") + file(WRITE ${ram_sections_path} "") + file(WRITE ${data_sections_path} "") + file(WRITE ${rom_start_path} "") + file(WRITE ${noinit_path} "") + file(WRITE ${rwdata_path} "") + file(WRITE ${rodata_path} "") + file(WRITE ${ramfunc_path} "") + file(WRITE ${nocache_path} "") + file(WRITE ${itcm_path} "") + file(WRITE ${dtcm_path} "") + file(WRITE ${pinned_ram_sections_path} "") + file(WRITE ${pinned_data_sections_path} "") + file(WRITE ${pinned_rodata_path} "") + set_property(GLOBAL PROPERTY snippet_files_cleared true) + endif() + + # Choose destination file, based on the argument. + if ("${location}" STREQUAL "SECTIONS") + set(snippet_path "${sections_path}") + elseif("${location}" STREQUAL "RAM_SECTIONS") + set(snippet_path "${ram_sections_path}") + elseif("${location}" STREQUAL "DATA_SECTIONS") + set(snippet_path "${data_sections_path}") + elseif("${location}" STREQUAL "ROM_START") + set(snippet_path "${rom_start_path}") + elseif("${location}" STREQUAL "NOINIT") + set(snippet_path "${noinit_path}") + elseif("${location}" STREQUAL "RWDATA") + set(snippet_path "${rwdata_path}") + elseif("${location}" STREQUAL "RODATA") + set(snippet_path "${rodata_path}") + elseif("${location}" STREQUAL "RAMFUNC_SECTION") + set(snippet_path "${ramfunc_path}") + elseif("${location}" STREQUAL "NOCACHE_SECTION") + set(snippet_path "${nocache_path}") + elseif("${location}" STREQUAL "ITCM_SECTION") + dt_has_chosen(HAS_ITCM PROPERTY "zephyr,itcm") + if(NOT HAS_ITCM) + message(FATAL_ERROR "Trying to link snippet into itcm but no itcm available. " + "Add `zephyr,itcm` to the device tree if supported on your device or choose another " + "location.") + endif() + set(snippet_path "${itcm_path}") + elseif("${location}" STREQUAL "DTCM_SECTION") + dt_has_chosen(HAS_DTCM PROPERTY "zephyr,dtcm") + if(NOT HAS_DTCM) + message(FATAL_ERROR "Trying to link snippet into dtcm but no dtcm available. " + "Add `zephyr,dtcm` to the device tree if supported on your device or choose another " + "location.") + endif() + set(snippet_path "${dtcm_path}") + elseif("${location}" STREQUAL "PINNED_RAM_SECTIONS") + set(snippet_path "${pinned_ram_sections_path}") + elseif("${location}" STREQUAL "PINNED_DATA_SECTIONS") + set(snippet_path "${pinned_data_sections_path}") + elseif("${location}" STREQUAL "PINNED_RODATA") + set(snippet_path "${pinned_rodata_path}") + else() + message(fatal_error "Must choose valid location for linker snippet.") + endif() + + cmake_parse_arguments(L "" "SORT_KEY" "" ${ARGN}) + set(SORT_KEY default) + if(DEFINED L_SORT_KEY) + set(SORT_KEY ${L_SORT_KEY}) + endif() + + foreach(file IN ITEMS ${L_UNPARSED_ARGUMENTS}) + # Resolve path. + if(IS_ABSOLUTE ${file}) + set(path ${file}) + else() + set(path ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + endif() + + if(IS_DIRECTORY ${path}) + message(FATAL_ERROR "zephyr_linker_sources() was called on a directory") + endif() + + # Find the relative path to the linker file from the include folder. + file(RELATIVE_PATH relpath ${ZEPHYR_BASE}/include ${path}) + + # Create strings to be written into the file + set (include_str "/* Sort key: \"${SORT_KEY}\" */#include \"${relpath}\"") + + # Remove line from other snippet file, if already used + get_property(old_path GLOBAL PROPERTY "snippet_files_used_${relpath}") + if (DEFINED old_path) + file(STRINGS ${old_path} lines) + list(FILTER lines EXCLUDE REGEX ${relpath}) + string(REPLACE ";" "\n;" lines "${lines}") # Add newline to each line. + file(WRITE ${old_path} ${lines} "\n") + endif() + set_property(GLOBAL PROPERTY "snippet_files_used_${relpath}" ${snippet_path}) + + # Add new line to existing lines, sort them, and write them back. + file(STRINGS ${snippet_path} lines) # Get current lines (without newlines). + list(APPEND lines ${include_str}) + list(SORT lines) + string(REPLACE ";" "\n;" lines "${lines}") # Add newline to each line. + file(WRITE ${snippet_path} ${lines} "\n") + endforeach() +endfunction(zephyr_linker_sources) + +# Helper macro for conditionally calling zephyr_code_relocate() when a +# specific Kconfig symbol is enabled. See zephyr_code_relocate() description +# for supported arguments. +macro(zephyr_code_relocate_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_code_relocate(${ARGN}) + endif() +endmacro() + +# Helper function for CONFIG_CODE_DATA_RELOCATION +# This function may either be invoked with a list of files, or a library +# name to relocate. +# +# The FILES directive will relocate a list of files (wildcards supported) +# This directive will relocate file1. and file2.c to SRAM: +# zephyr_code_relocate(FILES file1.c file2.c LOCATION SRAM) +# Note, files can also be passed as a comma separated list to support using +# cmake generator arguments +# +# The LIBRARY directive will relocate a library +# This directive will relocate the target my_lib to SRAM: +# zephyr_code_relocate(LIBRARY my_lib SRAM) +# +# The following optional arguments are supported: +# - NOCOPY: this flag indicates that the file data does not need to be copied +# at boot time (For example, for flash XIP). +# - NOKEEP: suppress the generation of KEEP() statements in the linker script, +# to allow any unused code in the given files/library to be discarded. +# - PHDR [program_header]: add program header. Used on Xtensa platforms. +function(zephyr_code_relocate) + set(options NOCOPY NOKEEP) + set(single_args LIBRARY LOCATION PHDR) + set(multi_args FILES) + cmake_parse_arguments(CODE_REL "${options}" "${single_args}" + "${multi_args}" ${ARGN}) + # Argument validation + if(CODE_REL_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "zephyr_code_relocate(${ARGV0} ...) " + "given unknown arguments: ${CODE_REL_UNPARSED_ARGUMENTS}") + endif() + if((NOT CODE_REL_FILES) AND (NOT CODE_REL_LIBRARY)) + message(FATAL_ERROR + "zephyr_code_relocate() requires either FILES or LIBRARY be provided") + endif() + if(CODE_REL_FILES AND CODE_REL_LIBRARY) + message(FATAL_ERROR "zephyr_code_relocate() only accepts " + "one argument between FILES and LIBRARY") + endif() + if(NOT CODE_REL_LOCATION) + message(FATAL_ERROR "zephyr_code_relocate() requires a LOCATION argument") + endif() + if(CODE_REL_LIBRARY) + # Use cmake generator expression to convert library to file list, + # supporting relative and absolute paths + set(genex_src_dir "$") + set(genex_src_list "$") + + if(CMAKE_HOST_WIN32) + # Note that this assumes windows absolute filenames start with a letter and colon, this does + # not support \\x network paths and is untested under the likes of msys2/cygwin + set(src_list_abs "$") + set(src_list_rel "$") + else() + set(src_list_abs "$") + set(src_list_rel "$") + endif() + + set(src_list "${genex_src_dir}/$${genex_src_dir}/>") + set(nonempty_src_list "$<$:${src_list}>") + set(sep_list "$<$,$>:$>") + set(file_list "${src_list_abs}${sep_list}${nonempty_src_list}") + else() + # Check if CODE_REL_FILES is a generator expression, if so leave it + # untouched. + string(GENEX_STRIP "${CODE_REL_FILES}" no_genex) + if(CODE_REL_FILES STREQUAL no_genex) + # no generator expression in CODE_REL_FILES, check if list of files + # is absolute + foreach(file ${CODE_REL_FILES}) + if(NOT IS_ABSOLUTE ${file}) + set(file ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + endif() + list(APPEND file_list ${file}) + endforeach() + else() + # Generator expression is present in file list. Leave the list untouched. + set(file_list ${CODE_REL_FILES}) + endif() + endif() + if(NOT CODE_REL_NOCOPY) + set(flag_list COPY) + else() + set(flag_list NOCOPY) + endif() + if(CODE_REL_NOKEEP) + list(APPEND flag_list NOKEEP) + endif() + if(CODE_REL_PHDR) + set(CODE_REL_LOCATION "${CODE_REL_LOCATION}\ :${CODE_REL_PHDR}") + endif() + # We use the "|" character to separate code relocation directives, instead of + # using set_property(APPEND) to produce a ";"-separated CMake list. This way, + # each directive can embed multiple CMake lists, representing flags and files, + # the latter of which can come from generator expressions. + get_property(code_rel_str TARGET code_data_relocation_target + PROPERTY COMPILE_DEFINITIONS) + set_property(TARGET code_data_relocation_target + PROPERTY COMPILE_DEFINITIONS + "${code_rel_str}|${CODE_REL_LOCATION}:${flag_list}:${file_list}") +endfunction() + +# Usage: +# check_dtc_flag("-Wtest" DTC_WARN_TEST) +# +# Writes 1 to the output variable 'ok' if +# the flag is supported, otherwise writes 0. +# +# using +function(check_dtc_flag flag ok) + execute_process( + COMMAND + ${DTC} ${flag} -v + ERROR_QUIET + OUTPUT_QUIET + RESULT_VARIABLE dtc_check_ret + ) + if (dtc_check_ret EQUAL 0) + set(${ok} 1 PARENT_SCOPE) + else() + set(${ok} 0 PARENT_SCOPE) + endif() +endfunction() + +# Function to round number to next power of two. +# +# Usage: +# pow2round() +# +# Example: +# set(test 2) +# pow2round(test) +# # test is still 2 +# +# set(test 5) +# pow2round(test) +# # test is now 8 +# +# Arguments: +# n = Variable containing the number to round +function(pow2round n) + math(EXPR x "${${n}} & (${${n}} - 1)") + if(${x} EQUAL 0) + return() + endif() + + math(EXPR ${n} "${${n}} | (${${n}} >> 1)") + math(EXPR ${n} "${${n}} | (${${n}} >> 2)") + math(EXPR ${n} "${${n}} | (${${n}} >> 4)") + math(EXPR ${n} "${${n}} | (${${n}} >> 8)") + math(EXPR ${n} "${${n}} | (${${n}} >> 16)") + math(EXPR ${n} "${${n}} | (${${n}} >> 32)") + math(EXPR ${n} "${${n}} + 1") + set(${n} ${${n}} PARENT_SCOPE) +endfunction() + +# Function to create a build string based on BOARD, BOARD_REVISION, and BUILD +# type. +# +# This is a common function to ensure that build strings are always created +# in a uniform way. +# A single string is returned containing the full build string constructed from +# all arguments. +# +# When MERGE is supplied a list of build strings will be returned with the full +# build string as first item in the list. +# The full order of build strings returned in the list will be: +# - Normalized board target build string, this includes qualifiers and revision +# - Build string with board variants removed in addition +# - Build string with cpuset removed in addition +# - Build string with soc removed in addition +# +# If BUILD is supplied, then build type will be appended to each entry in the +# list above. +# If REVISION is supplied or obtained as system wide setting a build string +# with the sanitized revision string will be added in addition to the +# non-revisioned entry for each entry. +# +# Usage: +# zephyr_build_string( +# BOARD +# [BOARD_QUALIFIERS ] +# [BOARD_REVISION ] +# [BUILD ] +# [MERGE [REVERSE]] +# ) +# +# : Output variable where the build string will be returned. +# SHORT : Output variable where the shortened build string will be returned. +# BOARD : Board name to use when creating the build string. +# BOARD_REVISION : Board revision to use when creating the build string. +# BUILD : Build type to use when creating the build string. +# MERGE: Return a list of build strings instead of a single build string. +# REVERSE: Reverse the list before returning it. +# +# Examples +# calling +# zephyr_build_string(build_string BOARD alpha BUILD debug) +# will return the string `alpha_debug` in `build_string` parameter. +# +# calling +# zephyr_build_string(build_string BOARD alpha BOARD_REVISION 1.0.0 BUILD debug) +# will return the string `alpha_1_0_0_debug` in `build_string` parameter. +# +# calling +# zephyr_build_string(build_string BOARD alpha BOARD_QUALIFIERS /soc/bar) +# will return the string `alpha_soc_bar` in `build_string` parameter. +# +# calling +# zephyr_build_string(build_string BOARD alpha BOARD_REVISION 1.0.0 BOARD_QUALIFIERS /soc/bar MERGE) +# will return a list of the following strings +# `alpha_soc_bar_1_0_0;alpha_soc_bar` in `build_string` parameter. +# +# calling +# zephyr_build_string(build_string SHORTENED short_build_string BOARD alpha BOARD_REVISION 1.0.0 BOARD_QUALIFIERS /soc/bar MERGE) +# will return two lists of the following strings +# `alpha_soc_bar_1_0_0;alpha_soc_bar` in `build_string` parameter. +# `alpha_bar_1_0_0;alpha_bar` in `short_build_string` parameter. +# +function(zephyr_build_string outvar) + set(options MERGE REVERSE) + set(single_args BOARD BOARD_QUALIFIERS BOARD_REVISION BUILD SHORT) + + cmake_parse_arguments(BUILD_STR "${options}" "${single_args}" "" ${ARGN}) + if(BUILD_STR_UNPARSED_ARGUMENTS) + message(FATAL_ERROR + "zephyr_build_string(${ARGV0} ...) given unknown arguments:" + " ${BUILD_STR_UNPARSED_ARGUMENTS}" + ) + endif() + + if(DEFINED BUILD_STR_BOARD_REVISION AND NOT BUILD_STR_BOARD) + message(FATAL_ERROR + "zephyr_build_string(${ARGV0} BOARD_REVISION ${BUILD_STR_BOARD_REVISION} ...)" + " given without BOARD argument, please specify BOARD" + ) + endif() + + if(DEFINED BUILD_STR_BOARD_QUALIFIERS AND NOT BUILD_STR_BOARD) + message(FATAL_ERROR + "zephyr_build_string(${ARGV0} BOARD_QUALIFIERS ${BUILD_STR_BOARD_QUALIFIERS} ...)" + " given without BOARD argument, please specify BOARD" + ) + endif() + + string(REPLACE "/" ";" str_segment_list "${BUILD_STR_BOARD_QUALIFIERS}") + string(REPLACE "." "_" revision_string "${BUILD_STR_BOARD_REVISION}") + + string(JOIN "_" ${outvar} ${BUILD_STR_BOARD} ${str_segment_list} ${revision_string} ${BUILD_STR_BUILD}) + + if(BUILD_STR_MERGE) + string(JOIN "_" variant_string ${BUILD_STR_BOARD} ${str_segment_list} ${BUILD_STR_BUILD}) + + if(NOT "${variant_string}" IN_LIST ${outvar}) + list(APPEND ${outvar} "${variant_string}") + endif() + endif() + + if(BUILD_STR_REVERSE) + list(REVERSE ${outvar}) + endif() + list(REMOVE_DUPLICATES ${outvar}) + + if(BUILD_STR_SHORT AND BUILD_STR_BOARD_QUALIFIERS) + string(REGEX REPLACE "^/[^/]*(.*)" "\\1" shortened_qualifiers "${BOARD_QUALIFIERS}") + string(REPLACE "/" ";" str_short_segment_list "${shortened_qualifiers}") + string(JOIN "_" ${BUILD_STR_SHORT} + ${BUILD_STR_BOARD} ${str_short_segment_list} ${revision_string} ${BUILD_STR_BUILD} + ) + if(BUILD_STR_MERGE) + string(JOIN "_" variant_string ${BUILD_STR_BOARD} ${str_short_segment_list} ${BUILD_STR_BUILD}) + + if(NOT "${variant_string}" IN_LIST ${BUILD_STR_SHORT}) + list(APPEND ${BUILD_STR_SHORT} "${variant_string}") + endif() + endif() + + if(BUILD_STR_REVERSE) + list(REVERSE ${BUILD_STR_SHORT}) + endif() + list(REMOVE_DUPLICATES ${BUILD_STR_SHORT}) + set(${BUILD_STR_SHORT} ${${BUILD_STR_SHORT}} PARENT_SCOPE) + endif() + + # This updates the provided outvar in parent scope (callers scope) + set(${outvar} ${${outvar}} PARENT_SCOPE) +endfunction() + +# Function to add one or more directories to the include list passed to the syscall generator. +function(zephyr_syscall_include_directories) + foreach(one_dir ${ARGV}) + if(EXISTS ${one_dir}) + set(include_dir ${one_dir}) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${one_dir}) + set(include_dir ${CMAKE_CURRENT_SOURCE_DIR}/${one_dir}) + else() + message(FATAL_ERROR "Syscall include directory not found: ${one_dir}") + endif() + + target_include_directories( + syscalls_interface INTERFACE + ${include_dir} + ) + add_dependencies( + syscalls_interface + ${include_dir} + ) + + unset(include_dir) + endforeach() +endfunction() + +# Function to add header file(s) to the list to be passed to syscall generator. +function(zephyr_syscall_header) + foreach(one_file ${ARGV}) + if(EXISTS ${one_file}) + set(header_file ${one_file}) + elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${one_file}) + set(header_file ${CMAKE_CURRENT_SOURCE_DIR}/${one_file}) + else() + message(FATAL_ERROR "Syscall header file not found: ${one_file}") + endif() + + target_sources( + syscalls_interface INTERFACE + ${header_file} + ) + add_dependencies( + syscalls_interface + ${header_file} + ) + + unset(header_file) + endforeach() +endfunction() + +# Function to add header file(s) to the list to be passed to syscall generator +# if condition is true. +function(zephyr_syscall_header_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_syscall_header(${ARGN}) + endif() +endfunction() + +# Verify blobs fetched using west. If the sha256 checksum isn't valid, a warning/ +# fatal error message is printed (depends on REQUIRED flag). +# +# Usage: +# zephyr_blobs_verify( [REQUIRED]) +# +# Example: +# zephyr_blobs_verify(MODULE my_module REQUIRED) # verify all blobs in my_module and fail on error +# zephyr_blobs_verify(FILES img/file.bin) # verify a single file and print on error +function(zephyr_blobs_verify) + cmake_parse_arguments(BLOBS_VERIFY "REQUIRED" "MODULE" "FILES" ${ARGN}) + + if((DEFINED BLOBS_VERIFY_MODULE) EQUAL (DEFINED BLOBS_VERIFY_FILES)) + message(FATAL_ERROR "Either MODULE or FILES required when calling ${CMAKE_CURRENT_FUNCTION}") + endif() + + if(NOT WEST) + return() + endif() + + execute_process( + COMMAND ${WEST} blobs list ${BLOBS_VERIFY_MODULE} --format "{status} {abspath}" + OUTPUT_VARIABLE BLOBS_LIST_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY + ) + + if(${BLOBS_VERIFY_REQUIRED}) + set(msg_lvl FATAL_ERROR) + else() + set(msg_lvl WARNING) + endif() + + string(REPLACE "\n" ";" BLOBS_LIST ${BLOBS_LIST_OUTPUT}) + + if(DEFINED BLOBS_VERIFY_FILES) + foreach(file ${BLOBS_VERIFY_FILES}) + # Resolve path. + if(IS_ABSOLUTE ${file}) + file(REAL_PATH "${file}" real_path) + else() + file(REAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${file}" real_path) + endif() + file(TO_NATIVE_PATH ${real_path} path) + + message(VERBOSE "Verifying blob \"${path}\"") + + # Each path that has a correct sha256 is prefixed with an A + if(NOT "A ${path}" IN_LIST BLOBS_LIST) + message(${msg_lvl} "Blob for path \"${path}\" isn't valid.") + endif() + endforeach() + else() + foreach(blob ${BLOBS_LIST}) + separate_arguments(blob) + list(GET blob 0 status) + list(GET blob 1 path) + + message(VERBOSE "Verifying blob \"${path}\"") + + if(NOT "${status}" STREQUAL "A") + message(${msg_lvl} "Blob for path \"${path}\" isn't valid. Update with: west blobs fetch ${BLOBS_VERIFY_MODULE}") + endif() + endforeach() + endif() +endfunction() + +######################################################## +# 2. Kconfig-aware extensions +######################################################## +# +# Kconfig is a configuration language developed for the Linux +# kernel. The below functions integrate CMake with Kconfig. +# + +# 2.1 Misc +# +# import_kconfig( [] [TARGET ]) +# +# Parse a KConfig fragment (typically with extension .config) and +# introduce all the symbols that are prefixed with 'prefix' into the +# CMake namespace. List all created variable names in the 'keys' +# output variable if present. +# +# : symbol prefix of settings in the Kconfig fragment. +# : absolute path to the config fragment file. +# : output variable which will be populated with variable +# names loaded from the kconfig fragment. +# TARGET : set all symbols on instead of adding them to the +# CMake namespace. +function(import_kconfig prefix kconfig_fragment) + cmake_parse_arguments(IMPORT_KCONFIG "" "TARGET" "" ${ARGN}) + file( + STRINGS + ${kconfig_fragment} + DOT_CONFIG_LIST + ENCODING "UTF-8" + ) + + foreach (LINE ${DOT_CONFIG_LIST}) + if("${LINE}" MATCHES "^(${prefix}[^=]+)=([ymn]|.+$)") + # Matched a normal value assignment, like: CONFIG_NET_BUF=y + # Note: if the value starts with 'y', 'm', or 'n', then we assume it's a + # bool or tristate (we don't know the type from alone) + # and we only match the first character. This is to align with Kconfiglib. + set(CONF_VARIABLE_NAME "${CMAKE_MATCH_1}") + set(CONF_VARIABLE_VALUE "${CMAKE_MATCH_2}") + elseif("${LINE}" MATCHES "^# (${prefix}[^ ]+) is not set") + # Matched something like: # CONFIG_FOO is not set + # This is interpreted as: CONFIG_FOO=n + set(CONF_VARIABLE_NAME "${CMAKE_MATCH_1}") + set(CONF_VARIABLE_VALUE "n") + else() + # Ignore this line. + # Note: we also ignore assignments which don't have the desired . + continue() + endif() + + # If the provided value is n, then the corresponding CMake variable or + # target property will be unset. + if("${CONF_VARIABLE_VALUE}" STREQUAL "n") + if(DEFINED IMPORT_KCONFIG_TARGET) + set_property(TARGET ${IMPORT_KCONFIG_TARGET} PROPERTY "${CONF_VARIABLE_NAME}") + else() + unset("${CONF_VARIABLE_NAME}" PARENT_SCOPE) + endif() + list(REMOVE_ITEM keys "${CONF_VARIABLE_NAME}") + continue() + endif() + + # Otherwise, the variable/property will be set to the provided value. + # For string values, we also remove the surrounding quotation marks. + if("${CONF_VARIABLE_VALUE}" MATCHES "^\"(.*)\"$") + set(CONF_VARIABLE_VALUE ${CMAKE_MATCH_1}) + endif() + + if(DEFINED IMPORT_KCONFIG_TARGET) + set_property(TARGET ${IMPORT_KCONFIG_TARGET} PROPERTY "${CONF_VARIABLE_NAME}" "${CONF_VARIABLE_VALUE}") + else() + set("${CONF_VARIABLE_NAME}" "${CONF_VARIABLE_VALUE}" PARENT_SCOPE) + endif() + list(APPEND keys "${CONF_VARIABLE_NAME}") + endforeach() + + if(DEFINED IMPORT_KCONFIG_TARGET) + set_property(TARGET ${IMPORT_KCONFIG_TARGET} PROPERTY "kconfigs" "${keys}") + endif() + + list(LENGTH IMPORT_KCONFIG_UNPARSED_ARGUMENTS unparsed_length) + if(unparsed_length GREATER 0) + if(unparsed_length GREATER 1) + # Two mandatory arguments and one optional, anything after that is an error. + list(GET IMPORT_KCONFIG_UNPARSED_ARGUMENTS 1 first_invalid) + message(FATAL_ERROR "Unexpected argument after '': import_kconfig(... ${first_invalid})") + endif() + set(${IMPORT_KCONFIG_UNPARSED_ARGUMENTS} "${keys}" PARENT_SCOPE) + endif() +endfunction() + +######################################################## +# 3. CMake-generic extensions +######################################################## +# +# These functions extend the CMake API in a way that is not particular +# to Zephyr. Primarily they work around limitations in the CMake +# language to allow cleaner build scripts. + +# 3.1. *_ifdef +# +# Functions for conditionally executing CMake functions with oneliners +# e.g. +# +# if(CONFIG_FFT) +# zephyr_library_source( +# fft_32.c +# fft_utils.c +# ) +# endif() +# +# Becomes +# +# zephyr_source_ifdef( +# CONFIG_FFT +# fft_32.c +# fft_utils.c +# ) +# +# More Generally +# "_ifdef(CONDITION args)" +# Becomes +# """ +# if(CONDITION) +# (args) +# endif() +# """ +# +# ifdef functions are added on an as-need basis. See +# https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for +# a list of available functions. +function(add_subdirectory_ifdef feature_toggle source_dir) + if(${${feature_toggle}}) + add_subdirectory(${source_dir} ${ARGN}) + endif() +endfunction() + +function(target_sources_ifdef feature_toggle target scope item) + if(${${feature_toggle}}) + target_sources(${target} ${scope} ${item} ${ARGN}) + endif() +endfunction() + +function(target_compile_definitions_ifdef feature_toggle target scope item) + if(${${feature_toggle}}) + target_compile_definitions(${target} ${scope} ${item} ${ARGN}) + endif() +endfunction() + +function(target_include_directories_ifdef feature_toggle target scope item) + if(${${feature_toggle}}) + target_include_directories(${target} ${scope} ${item} ${ARGN}) + endif() +endfunction() + +function(target_link_libraries_ifdef feature_toggle target item) + if(${${feature_toggle}}) + target_link_libraries(${target} ${item} ${ARGN}) + endif() +endfunction() + +function(add_compile_option_ifdef feature_toggle option) + if(${${feature_toggle}}) + add_compile_options(${option}) + endif() +endfunction() + +function(target_compile_option_ifdef feature_toggle target scope option) + if(${feature_toggle}) + target_compile_options(${target} ${scope} ${option}) + endif() +endfunction() + +function(target_cc_option_ifdef feature_toggle target scope option) + if(${feature_toggle}) + target_cc_option(${target} ${scope} ${option}) + endif() +endfunction() + +function(zephyr_library_sources_ifdef feature_toggle source) + if(${${feature_toggle}}) + zephyr_library_sources(${source} ${ARGN}) + endif() +endfunction() + +function(zephyr_sources_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_sources(${ARGN}) + endif() +endfunction() + +function(zephyr_cc_option_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_cc_option(${ARGN}) + endif() +endfunction() + +function(zephyr_ld_option_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_ld_options(${ARGN}) + endif() +endfunction() + +function(zephyr_link_libraries_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_link_libraries(${ARGN}) + endif() +endfunction() + +function(zephyr_compile_options_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_compile_options(${ARGN}) + endif() +endfunction() + +function(zephyr_compile_definitions_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_compile_definitions(${ARGN}) + endif() +endfunction() + +function(zephyr_include_directories_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_include_directories(${ARGN}) + endif() +endfunction() + +function(zephyr_library_compile_definitions_ifdef feature_toggle item) + if(${${feature_toggle}}) + zephyr_library_compile_definitions(${item} ${ARGN}) + endif() +endfunction() + +function(zephyr_library_include_directories_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_library_include_directories(${ARGN}) + endif() +endfunction() + +function(zephyr_library_compile_options_ifdef feature_toggle item) + if(${${feature_toggle}}) + zephyr_library_compile_options(${item} ${ARGN}) + endif() +endfunction() + +function(zephyr_link_interface_ifdef feature_toggle interface) + if(${${feature_toggle}}) + target_link_libraries(${interface} INTERFACE zephyr_interface) + endif() +endfunction() + +function(zephyr_library_link_libraries_ifdef feature_toggle item) + if(${${feature_toggle}}) + zephyr_library_link_libraries(${item}) + endif() +endfunction() + +function(zephyr_linker_sources_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_linker_sources(${ARGN}) + endif() +endfunction() + +function(zephyr_library_add_dependencies_ifdef feature_toggle) + if(${${feature_toggle}}) + zephyr_library_add_dependencies(${ARGN}) + endif() +endfunction() + +macro(list_append_ifdef feature_toggle list) + if(${${feature_toggle}}) + list(APPEND ${list} ${ARGN}) + endif() +endmacro() + +# 3.2. *_ifndef +# See 3.1 *_ifdef +function(set_ifndef variable value) + if(NOT ${variable}) + set(${variable} ${value} ${ARGN} PARENT_SCOPE) + endif() +endfunction() + +function(add_subdirectory_ifndef feature_toggle source_dir) + if(NOT ${feature_toggle}) + add_subdirectory(${source_dir} ${ARGN}) + endif() +endfunction() + +function(target_sources_ifndef feature_toggle target scope item) + if(NOT ${feature_toggle}) + target_sources(${target} ${scope} ${item} ${ARGN}) + endif() +endfunction() + +function(target_compile_definitions_ifndef feature_toggle target scope item) + if(NOT ${feature_toggle}) + target_compile_definitions(${target} ${scope} ${item} ${ARGN}) + endif() +endfunction() + +function(target_include_directories_ifndef feature_toggle target scope item) + if(NOT ${feature_toggle}) + target_include_directories(${target} ${scope} ${item} ${ARGN}) + endif() +endfunction() + +function(target_link_libraries_ifndef feature_toggle target item) + if(NOT ${feature_toggle}) + target_link_libraries(${target} ${item} ${ARGN}) + endif() +endfunction() + +function(add_compile_option_ifndef feature_toggle option) + if(NOT ${feature_toggle}) + add_compile_options(${option}) + endif() +endfunction() + +function(target_compile_option_ifndef feature_toggle target scope option) + if(NOT ${feature_toggle}) + target_compile_options(${target} ${scope} ${option}) + endif() +endfunction() + +function(target_cc_option_ifndef feature_toggle target scope option) + if(NOT ${feature_toggle}) + target_cc_option(${target} ${scope} ${option}) + endif() +endfunction() + +function(zephyr_library_sources_ifndef feature_toggle source) + if(NOT ${feature_toggle}) + zephyr_library_sources(${source} ${ARGN}) + endif() +endfunction() + +function(zephyr_sources_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_sources(${ARGN}) + endif() +endfunction() + +function(zephyr_cc_option_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_cc_option(${ARGN}) + endif() +endfunction() + +function(zephyr_ld_option_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_ld_options(${ARGN}) + endif() +endfunction() + +function(zephyr_link_libraries_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_link_libraries(${ARGN}) + endif() +endfunction() + +function(zephyr_compile_options_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_compile_options(${ARGN}) + endif() +endfunction() + +function(zephyr_compile_definitions_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_compile_definitions(${ARGN}) + endif() +endfunction() + +function(zephyr_include_directories_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_include_directories(${ARGN}) + endif() +endfunction() + +function(zephyr_library_compile_definitions_ifndef feature_toggle item) + if(NOT ${feature_toggle}) + zephyr_library_compile_definitions(${item} ${ARGN}) + endif() +endfunction() + +function(zephyr_library_include_directories_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_library_include_directories(${ARGN}) + endif() +endfunction() + +function(zephyr_library_compile_options_ifndef feature_toggle item) + if(NOT ${feature_toggle}) + zephyr_library_compile_options(${item} ${ARGN}) + endif() +endfunction() + +function(zephyr_link_interface_ifndef feature_toggle interface) + if(NOT ${feature_toggle}) + target_link_libraries(${interface} INTERFACE zephyr_interface) + endif() +endfunction() + +function(zephyr_library_link_libraries_ifndef feature_toggle item) + if(NOT ${feature_toggle}) + zephyr_library_link_libraries(${item}) + endif() +endfunction() + +function(zephyr_linker_sources_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_linker_sources(${ARGN}) + endif() +endfunction() + +function(zephyr_library_add_dependencies_ifndef feature_toggle) + if(NOT ${feature_toggle}) + zephyr_library_add_dependencies(${ARGN}) + endif() +endfunction() + +macro(list_append_ifndef feature_toggle list) + if(NOT ${feature_toggle}) + list(APPEND ${list} ${ARGN}) + endif() +endmacro() + +# 3.3. *_option Compiler-compatibility checks +# +# Utility functions for silently omitting compiler flags when the +# compiler lacks support. *_cc_option was ported from KBuild, see +# cc-option in +# https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt + +# Writes 1 to the output variable 'ok' for the language 'lang' if +# the flag is supported, otherwise writes 0. +# +# lang must be C or CXX +# +# TODO: Support ASM +# +# Usage: +# +# check_compiler_flag(C "-Wall" my_check) +# print(my_check) # my_check is now 1 +function(check_compiler_flag lang option ok) + if(NOT DEFINED CMAKE_REQUIRED_QUIET) + set(CMAKE_REQUIRED_QUIET 1) + endif() + + string(MAKE_C_IDENTIFIER + "check${option}_${lang}_${CMAKE_REQUIRED_FLAGS}" + ${ok} + ) + + if(${lang} STREQUAL C) + check_c_compiler_flag("${option}" ${${ok}}) + else() + check_cxx_compiler_flag("${option}" ${${ok}}) + endif() + + if(${${${ok}}}) + set(ret 1) + else() + set(ret 0) + endif() + + set(${ok} ${ret} PARENT_SCOPE) +endfunction() + +function(target_cc_option target scope option) + target_cc_option_fallback(${target} ${scope} ${option} "") +endfunction() + +# Support an optional second option for when the first option is not +# supported. +function(target_cc_option_fallback target scope option1 option2) + if(CONFIG_CPP) + foreach(lang C CXX) + # For now, we assume that all flags that apply to C/CXX also + # apply to ASM. + zephyr_check_compiler_flag(${lang} ${option1} check) + if(${check}) + target_compile_options(${target} ${scope} + $<$:${option1}> + $<$:${option1}> + ) + elseif(option2) + target_compile_options(${target} ${scope} + $<$:${option2}> + $<$:${option2}> + ) + endif() + endforeach() + else() + zephyr_check_compiler_flag(C ${option1} check) + if(${check}) + target_compile_options(${target} ${scope} ${option1}) + elseif(option2) + target_compile_options(${target} ${scope} ${option2}) + endif() + endif() +endfunction() + +function(target_ld_options target scope) + zephyr_get_parse_args(args ${ARGN}) + list(REMOVE_ITEM ARGN NO_SPLIT) + + foreach(option ${ARGN}) + if(args_NO_SPLIT) + set(option ${ARGN}) + endif() + string(JOIN "" check_identifier "check" ${option}) + string(MAKE_C_IDENTIFIER ${check_identifier} check) + + set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(JOIN " " CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} ${option}) + zephyr_check_compiler_flag(C "" ${check}) + set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS}) + + target_link_libraries_ifdef(${check} ${target} ${scope} ${option}) + + if(args_NO_SPLIT) + break() + endif() + endforeach() +endfunction() + +# 3.3.1 Toolchain integration +# +# 'toolchain_parse_make_rule' is a function that parses the output of +# 'gcc -M'. +# +# The argument 'input_file' is in input parameter with the path to the +# file with the dependency information. +# +# The argument 'include_files' is an output parameter with the result +# of parsing the include files. +function(toolchain_parse_make_rule input_file include_files) + file(STRINGS ${input_file} input) + + # The file is formatted like this: + # empty_file.o: misc/empty_file.c \ + # nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts \ + # nrf52840_qiaa.dtsi + + # The dep file will contain `\` for line continuation. + # This results in `\;` which is then treated a the char `;` instead of + # the element separator, so let's get the pure `;` back. + string(REPLACE "\;" ";" input_as_list ${input}) + + # Pop the first line and treat it specially + list(POP_FRONT input_as_list first_input_line) + string(FIND ${first_input_line} ": " index) + math(EXPR j "${index} + 2") + string(SUBSTRING ${first_input_line} ${j} -1 first_include_file) + + # Remove whitespace before and after filename and convert to CMake path. + string(STRIP "${first_include_file}" first_include_file) + file(TO_CMAKE_PATH "${first_include_file}" first_include_file) + set(result "${first_include_file}") + + # Remove whitespace before and after filename and convert to CMake path. + foreach(file ${input_as_list}) + string(STRIP "${file}" file) + file(TO_CMAKE_PATH "${file}" file) + list(APPEND result "${file}") + endforeach() + + set(${include_files} ${result} PARENT_SCOPE) +endfunction() + +# 'check_set_linker_property' is a function that check the provided linker +# flag and only set the linker property if the check succeeds +# +# This function is similar in nature to the CMake set_property function, but +# with the extension that it will check that the linker supports the flag before +# setting the property. +# +# APPEND: Flag indicated that the property should be appended to the existing +# value list for the property. +# TARGET: Name of target on which to add the property (commonly: linker) +# PROPERTY: Name of property with the value(s) following immediately after +# property name +function(check_set_linker_property) + set(options APPEND) + set(single_args TARGET) + set(multi_args PROPERTY) + cmake_parse_arguments(LINKER_PROPERTY "${options}" "${single_args}" "${multi_args}" ${ARGN}) + + if(LINKER_PROPERTY_APPEND) + set(APPEND "APPEND") + endif() + + list(GET LINKER_PROPERTY_PROPERTY 0 property) + list(REMOVE_AT LINKER_PROPERTY_PROPERTY 0) + set(option ${LINKER_PROPERTY_PROPERTY}) + + string(MAKE_C_IDENTIFIER check${option} check) + + set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${option}") + zephyr_check_compiler_flag(C "" ${check}) + set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS}) + + if(${${check}}) + set_property(TARGET ${LINKER_PROPERTY_TARGET} ${APPEND} PROPERTY ${property} ${option}) + endif() +endfunction() + +# 'set_compiler_property' is a function that sets the property for the C and +# C++ property targets used for toolchain abstraction. +# +# This function is similar in nature to the CMake set_property function, but +# with the extension that it will set the property on both the compile and +# compiler-cpp targets. +# +# APPEND: Flag indicated that the property should be appended to the existing +# value list for the property. +# PROPERTY: Name of property with the value(s) following immediately after +# property name +function(set_compiler_property) + set(options APPEND) + set(multi_args PROPERTY) + cmake_parse_arguments(COMPILER_PROPERTY "${options}" "${single_args}" "${multi_args}" ${ARGN}) + if(COMPILER_PROPERTY_APPEND) + set(APPEND "APPEND") + set(APPEND-CPP "APPEND") + endif() + + set_property(TARGET compiler ${APPEND} PROPERTY ${COMPILER_PROPERTY_PROPERTY}) + set_property(TARGET compiler-cpp ${APPEND} PROPERTY ${COMPILER_PROPERTY_PROPERTY}) +endfunction() + +# 'check_set_compiler_property' is a function that check the provided compiler +# flag and only set the compiler or compiler-cpp property if the check succeeds +# +# This function is similar in nature to the CMake set_property function, but +# with the extension that it will check that the compiler supports the flag +# before setting the property on compiler or compiler-cpp targets. +# +# To test flags together, such as '-Wformat -Wformat-security', an option group +# can be specified by using shell-like quoting along with a 'SHELL:' prefix. +# The 'SHELL:' prefix will be dropped before testing, so that +# '"SHELL:-Wformat -Wformat-security"' becomes '-Wformat -Wformat-security' for +# testing. +# +# APPEND: Flag indicated that the property should be appended to the existing +# value list for the property. +# PROPERTY: Name of property with the value(s) following immediately after +# property name +function(check_set_compiler_property) + set(options APPEND) + set(multi_args PROPERTY) + cmake_parse_arguments(COMPILER_PROPERTY "${options}" "${single_args}" "${multi_args}" ${ARGN}) + if(COMPILER_PROPERTY_APPEND) + set(APPEND "APPEND") + set(APPEND-CPP "APPEND") + endif() + + list(GET COMPILER_PROPERTY_PROPERTY 0 property) + list(REMOVE_AT COMPILER_PROPERTY_PROPERTY 0) + + foreach(option ${COMPILER_PROPERTY_PROPERTY}) + if(${option} MATCHES "^SHELL:") + string(REGEX REPLACE "^SHELL:" "" option ${option}) + separate_arguments(option UNIX_COMMAND ${option}) + endif() + + if(CONFIG_CPP) + zephyr_check_compiler_flag(CXX "${option}" check) + + if(${check}) + set_property(TARGET compiler-cpp ${APPEND-CPP} PROPERTY ${property} ${option}) + set(APPEND-CPP "APPEND") + endif() + endif() + + zephyr_check_compiler_flag(C "${option}" check) + + if(${check}) + set_property(TARGET compiler ${APPEND} PROPERTY ${property} ${option}) + set(APPEND "APPEND") + endif() + endforeach() +endfunction() + + +# 3.4. Debugging CMake + +# Usage: +# print(BOARD) +# +# will print: "BOARD: nrf52dk" +function(print arg) + message(STATUS "${arg}: ${${arg}}") +endfunction() + +# Usage: +# assert(ZEPHYR_TOOLCHAIN_VARIANT "ZEPHYR_TOOLCHAIN_VARIANT not set.") +# +# will cause a FATAL_ERROR and print an error message if the first +# expression is false +macro(assert test comment) + if(NOT ${test}) + message(FATAL_ERROR "Assertion failed: ${comment}") + endif() +endmacro() + +# Usage: +# assert_not(OBSOLETE_VAR "OBSOLETE_VAR has been removed; use NEW_VAR instead") +# +# will cause a FATAL_ERROR and print an error message if the first +# expression is true +macro(assert_not test comment) + if(${test}) + message(FATAL_ERROR "Assertion failed: ${comment}") + endif() +endmacro() + +# Usage: +# assert_exists(CMAKE_READELF) +# +# will cause a FATAL_ERROR if there is no file or directory behind the +# variable +macro(assert_exists var) + if(NOT EXISTS ${${var}}) + message(FATAL_ERROR "No such file or directory: ${var}: '${${var}}'") + endif() +endmacro() + +# 3.5. File system management +function(generate_unique_target_name_from_filename filename target_name) + get_filename_component(basename ${filename} NAME) + string(REPLACE "." "_" x ${basename}) + string(REPLACE "@" "_" x ${x}) + + string(MD5 unique_chars ${filename}) + + set(${target_name} gen_${x}_${unique_chars} PARENT_SCOPE) +endfunction() + +# Usage: +# zephyr_file( ...) +# +# Zephyr file function extension. +# This function currently supports the following +# +# APPLICATION_ROOT : Check all paths in provided variable, and convert +# those paths that are defined with `-D=` +# to absolute path, relative from `APPLICATION_SOURCE_DIR` +# Issue an error for any relative path not specified +# by user with `-D` +# +# returns an updated list of absolute paths +# +# Usage: +# zephyr_file(CONF_FILES [DTS ] [KCONF ] +# [BOARD [BOARD_REVISION ] | NAMES ...] +# [BUILD ] [SUFFIX ] [REQUIRED] +# ) +# +# CONF_FILES : Find all configuration files in the list of paths and +# return them in a list. If paths is empty then no configuration +# files are returned. Configuration files will be: +# - DTS: Overlay files (.overlay) +# - Kconfig: Config fragments (.conf) +# - defconfig: defconfig files (_defconfig) +# The conf file search will return existing configuration +# files for the current board. +# CONF_FILES takes the following additional arguments: +# BOARD : Find configuration files for specified board. +# BOARD_REVISION : Find configuration files for specified board +# revision. Requires BOARD to be specified. +# +# If no board is given the current BOARD and +# BOARD_REVISION will be used, unless NAMES are +# specified. +# +# NAMES [name2] ... List of file names to look for and instead of +# creating file names based on board settings. +# Only the first match found in will be +# returned in the +# DTS : List to append DTS overlay files in to +# KCONF : List to append Kconfig fragment files in to +# DEFCONF : List to append _defconfig files in to +# BUILD : Build type to include for search. +# For example: +# BUILD debug, will look for _debug.conf +# and _debug.overlay, instead of .conf +# SUFFIX : Suffix name to check for instead of the default name +# but with a fallback to the default name if not found. +# For example: +# SUFFIX fish, will look for _fish.conf and use +# if found but will use .conf if not found +# REQUIRED: Option to indicate that the specified by DTS or KCONF +# must contain at least one element, else an error will be raised. +# +function(zephyr_file) + set(file_options APPLICATION_ROOT CONF_FILES) + if((ARGC EQUAL 0) OR (NOT (ARGV0 IN_LIST file_options))) + message(FATAL_ERROR "No given to `zephyr_file( ...)` function,\n \ +Please provide one of following: APPLICATION_ROOT, CONF_FILES") + endif() + + if(${ARGV0} STREQUAL APPLICATION_ROOT) + set(single_args APPLICATION_ROOT) + elseif(${ARGV0} STREQUAL CONF_FILES) + set(options REQUIRED) + set(single_args BOARD BOARD_REVISION BOARD_QUALIFIERS DTS KCONF DEFCONFIG BUILD SUFFIX) + set(multi_args CONF_FILES NAMES) + endif() + + cmake_parse_arguments(ZFILE "${options}" "${single_args}" "${multi_args}" ${ARGN}) + if(ZFILE_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "zephyr_file(${ARGV0} ...) given unknown arguments: ${ZFILE_UNPARSED_ARGUMENTS}") + endif() + + if(ZFILE_APPLICATION_ROOT) + # Note: user can do: `-D=` and app can at same + # time specify `list(APPEND )` + # Thus need to check and update only CACHED variables (-D). + set(CACHED_PATH $CACHE{${ZFILE_APPLICATION_ROOT}}) + foreach(path ${CACHED_PATH}) + # The cached variable is relative path, i.e. provided by `-D` or + # `set( CACHE)`, so let's update current scope variable to absolute + # path from `APPLICATION_SOURCE_DIR`. + if(NOT IS_ABSOLUTE ${path}) + set(abs_path ${APPLICATION_SOURCE_DIR}/${path}) + list(FIND ${ZFILE_APPLICATION_ROOT} ${path} index) + if(NOT ${index} LESS 0) + list(REMOVE_AT ${ZFILE_APPLICATION_ROOT} ${index}) + list(INSERT ${ZFILE_APPLICATION_ROOT} ${index} ${abs_path}) + endif() + endif() + endforeach() + + # Now all cached relative paths has been updated. + # Let's check if anyone uses relative path as scoped variable, and fail + foreach(path ${${ZFILE_APPLICATION_ROOT}}) + if(NOT IS_ABSOLUTE ${path}) + message(FATAL_ERROR +"Relative path encountered in scoped variable: ${ZFILE_APPLICATION_ROOT}, value=${path}\n \ +Please adjust any `set(${ZFILE_APPLICATION_ROOT} ${path})` or `list(APPEND ${ZFILE_APPLICATION_ROOT} ${path})`\n \ +to absolute path using `\${CMAKE_CURRENT_SOURCE_DIR}/${path}` or similar. \n \ +Relative paths are only allowed with `-D${ARGV1}=`") + endif() + endforeach() + + # This updates the provided argument in parent scope (callers scope) + set(${ZFILE_APPLICATION_ROOT} ${${ZFILE_APPLICATION_ROOT}} PARENT_SCOPE) + endif() + + if(ZFILE_CONF_FILES) + if(DEFINED ZFILE_BOARD_REVISION AND NOT ZFILE_BOARD) + message(FATAL_ERROR + "zephyr_file(${ARGV0} BOARD_REVISION ${ZFILE_BOARD_REVISION} ...)" + " given without BOARD argument, please specify BOARD" + ) + endif() + + if(NOT DEFINED ZFILE_BOARD) + # Defaulting to system wide settings when BOARD is not given as argument + set(ZFILE_BOARD ${BOARD}) + if(DEFINED BOARD_REVISION) + set(ZFILE_BOARD_REVISION ${BOARD_REVISION}) + endif() + + if(DEFINED BOARD_QUALIFIERS) + set(ZFILE_BOARD_QUALIFIERS ${BOARD_QUALIFIERS}) + endif() + endif() + + if(ZFILE_NAMES) + set(dts_filename_list ${ZFILE_NAMES}) + set(kconf_filename_list ${ZFILE_NAMES}) + else() + zephyr_build_string(filename_list + SHORT shortened_filename_list + BOARD ${ZFILE_BOARD} + BOARD_REVISION ${ZFILE_BOARD_REVISION} + BOARD_QUALIFIERS ${ZFILE_BOARD_QUALIFIERS} + BUILD ${ZFILE_BUILD} + MERGE REVERSE + ) + + set(dts_filename_list ${filename_list}) + set(dts_shortened_filename_list ${shortened_filename_list}) + list(TRANSFORM dts_filename_list APPEND ".overlay") + list(TRANSFORM dts_shortened_filename_list APPEND ".overlay") + + set(kconf_filename_list ${filename_list}) + set(kconf_shortened_filename_list ${shortened_filename_list}) + list(TRANSFORM kconf_filename_list APPEND ".conf") + list(TRANSFORM kconf_shortened_filename_list APPEND ".conf") + endif() + + if(ZFILE_DTS) + foreach(path ${ZFILE_CONF_FILES}) + foreach(filename IN ZIP_LISTS dts_filename_list dts_shortened_filename_list) + foreach(i RANGE 1) + if(NOT IS_ABSOLUTE filename_${i} AND DEFINED filename_${i}) + set(test_file_${i} ${path}/${filename_${i}}) + else() + set(test_file_${i} ${filename_${i}}) + endif() + zephyr_file_suffix(test_file_${i} SUFFIX ${ZFILE_SUFFIX}) + + if(NOT EXISTS ${test_file_${i}}) + set(test_file_${i}) + endif() + endforeach() + + if(test_file_0 OR test_file_1) + list(APPEND found_dts_files ${test_file_0}) + list(APPEND found_dts_files ${test_file_1}) + + if(DEFINED ZFILE_BUILD) + set(deprecated_file_found y) + endif() + + if(ZFILE_NAMES) + break() + endif() + endif() + + if(test_file_1 AND NOT BOARD_${ZFILE_BOARD}_SINGLE_SOC) + message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name " + "(${filename_1}) not allowed, use '_.overlay' naming" + ) + endif() + + if(test_file_0 AND test_file_1) + message(FATAL_ERROR "Conflicting file names discovered. Cannot use both ${filename_0} " + "and ${filename_1}. Please choose one naming style, " + "${filename_0} is recommended." + ) + endif() + endforeach() + endforeach() + + list(APPEND ${ZFILE_DTS} ${found_dts_files}) + + # This updates the provided list in parent scope (callers scope) + set(${ZFILE_DTS} ${${ZFILE_DTS}} PARENT_SCOPE) + endif() + + if(ZFILE_KCONF) + set(found_conf_files) + foreach(path ${ZFILE_CONF_FILES}) + foreach(filename IN ZIP_LISTS kconf_filename_list kconf_shortened_filename_list) + foreach(i RANGE 1) + if(NOT IS_ABSOLUTE filename_${i} AND DEFINED filename_${i}) + set(test_file_${i} ${path}/${filename_${i}}) + else() + set(test_file_${i} ${filename_${i}}) + endif() + zephyr_file_suffix(test_file_${i} SUFFIX ${ZFILE_SUFFIX}) + + if(NOT EXISTS ${test_file_${i}}) + set(test_file_${i}) + endif() + endforeach() + + if(test_file_0 OR test_file_1) + list(APPEND found_conf_files ${test_file_0}) + list(APPEND found_conf_files ${test_file_1}) + + if(DEFINED ZFILE_BUILD) + set(deprecated_file_found y) + endif() + + if(ZFILE_NAMES) + break() + endif() + endif() + + if(test_file_1 AND NOT BOARD_${ZFILE_BOARD}_SINGLE_SOC) + message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name " + "(${filename_1}) not allowed, use '_.conf' naming" + ) + endif() + + if(test_file_0 AND test_file_1) + message(FATAL_ERROR "Conflicting file names discovered. Cannot use both ${filename_0} " + "and ${filename_1}. Please choose one naming style, " + "${filename_0} is recommended." + ) + endif() + endforeach() + endforeach() + + list(APPEND ${ZFILE_KCONF} ${found_conf_files}) + + # This updates the provided list in parent scope (callers scope) + set(${ZFILE_KCONF} ${${ZFILE_KCONF}} PARENT_SCOPE) + + if(NOT ${ZFILE_KCONF}) + set(not_found ${kconf_filename_list}) + endif() + endif() + + if(ZFILE_REQUIRED AND DEFINED not_found) + message(FATAL_ERROR + "No ${not_found} file(s) was found in the ${ZFILE_CONF_FILES} folder(s), " + "please read the Zephyr documentation on application development." + ) + endif() + + if(deprecated_file_found) + message(DEPRECATION "prj_.conf was deprecated after Zephyr 3.5," + " you should switch to using -DFILE_SUFFIX instead") + endif() + + if(ZFILE_DEFCONFIG) + set(found_defconf_files) + foreach(path ${ZFILE_CONF_FILES}) + foreach(filename IN ZIP_LISTS filename_list shortened_filename_list) + foreach(i RANGE 1) + set(test_file_${i} ${path}/${filename_${i}}_defconfig) + + if(EXISTS ${test_file_${i}}) + list(APPEND found_defconf_files ${test_file_${i}}) + else() + set(test_file_${i}) + endif() + endforeach() + + if(test_file_1 AND NOT BOARD_${ZFILE_BOARD}_SINGLE_SOC) + message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name " + "(${filename_1}_defconfig) not allowed, use '__defconfig' naming" + ) + endif() + + if(test_file_0 AND test_file_1) + message(FATAL_ERROR "Conflicting file names discovered. Cannot use both " + "${filename_0}_defconfig and ${filename_1}_defconfig. Please choose one " + "naming style, ${filename_0}_defconfig is recommended." + ) + endif() + endforeach() + endforeach() + list(APPEND ${ZFILE_DEFCONFIG} ${found_defconf_files}) + + # This updates the provided list in parent scope (callers scope) + set(${ZFILE_DEFCONFIG} ${${ZFILE_DEFCONFIG}} PARENT_SCOPE) + endif() + endif() +endfunction() + +# Usage: +# zephyr_file_copy( [ONLY_IF_DIFFERENT]) +# +# Zephyr file copy extension. +# This function is similar to CMake function +# 'file(COPY_FILE [ONLY_IF_DIFFERENT])' +# introduced with CMake 3.21. +# +# Because the minimal required CMake version with Zephyr is 3.20, this function +# is not guaranteed to be available. +# +# When using CMake version 3.21 or newer 'zephyr_file_copy()' simply calls +# 'file(COPY_FILE...)' directly. +# When using CMake version 3.20, the implementation will execute using CMake +# for running command line tool in a subprocess for identical functionality. +function(zephyr_file_copy oldname newname) + set(options ONLY_IF_DIFFERENT) + cmake_parse_arguments(ZEPHYR_FILE_COPY "${options}" "" "" ${ARGN}) + + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21.0) + if(ZEPHYR_FILE_COPY_ONLY_IF_DIFFERENT) + set(copy_file_options ONLY_IF_DIFFERENT) + endif() + file(COPY_FILE ${oldname} ${newname} ${copy_file_options}) + else() + if(ZEPHYR_FILE_COPY_ONLY_IF_DIFFERENT) + set(copy_file_command copy_if_different) + else() + set(copy_file_command copy) + endif() + execute_process( + COMMAND ${CMAKE_COMMAND} -E ${copy_file_command} ${oldname} ${newname} + ) + endif() +endfunction() + +# Usage: +# zephyr_file_suffix( SUFFIX ) +# +# Zephyr file add suffix extension. +# This function will check the provied filename or list of filenames to see if they have a +# `_` extension to them and if so, updates the supplied variable/list with the new +# path/paths. +# +# : Variable (singlular or list) of absolute path filename(s) which should be checked +# and updated if there is a filename which has the present. +# : The suffix to test for and append to the end of the provided filename. +# +# Returns an updated variable of absolute path(s) +# +function(zephyr_file_suffix filename) + set(single_args SUFFIX) + cmake_parse_arguments(SFILE "" "${single_args}" "" ${ARGN}) + + if(NOT DEFINED SFILE_SUFFIX OR NOT DEFINED ${filename}) + # If the file suffix variable is not known then there is nothing to do, return early + return() + endif() + + set(tmp_new_list) + + foreach(file ${${filename}}) + if("${file}" STREQUAL "") + # Skip checking empty variables + continue() + endif() + + # Search for the full stop so we know where to add the file suffix before the file extension + cmake_path(GET file EXTENSION file_ext) + cmake_path(REMOVE_EXTENSION file OUTPUT_VARIABLE new_filename) + cmake_path(APPEND_STRING new_filename "_${SFILE_SUFFIX}${file_ext}") + + # Use the filename with the suffix if it exists, if not then fall back to the default + if(EXISTS "${new_filename}") + list(APPEND tmp_new_list ${new_filename}) + else() + list(APPEND tmp_new_list ${file}) + endif() + endforeach() + + # Update supplied variable if it differs + if(NOT "${${filename}}" STREQUAL "${tmp_new_list}") + set(${filename} "${tmp_new_list}" PARENT_SCOPE) + endif() +endfunction() + +# Usage: +# zephyr_string( ...) +# +# Zephyr string function extension. +# This function extends the CMake string function by providing additional +# manipulation arguments to CMake string. +# +# SANITIZE: Ensure that the output string does not contain any special +# characters. Special characters, such as -, +, =, $, etc. are +# converted to underscores '_'. +# +# SANITIZE TOUPPER: Ensure that the output string does not contain any special +# characters. Special characters, such as -, +, =, $, etc. are +# converted to underscores '_'. +# The sanitized string will be returned in UPPER case. +# +# returns the updated string +function(zephyr_string) + set(options SANITIZE TOUPPER) + cmake_parse_arguments(ZEPHYR_STRING "${options}" "" "" ${ARGN}) + + if (NOT ZEPHYR_STRING_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Function zephyr_string() called without a return variable") + endif() + + list(GET ZEPHYR_STRING_UNPARSED_ARGUMENTS 0 return_arg) + list(REMOVE_AT ZEPHYR_STRING_UNPARSED_ARGUMENTS 0) + + list(JOIN ZEPHYR_STRING_UNPARSED_ARGUMENTS "" work_string) + + if(ZEPHYR_STRING_SANITIZE) + string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" work_string ${work_string}) + endif() + + if(ZEPHYR_STRING_TOUPPER) + string(TOUPPER ${work_string} work_string) + endif() + + set(${return_arg} ${work_string} PARENT_SCOPE) +endfunction() + +# Usage: +# zephyr_list(TRANSFORM +# [OUTPUT_VARIABLE : This currently must be NORMALIZE_PATHS. This action +# converts the argument list to a ;-list with +# CMake path names, after passing its contents through +# a configure_file() transformation. The input list +# may be whitespace- or semicolon-separated. +# +# OUTPUT_VARIABLE: the result is normally stored in place, but +# an alternative variable to store the result +# can be provided with this. +function(zephyr_list transform list_var action) + # Parse arguments. + if(NOT "${transform}" STREQUAL "TRANSFORM") + message(FATAL_ERROR "the first argument must be TRANSFORM") + endif() + if(NOT "${action}" STREQUAL "NORMALIZE_PATHS") + message(FATAL_ERROR "the third argument must be NORMALIZE_PATHS") + endif() + set(single_args OUTPUT_VARIABLE) + cmake_parse_arguments(ZEPHYR_LIST "" "${single_args}" "" ${ARGN}) + if(DEFINED ZEPHYR_LIST_OUTPUT_VARIABLE) + set(out_var ${ZEPHYR_LIST_OUTPUT_VARIABLE}) + else() + set(out_var ${list_var}) + endif() + set(input ${${list_var}}) + + # Perform the transformation. + set(ret) + string(CONFIGURE "${input}" input_expanded) + string(REPLACE " " ";" input_raw_list "${input_expanded}") + foreach(file ${input_raw_list}) + file(TO_CMAKE_PATH "${file}" cmake_path_file) + list(APPEND ret ${cmake_path_file}) + endforeach() + + set(${out_var} ${ret} PARENT_SCOPE) +endfunction() + +# Usage: +# zephyr_var_name( ) +# +# Internal function for construction of scoped variable name expansion string. +# Examples: +# reading a current scope FOO variable is identical to expand ${FOO}. +# reading a cache scope FOO variable is identical to expand $CACHE{FOO}. +# +# this functions will return the var name in out var for the scope if it is +# defined, else it will set the outvar to undefined. +function(zephyr_var_name variable scope out) + if(scope STREQUAL "ENV" OR scope STREQUAL "CACHE") + if(DEFINED ${scope}{${variable}}) + set(${out} "$${scope}{${variable}}" PARENT_SCOPE) + else() + set(${out} PARENT_SCOPE) + endif() + else() + if(DEFINED ${scope}_${variable}) + set(${out} "${${scope}_${variable}}" PARENT_SCOPE) + else() + set(${out} PARENT_SCOPE) + endif() + endif() +endfunction() + +# Usage: +# zephyr_get( [MERGE [REVERSE]] [SYSBUILD [LOCAL|GLOBAL]] [VAR ...]) +# +# Return the value of as local scoped variable of same name. If MERGE +# is supplied, will return a list of found items. If REVERSE is supplied +# together with MERGE, the order of the list will be reversed before being +# returned. Reverse will happen before the list is returned and hence it will +# not change the order of precedence in which the list itself is constructed. +# +# VAR can be used either to store the result in a variable with a different +# name, or to look for values from multiple variables. +# zephyr_get(FOO VAR FOO_A FOO_B) +# zephyr_get(FOO MERGE VAR FOO_A FOO_B) +# +# zephyr_get() is a common function to provide a uniform way of supporting +# build settings that can be set from sysbuild, CMakeLists.txt, CMake cache, or +# in environment. +# +# The order of precedence for variables defined in multiple scopes: +# - Sysbuild defined when sysbuild is used. +# Sysbuild variables can be defined as global or local to specific image. +# Examples: +# - BOARD is considered a global sysbuild cache variable +# - blinky_BOARD is considered a local sysbuild cache variable only for the +# blinky image. +# If no sysbuild scope is specified, GLOBAL is assumed. +# If using MERGE then SYSBUILD GLOBAL will get both the local and global +# sysbuild scope variables (in that order, if both exist). +# - CMake cache, set by `-D=` or `set( CACHE ...) +# - Environment +# - Locally in CMakeLists.txt before 'find_package(Zephyr)' +# +# For example, if ZEPHYR_TOOLCHAIN_VARIANT is set in environment but locally +# overridden by setting ZEPHYR_TOOLCHAIN_VARIANT directly in the CMake cache +# using `-DZEPHYR_TOOLCHAIN_VARIANT=`, then the value from the cache is +# returned. +function(zephyr_get variable) + cmake_parse_arguments(GET_VAR "MERGE;REVERSE" "SYSBUILD" "VAR" ${ARGN}) + + if(DEFINED GET_VAR_SYSBUILD) + if(NOT ("${GET_VAR_SYSBUILD}" STREQUAL "GLOBAL" OR + "${GET_VAR_SYSBUILD}" STREQUAL "LOCAL") + ) + message(FATAL_ERROR "zephyr_get(... SYSBUILD) requires GLOBAL or LOCAL.") + endif() + else() + set(GET_VAR_SYSBUILD "GLOBAL") + endif() + + if(GET_VAR_REVERSE AND NOT GET_VAR_MERGE) + message(FATAL_ERROR "zephyr_get(... REVERSE) missing a required argument: MERGE") + endif() + + if(NOT DEFINED GET_VAR_VAR) + set(GET_VAR_VAR ${variable}) + endif() + + # Keep current scope variables in internal variables. + # This is needed to properly handle cases where we want to check value against + # environment value or when appending with the MERGE operation. + foreach(var ${GET_VAR_VAR}) + set(current_${var} ${${var}}) + set(${var}) + + if(SYSBUILD) + get_property(sysbuild_name TARGET sysbuild_cache PROPERTY SYSBUILD_NAME) + get_property(sysbuild_main_app TARGET sysbuild_cache PROPERTY SYSBUILD_MAIN_APP) + get_property(sysbuild_local_${var} TARGET sysbuild_cache PROPERTY ${sysbuild_name}_${var}) + get_property(sysbuild_global_${var} TARGET sysbuild_cache PROPERTY ${var}) + if(NOT DEFINED sysbuild_local_${var} AND sysbuild_main_app) + set(sysbuild_local_${var} ${sysbuild_global_${var}}) + endif() + if(NOT "${GET_VAR_SYSBUILD}" STREQUAL "GLOBAL") + set(sysbuild_global_${var}) + endif() + else() + set(sysbuild_local_${var}) + set(sysbuild_global_${var}) + endif() + + if(TARGET snippets_scope) + get_property(snippets_${var} TARGET snippets_scope PROPERTY ${var}) + endif() + endforeach() + + set(${variable} "") + set(scopes "sysbuild_local;sysbuild_global;CACHE;snippets;ENV;current") + if(GET_VAR_REVERSE) + list(REVERSE scopes) + endif() + foreach(scope IN LISTS scopes) + foreach(var ${GET_VAR_VAR}) + zephyr_var_name("${var}" "${scope}" expansion_var) + if(DEFINED expansion_var) + string(CONFIGURE "${expansion_var}" scope_value) + if(GET_VAR_MERGE) + list(APPEND ${variable} ${scope_value}) + else() + set(${variable} ${scope_value} PARENT_SCOPE) + + if("${scope}" STREQUAL "ENV") + # Set the environment variable in CMake cache, so that a build + # invocation triggering a CMake rerun doesn't rely on the + # environment variable still being available / have identical value. + set(${var} $ENV{${var}} CACHE INTERNAL "Cached environment variable ${var}") + endif() + + if("${scope}" STREQUAL "ENV" AND DEFINED current_${var} + AND NOT "${current_${var}}" STREQUAL "$ENV{${var}}" + ) + # Variable exists as current scoped variable, defined in a CMakeLists.txt + # file, however it is also set in environment. + # This might be a surprise to the user, so warn about it. + message(WARNING "environment variable '${var}' is hiding local " + "variable of same name.\n" + "Environment value (in use): $ENV{${var}}\n" + "Current scope value (hidden): ${current_${var}}\n" + ) + endif() + + return() + endif() + endif() + endforeach() + endforeach() + + if(GET_VAR_MERGE) + if(GET_VAR_REVERSE) + list(REVERSE ${variable}) + list(REMOVE_DUPLICATES ${variable}) + list(REVERSE ${variable}) + else() + list(REMOVE_DUPLICATES ${variable}) + endif() + set(${variable} ${${variable}} PARENT_SCOPE) + endif() +endfunction(zephyr_get variable) + +# Usage: +# zephyr_create_scope() +# +# Create a new scope for creation of scoped variables. +# +# : Name of new scope. +# +function(zephyr_create_scope scope) + if(TARGET ${scope}_scope) + message(FATAL_ERROR "zephyr_create_scope(${scope}) already exists.") + endif() + + add_custom_target(${scope}_scope) +endfunction() + +# Usage: +# zephyr_set( SCOPE [APPEND]) +# +# Zephyr extension of CMake set which allows a variable to be set in a specific +# scope. The scope is used on later zephyr_get() invocation for precedence +# handling when a variable it set in multiple scopes. +# +# : Name of variable +# : Value of variable, multiple values will create a list. +# The SCOPE argument identifies the end of value list. +# SCOPE : Name of scope for the variable +# APPEND : Append values to the already existing variable in +# +function(zephyr_set variable) + cmake_parse_arguments(SET_VAR "APPEND" "SCOPE" "" ${ARGN}) + + zephyr_check_arguments_required_all(zephyr_set SET_VAR SCOPE) + + if(NOT TARGET ${SET_VAR_SCOPE}_scope) + message(FATAL_ERROR "zephyr_set(... SCOPE ${SET_VAR_SCOPE}) doesn't exists.") + endif() + + if(SET_VAR_APPEND) + set(property_args APPEND) + endif() + + set_property(TARGET ${SET_VAR_SCOPE}_scope ${property_args} + PROPERTY ${variable} ${SET_VAR_UNPARSED_ARGUMENTS} + ) +endfunction() + +# Usage: +# zephyr_check_cache( [REQUIRED]) +# +# Check the current CMake cache for and warn the user if the value +# is being modified. +# +# This can be used to ensure the user does not accidentally try to change +# Zephyr build variables, such as: +# - BOARD +# - SHIELD +# +# variable: Name of to check and set, for example BOARD. +# REQUIRED: Optional flag. If specified, then an unset will be +# treated as an error. +# WATCH: Optional flag. If specified, watch the variable and print a warning if +# the variable is later being changed. +# +# Details: +# can be set by 3 sources. +# - Using CMake argument, -D +# - Using an environment variable +# - In the project CMakeLists.txt before `find_package(Zephyr)`. +# +# CLI has the highest precedence, then comes environment variables, +# and then finally CMakeLists.txt. +# +# The value defined on the first CMake invocation will be stored in the CMake +# cache as CACHED_. This allows the Zephyr build system to detect +# when a user reconfigures a sticky variable. +# +# A user can ignore all the precedence rules if the same source is always used +# E.g. always specifies -D= on the command line, +# always has an environment set, or always has a set( foo) +# line in his CMakeLists.txt and avoids mixing sources. +# +# The selected can be accessed through the variable '' in +# following Zephyr CMake code. +# +# If the user tries to change to a new value, then a warning will +# be printed, and the previously cached value (CACHED_) will be +# used, as it has precedence. +# +# Together with the warning, user is informed that in order to change +# the build directory must be cleaned. +# +function(zephyr_check_cache variable) + cmake_parse_arguments(CACHE_VAR "REQUIRED;WATCH" "" "" ${ARGN}) + string(TOLOWER ${variable} variable_text) + string(REPLACE "_" " " variable_text ${variable_text}) + + get_property(cached_value CACHE ${variable} PROPERTY VALUE) + + # If the build has already been configured in an earlier CMake invocation, + # then CACHED_${variable} is set. The CACHED_${variable} setting takes + # precedence over any user or CMakeLists.txt input. + # If we detect that user tries to change the setting, then print a warning + # that a pristine build is needed. + + # If user uses -D=, then cli_argument will hold the new + # value, otherwise cli_argument will hold the existing (old) value. + set(cli_argument ${cached_value}) + if(cli_argument STREQUAL CACHED_${variable}) + # The is no changes to the value. + unset(cli_argument) + endif() + + set(app_cmake_lists ${${variable}}) + if(cached_value STREQUAL ${variable}) + # The app build scripts did not set a default, The variable we are + # reading is the cached value from the CLI + unset(app_cmake_lists) + endif() + + if(DEFINED CACHED_${variable}) + # Warn the user if it looks like he is trying to change the variable + # without cleaning first + if(cli_argument) + if(NOT ((CACHED_${variable} STREQUAL cli_argument) OR (${variable}_DEPRECATED STREQUAL cli_argument))) + message(WARNING "The build directory must be cleaned pristinely when " + "changing ${variable_text},\n" + "Current value=\"${CACHED_${variable}}\", " + "Ignored value=\"${cli_argument}\"") + endif() + endif() + + if(CACHED_${variable}) + set(${variable} ${CACHED_${variable}} PARENT_SCOPE) + set(${variable} ${CACHED_${variable}}) + # This resets the user provided value with previous (working) value. + set(${variable} ${CACHED_${variable}} CACHE STRING "Selected ${variable_text}" FORCE) + else() + unset(${variable} PARENT_SCOPE) + unset(${variable} CACHE) + endif() + else() + zephyr_get(${variable}) + endif() + + if(${CACHE_VAR_REQUIRED} AND NOT DEFINED ${variable}) + message(FATAL_ERROR "${variable} is not being defined on the CMake command-line," + " in the environment or by the app." + ) + endif() + + if(DEFINED ${variable}) + # Store the specified variable in parent scope and the cache + set(${variable} ${${variable}} PARENT_SCOPE) + set(${variable} ${${variable}} CACHE STRING "Selected ${variable_text}") + endif() + set(CACHED_${variable} ${${variable}} CACHE STRING "Selected ${variable_text}") + + if(CACHE_VAR_WATCH) + # The variable is now set to its final value. + zephyr_boilerplate_watch(${variable}) + endif() +endfunction(zephyr_check_cache variable) + + +# Usage: +# zephyr_boilerplate_watch(SOME_BOILERPLATE_VAR) +# +# Inform the build system that SOME_BOILERPLATE_VAR, a variable +# handled in the Zephyr package's boilerplate code, is now fixed and +# should no longer be changed. +# +# This function uses variable_watch() to print a noisy warning +# if the variable is set after it returns. +function(zephyr_boilerplate_watch variable) + variable_watch(${variable} zephyr_variable_set_too_late) +endfunction() + +function(zephyr_variable_set_too_late variable access value current_list_file) + if (access STREQUAL "MODIFIED_ACCESS") + message(WARNING +" + ********************************************************************** + * + * WARNING + * + * CMake variable ${variable} set to \"${value}\" in: + * ${current_list_file} + * + * This is too late to make changes! The change was ignored. + * + * Hint: ${variable} must be set before calling find_package(Zephyr ...). + * + ********************************************************************** +") + endif() +endfunction() + +# Usage: +# zephyr_get_targets( ) +# +# Get build targets for a given directory and sub-directories. +# +# This functions will traverse the build tree, starting from . +# It will read the `BUILDSYSTEM_TARGETS` for each directory in the build tree +# and return the build types matching the list. +# Example of types: OBJECT_LIBRARY, STATIC_LIBRARY, INTERFACE_LIBRARY, UTILITY. +# +# returns a list of targets in matching the required . +function(zephyr_get_targets directory types targets) + get_property(sub_directories DIRECTORY ${directory} PROPERTY SUBDIRECTORIES) + get_property(dir_targets DIRECTORY ${directory} PROPERTY BUILDSYSTEM_TARGETS) + foreach(dir_target ${dir_targets}) + get_property(target_type TARGET ${dir_target} PROPERTY TYPE) + if(${target_type} IN_LIST types) + list(APPEND ${targets} ${dir_target}) + endif() + endforeach() + + foreach(directory ${sub_directories}) + zephyr_get_targets(${directory} "${types}" ${targets}) + endforeach() + set(${targets} ${${targets}} PARENT_SCOPE) +endfunction() + +# Usage: +# test_sysbuild([REQUIRED]) +# +# Test that current sample is invoked through sysbuild. +# +# This function tests that current CMake configure was invoked through sysbuild. +# If CMake configure was not invoked through sysbuild, then a warning is printed +# to the user. The warning can be upgraded to an error by setting `REQUIRED` as +# argument the `test_sysbuild()`. +# +# This function allows samples that are multi-image samples by nature to ensure +# all samples are correctly built together. +function(test_sysbuild) + cmake_parse_arguments(TEST_SYSBUILD "REQUIRED" "" "" ${ARGN}) + + if(TEST_SYSBUILD_REQUIRED) + set(message_mode FATAL_ERROR) + else() + set(message_mode WARNING) + endif() + + if(NOT SYSBUILD) + message(${message_mode} + "Project '${PROJECT_NAME}' is designed for sysbuild.\n" + "For correct user-experiences, please build '${PROJECT_NAME}' " + "using sysbuild." + ) + endif() +endfunction() + +# Usage: +# target_byproducts(TARGET BYPRODUCTS [...]) +# +# Specify additional BYPRODUCTS that this target produces. +# +# This function allows the build system to specify additional byproducts to +# target created with `add_executable()`. When linking an executable the linker +# may produce additional files, like map files. Those files are not known to the +# build system. This function makes it possible to describe such additional +# byproducts in an easy manner. +function(target_byproducts) + cmake_parse_arguments(TB "" "TARGET" "BYPRODUCTS" ${ARGN}) + + if(NOT DEFINED TB_TARGET) + message(FATAL_ERROR "target_byproducts() missing parameter: TARGET ") + endif() + + add_custom_command(TARGET ${TB_TARGET} + POST_BUILD COMMAND ${CMAKE_COMMAND} -E true + BYPRODUCTS ${TB_BYPRODUCTS} + COMMENT "Logical command for additional byproducts on target: ${TB_TARGET}" + ) +endfunction() + +# Usage: +# topological_sort(TARGETS [ ...] +# PROPERTY_NAME +# RESULT ) +# +# This function performs topological sorting of CMake targets using a specific +# , which dictates target dependencies. A fatal error occurs if the +# provided dependencies cannot be met, e.g., if they contain cycles. +# +# TARGETS: List of target names. +# PROPERTY_NAME: Name of the target property to be used when sorting. For every +# target listed in TARGETS, this property must contain a list +# (possibly empty) of other targets, which this target depends on +# for a particular purpose. The property must not contain any +# target which is not also found in TARGETS. +# RESULT: Output variable, where the topologically sorted list of target +# names will be returned. +# +function(topological_sort) + cmake_parse_arguments(TS "" "RESULT;PROPERTY_NAME" "TARGETS" ${ARGN}) + + set(dep_targets) + set(start_targets) + set(sorted_targets) + + foreach(target ${TS_TARGETS}) + get_target_property(${target}_dependencies ${target} ${TS_PROPERTY_NAME}) + + if(${target}_dependencies) + list(APPEND dep_targets ${target}) + else() + list(APPEND start_targets ${target}) + endif() + endforeach() + + while(TRUE) + list(POP_FRONT start_targets node) + list(APPEND sorted_targets ${node}) + set(to_remove) + foreach(target ${dep_targets}) + if("${node}" IN_LIST ${target}_dependencies) + list(REMOVE_ITEM ${target}_dependencies ${node}) + if(NOT ${target}_dependencies) + list(APPEND start_targets ${target}) + list(APPEND to_remove ${target}) + endif() + endif() + endforeach() + + foreach(target ${to_remove}) + list(REMOVE_ITEM dep_targets ${target}) + endforeach() + if(NOT start_targets) + break() + endif() + endwhile() + + if(dep_targets) + foreach(target ${dep_targets}) + get_target_property(deps ${target} ${TS_PROPERTY_NAME}) + list(JOIN deps " " deps) + list(APPEND dep_string "${target} depends on: ${deps}") + endforeach() + list(JOIN dep_string "\n" dep_string) + message(FATAL_ERROR "Unmet or cyclic dependencies:\n${dep_string}") + endif() + + set(${TS_RESULT} "${sorted_targets}" PARENT_SCOPE) +endfunction() + +######################################################## +# 4. Devicetree extensions +######################################################## +# 4.1. dt_* +# +# The following methods are for retrieving devicetree information in CMake. +# +# Notes: +# +# - In CMake, we refer to the nodes using the node's path, therefore +# there is no dt_path(...) function for obtaining a node identifier +# like there is in the C devicetree.h API. +# +# - As another difference from the C API, you can generally use an +# alias at the beginning of a path interchangeably with the full +# path to the aliased node in these functions. The usage comments +# will make this clear in each case. + +# Usage: +# dt_nodelabel( NODELABEL