From 09af912f9776b4f386d26d77435864ea61fb41d5 Mon Sep 17 00:00:00 2001 From: amgross <33063505+amgross@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:06:28 +0200 Subject: [PATCH 01/26] Add BSD-3 license to endianness agnostic files (#417) Co-authored-by: amgross --- erpc_c/port/erpc_endianness_agnostic_example.h | 4 ++++ erpc_c/port/erpc_endianness_undefined.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/erpc_c/port/erpc_endianness_agnostic_example.h b/erpc_c/port/erpc_endianness_agnostic_example.h index ce4daa895..fe41dafb1 100644 --- a/erpc_c/port/erpc_endianness_agnostic_example.h +++ b/erpc_c/port/erpc_endianness_agnostic_example.h @@ -1,3 +1,7 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + */ + /* * This is example file for endianness agnostic communication based on byteswap.h. * Other approach can be done with htons(), htonl(), ntohs(), ntohl() and such. diff --git a/erpc_c/port/erpc_endianness_undefined.h b/erpc_c/port/erpc_endianness_undefined.h index df35d48a2..505243fb3 100644 --- a/erpc_c/port/erpc_endianness_undefined.h +++ b/erpc_c/port/erpc_endianness_undefined.h @@ -1,3 +1,6 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + */ #ifndef _ERPC_ENDIANNESS_UNDEFINED_H_ #define _ERPC_ENDIANNESS_UNDEFINED_H_ From 4bd2ecd57638fcc639e767d4ac9cfae78f677c5b Mon Sep 17 00:00:00 2001 From: "Michal Princ (nxa17570)" Date: Fri, 25 Oct 2024 16:37:24 +0200 Subject: [PATCH 02/26] [MCUX-71720] Enable erpc middleware repo Signed-off-by: Michal Princ Signed-off-by: Tomas Galbicka --- .gitignore | 1 - CHANGELOG.md | 355 + CMakeLists.txt | 58 + CMakePresets.json | 24 + Kconfig | 177 + README.md | 52 +- SCR.txt | 37 + cmake/erpc_utils.cmake | 57 + cmake/extensions.cmake | 5531 +++++++++++++ cmake/kconfig.cmake | 176 + cmake/python.cmake | 46 + cmake/test.cmake | 150 + doxygen/Doxyfile.erpc | 2 +- doxygen/Doxyfile.erpcgen | 2 +- erpc_c/CMakeLists.txt | 23 + erpc_c/{readme.txt => README.md} | 13 +- erpc_c/config/erpc_config.h | 4 + erpc_c/erpc_c_sources.cmake | 79 + erpc_c/port/erpc_serial.cpp | 2 +- .../transports/erpc_lpspi_slave_transport.cpp | 7 +- erpc_c/transports/erpc_mu_transport.hpp | 7 +- erpc_c/transports/transports.dox | 18 + erpc_java/{readme.md => README.md} | 6 +- erpc_python/{readme.md => README.md} | 0 erpcgen/CMakeLists.txt | 149 + erpcgen/README.md | 13 + erpcgen/readme.txt | 14 - examples/CMakeLists.txt | 13 + examples/MCUXPRESSO_SDK/Kconfig | 40 + .../MCUXPRESSO_SDK/erpc_error_handler.cpp | 106 + examples/MCUXPRESSO_SDK/erpc_error_handler.h | 47 + examples/MCUXPRESSO_SDK/erpc_examples.cmake | 100 + .../service/c_erpc_matrix_multiply_client.cpp | 58 + .../service/c_erpc_matrix_multiply_client.h | 53 + .../service/c_erpc_matrix_multiply_server.cpp | 78 + .../service/c_erpc_matrix_multiply_server.h | 58 + .../service/erpc_matrix_multiply.erpc | 24 + .../service/erpc_matrix_multiply_client.cpp | 126 + .../service/erpc_matrix_multiply_client.hpp | 42 + .../service/erpc_matrix_multiply_common.h | 56 + .../service/erpc_matrix_multiply_common.hpp | 48 + .../erpc_matrix_multiply_interface.cpp | 28 + .../erpc_matrix_multiply_interface.hpp | 40 + .../service/erpc_matrix_multiply_server.cpp | 147 + .../service/erpc_matrix_multiply_server.hpp | 57 + ...erpc_two_way_rpc_Core0Interface_client.cpp | 99 + ...c_erpc_two_way_rpc_Core0Interface_client.h | 67 + ...erpc_two_way_rpc_Core0Interface_server.cpp | 119 + ...c_erpc_two_way_rpc_Core0Interface_server.h | 72 + ...erpc_two_way_rpc_Core1Interface_client.cpp | 57 + ...c_erpc_two_way_rpc_Core1Interface_client.h | 54 + ...erpc_two_way_rpc_Core1Interface_server.cpp | 77 + ...c_erpc_two_way_rpc_Core1Interface_server.h | 59 + .../service/erpc_two_way_rpc.erpc | 37 + ...erpc_two_way_rpc_Core0Interface_client.cpp | 263 + ...erpc_two_way_rpc_Core0Interface_client.hpp | 51 + .../erpc_two_way_rpc_Core0Interface_common.h | 43 + ...erpc_two_way_rpc_Core0Interface_common.hpp | 35 + ...c_two_way_rpc_Core0Interface_interface.cpp | 61 + ...c_two_way_rpc_Core0Interface_interface.hpp | 58 + ...erpc_two_way_rpc_Core0Interface_server.cpp | 242 + ...erpc_two_way_rpc_Core0Interface_server.hpp | 65 + ...erpc_two_way_rpc_Core1Interface_client.cpp | 96 + ...erpc_two_way_rpc_Core1Interface_client.hpp | 42 + .../erpc_two_way_rpc_Core1Interface_common.h | 43 + ...erpc_two_way_rpc_Core1Interface_common.hpp | 35 + ...c_two_way_rpc_Core1Interface_interface.cpp | 27 + ...c_two_way_rpc_Core1Interface_interface.hpp | 40 + ...erpc_two_way_rpc_Core1Interface_server.cpp | 115 + ...erpc_two_way_rpc_Core1Interface_server.hpp | 56 + examples/README.md | 33 +- examples/hello_world/CMakeLists.txt | 131 + examples/hello_world/README.md | 54 + examples/hello_world/c/erpc_config.h | 187 + examples/hello_world/c/erpc_error_handler.cpp | 106 + examples/hello_world/c/erpc_error_handler.h | 47 + examples/hello_world/c/main_client.c | 46 + examples/hello_world/c/main_client.cpp | 92 + examples/hello_world/c/main_server.c | 70 + examples/hello_world/c/main_server.cpp | 116 + examples/hello_world/config.h | 4 + examples/hello_world/hello_world.erpc | 5 + examples/hello_world/java/pom.xml | 63 + .../src/main/java/org/example/Client.java | 25 + .../src/main/java/org/example/Config.java | 52 + .../java/src/main/java/org/example/Main.java | 13 + .../src/main/java/org/example/Server.java | 23 + .../java/org/example/TextServiceService.java | 23 + examples/hello_world/py/config.py | 18 + examples/hello_world/py/main_client.py | 37 + examples/hello_world/py/main_server.py | 57 + examples/{ => idl}/ble/ble_common.erpc | 0 examples/{ => idl}/ble/ble_gap.erpc | 0 examples/{ => idl}/ble/ble_gatt.erpc | 0 examples/{ => idl}/ble/bluetooth.erpc | 0 examples/{ => idl}/smac.erpc | 0 examples/{ => idl}/temp_alarm.erpc | 0 .../{readme.md => README.md} | 0 .../README.md} | 0 .../matrix_multiply.py | 0 .../service/__init__.py | 0 .../service/erpc_matrix_multiply.erpc | 0 .../service/erpc_matrix_multiply/__init__.py | 0 .../service/erpc_matrix_multiply/client.py | 0 .../service/erpc_matrix_multiply/common.py | 0 .../service/erpc_matrix_multiply/interface.py | 0 .../service/erpc_matrix_multiply/server.py | 0 examples/matrix_multiply_tcp_c/CMakeLists.txt | 74 + examples/matrix_multiply_tcp_c/README.md | 22 + examples/matrix_multiply_tcp_c/erpc_config.h | 187 + .../erpc_error_handler.cpp | 106 + .../erpc_error_handler.h | 47 + .../erpc_matrix_multiply.erpc | 22 + examples/matrix_multiply_tcp_c/main_client.c | 114 + .../matrix_multiply_tcp_c/main_client.cpp | 155 + examples/matrix_multiply_tcp_c/main_server.c | 106 + .../matrix_multiply_tcp_c/main_server.cpp | 137 + mcux/CMakeLists.txt | 408 + mcux/Kconfig | 253 + prj.conf | 44 + scripts/kconfig/diffconfig | 134 + scripts/kconfig/guiconfig.py | 2331 ++++++ scripts/kconfig/hardenconfig.py | 80 + scripts/kconfig/hardened.csv | 141 + scripts/kconfig/kconfig.py | 318 + scripts/kconfig/kconfigfunctions.py | 924 +++ scripts/kconfig/kconfiglib.py | 7298 +++++++++++++++++ scripts/kconfig/lint.py | 341 + scripts/kconfig/menuconfig.py | 3284 ++++++++ test/CMakeLists.txt | 47 + test/README.md | 146 + test/common/unit_test_arbitrator_app0.cpp | 9 +- test/common/unit_test_client.cpp | 7 +- test/common/unit_test_serial_client.cpp | 21 +- test/common/unit_test_serial_server.cpp | 21 +- .../unit_test_tcp_arbitrator_client.cpp | 3 +- .../unit_test_tcp_arbitrator_server.cpp | 3 +- test/common/unit_test_tcp_client.cpp | 19 +- test/common/unit_test_tcp_server.cpp | 19 +- test/java_impl_tests/pom.xml | 38 +- test/java_impl_tests/run_tests.py | 2 +- .../client/CommonClient.java | 78 - .../common/Constants.java | 21 - .../interfaces/ICommon.java | 35 - .../server/AbstractCommonService.java | 67 - .../client/CommonClient.java | 77 - .../common/Constants.java | 21 - .../common/enums/enumColor.java | 57 - .../common/enums/myEnum.java | 57 - .../common/enums/numbers.java | 62 - .../interfaces/ICommon.java | 34 - .../server/AbstractCommonService.java | 66 - .../erpc/tests/client/TestAnnotations.java | 4 +- .../erpc/tests/client/TestArrays.java | 44 +- .../erpc/tests/client/TestBinary.java | 6 +- .../erpc/tests/client/TestBuiltin.java | 28 +- .../erpc/tests/client/TestConst.java | 4 +- .../erpc/tests/client/TestEnums.java | 14 +- .../erpc/tests/client/TestLists.java | 32 +- .../erpc/tests/client/TestStruct.java | 26 +- .../erpc/tests/client/TestTypedef.java | 14 +- test/prj.conf | 15 + test/python_impl_tests/README.md | 41 +- .../service/test_binary => }/__init__.py | 0 test/python_impl_tests/conftest.py | 207 + .../scripts/common/common.py | 41 - test/python_impl_tests/scripts/conftest.py | 23 - .../scripts/server/clientTCP.py | 40 - .../scripts/server/clientUART.py | 39 - .../scripts/server/connection.py | 54 - .../scripts/server/runner.py | 77 - .../scripts/server/serverTCP.py | 52 - .../scripts/server/serverUART.py | 51 - .../scripts/service/__init__.py | 12 - .../test_annotations/test_annotations.py | 103 - .../scripts/test_binary/test_binary.py | 116 - .../scripts/test_builtin/test_builtin.py | 343 - .../scripts/test_const/test_const.py | 82 - .../scripts/test_enums/test_enums.py | 215 - .../scripts/test_lists/test_lists.py | 396 - .../scripts/test_struct/test_struct.py | 315 - .../scripts/test_typedef/test_typedef.py | 165 - .../scripts/test_unions/test_unions.py | 266 - .../__init__.py | 0 .../test_annotations/test_annotations.py | 80 + .../__init__.py | 0 .../test_arbitrator/test_arbitrator.py | 365 + .../test_enums => test_arrays}/__init__.py | 0 .../{scripts => }/test_arrays/test_arrays.py | 398 +- .../test_group => test_binary}/__init__.py | 0 .../test_binary/test_binary.py | 88 + .../test_lists => test_builtin}/__init__.py | 0 .../test_builtin/test_builtin.py | 282 + .../test_typedef => test_const}/__init__.py | 0 .../test_const/test_const.py | 66 + .../test_unions => test_enums}/__init__.py | 0 .../test_enums/test_enums.py | 179 + test/python_impl_tests/test_lists/__init__.py | 0 .../test_lists/test_lists.py | 317 + .../python_impl_tests/test_struct/__init__.py | 0 .../test_struct/test_struct.py | 297 + .../test_typedef/__init__.py | 0 .../test_typedef/test_typedef.py | 133 + .../python_impl_tests/test_unions/__init__.py | 0 .../test_unions/test_unions.py | 224 + test/run_unit_tests.py | 989 ++- test/test_annotations/CMakeLists.txt | 14 + test/test_arbitrator/CMakeLists.txt | 72 + test/test_arrays/CMakeLists.txt | 13 + test/test_binary/CMakeLists.txt | 10 + test/test_builtin/CMakeLists.txt | 10 + test/test_callbacks/CMakeLists.txt | 92 + test/test_const/CMakeLists.txt | 10 + test/test_enums/CMakeLists.txt | 10 + test/test_lists/CMakeLists.txt | 10 + test/test_shared/CMakeLists.txt | 10 + test/test_struct/CMakeLists.txt | 13 + test/test_typedef/CMakeLists.txt | 10 + test/test_unions/CMakeLists.txt | 13 + test/zephyr/uart/config/erpc_config.h | 2 +- .../uart/test_annotations/testcase.yaml | 2 +- .../zephyr/uart/test_arbitrator/testcase.yaml | 2 +- test/zephyr/uart/test_arrays/testcase.yaml | 2 +- test/zephyr/uart/test_binary/testcase.yaml | 2 +- test/zephyr/uart/test_builtin/testcase.yaml | 2 +- test/zephyr/uart/test_callbacks/testcase.yaml | 2 +- test/zephyr/uart/test_const/testcase.yaml | 2 +- test/zephyr/uart/test_enums/testcase.yaml | 2 +- test/zephyr/uart/test_lists/testcase.yaml | 2 +- test/zephyr/uart/test_shared/testcase.yaml | 2 +- test/zephyr/uart/test_struct/testcase.yaml | 2 +- test/zephyr/uart/test_typedef/testcase.yaml | 2 +- test/zephyr/uart/test_unions/testcase.yaml | 2 +- 233 files changed, 31767 insertions(+), 3466 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 Kconfig create mode 100644 SCR.txt create mode 100644 cmake/erpc_utils.cmake create mode 100644 cmake/extensions.cmake create mode 100644 cmake/kconfig.cmake create mode 100644 cmake/python.cmake create mode 100644 cmake/test.cmake create mode 100644 erpc_c/CMakeLists.txt rename erpc_c/{readme.txt => README.md} (53%) create mode 100644 erpc_c/erpc_c_sources.cmake rename erpc_java/{readme.md => README.md} (92%) rename erpc_python/{readme.md => README.md} (100%) create mode 100644 erpcgen/CMakeLists.txt create mode 100644 erpcgen/README.md delete mode 100644 erpcgen/readme.txt create mode 100644 examples/CMakeLists.txt create mode 100644 examples/MCUXPRESSO_SDK/Kconfig create mode 100644 examples/MCUXPRESSO_SDK/erpc_error_handler.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_error_handler.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_examples.cmake create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/c_erpc_matrix_multiply_client.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/c_erpc_matrix_multiply_client.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/c_erpc_matrix_multiply_server.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/c_erpc_matrix_multiply_server.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply.erpc create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_client.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_client.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_common.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_common.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_interface.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_interface.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_server.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_matrix_multiply/service/erpc_matrix_multiply_server.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core0Interface_client.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core0Interface_client.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core0Interface_server.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core0Interface_server.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core1Interface_client.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core1Interface_client.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core1Interface_server.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/c_erpc_two_way_rpc_Core1Interface_server.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc.erpc create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_client.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_client.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_common.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_common.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_interface.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_interface.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_server.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core0Interface_server.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_client.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_client.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_common.h create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_common.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_interface.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_interface.hpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_server.cpp create mode 100644 examples/MCUXPRESSO_SDK/erpc_two_way_rpc/service/erpc_two_way_rpc_Core1Interface_server.hpp create mode 100644 examples/hello_world/CMakeLists.txt create mode 100644 examples/hello_world/README.md create mode 100644 examples/hello_world/c/erpc_config.h create mode 100644 examples/hello_world/c/erpc_error_handler.cpp create mode 100644 examples/hello_world/c/erpc_error_handler.h create mode 100644 examples/hello_world/c/main_client.c create mode 100644 examples/hello_world/c/main_client.cpp create mode 100644 examples/hello_world/c/main_server.c create mode 100644 examples/hello_world/c/main_server.cpp create mode 100644 examples/hello_world/config.h create mode 100644 examples/hello_world/hello_world.erpc create mode 100644 examples/hello_world/java/pom.xml create mode 100644 examples/hello_world/java/src/main/java/org/example/Client.java create mode 100644 examples/hello_world/java/src/main/java/org/example/Config.java create mode 100644 examples/hello_world/java/src/main/java/org/example/Main.java create mode 100644 examples/hello_world/java/src/main/java/org/example/Server.java create mode 100644 examples/hello_world/java/src/main/java/org/example/TextServiceService.java create mode 100644 examples/hello_world/py/config.py create mode 100644 examples/hello_world/py/main_client.py create mode 100644 examples/hello_world/py/main_server.py rename examples/{ => idl}/ble/ble_common.erpc (100%) rename examples/{ => idl}/ble/ble_gap.erpc (100%) rename examples/{ => idl}/ble/ble_gatt.erpc (100%) rename examples/{ => idl}/ble/bluetooth.erpc (100%) rename examples/{ => idl}/smac.erpc (100%) rename examples/{ => idl}/temp_alarm.erpc (100%) rename examples/matrix_multiply_java/{readme.md => README.md} (100%) rename examples/{matrix_multiply_tcp_python/readme.md => matrix_multiply_python/README.md} (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/matrix_multiply.py (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/__init__.py (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/erpc_matrix_multiply.erpc (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/erpc_matrix_multiply/__init__.py (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/erpc_matrix_multiply/client.py (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/erpc_matrix_multiply/common.py (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/erpc_matrix_multiply/interface.py (100%) rename examples/{matrix_multiply_tcp_python => matrix_multiply_python}/service/erpc_matrix_multiply/server.py (100%) create mode 100644 examples/matrix_multiply_tcp_c/CMakeLists.txt create mode 100644 examples/matrix_multiply_tcp_c/README.md create mode 100644 examples/matrix_multiply_tcp_c/erpc_config.h create mode 100644 examples/matrix_multiply_tcp_c/erpc_error_handler.cpp create mode 100644 examples/matrix_multiply_tcp_c/erpc_error_handler.h create mode 100644 examples/matrix_multiply_tcp_c/erpc_matrix_multiply.erpc create mode 100644 examples/matrix_multiply_tcp_c/main_client.c create mode 100644 examples/matrix_multiply_tcp_c/main_client.cpp create mode 100644 examples/matrix_multiply_tcp_c/main_server.c create mode 100644 examples/matrix_multiply_tcp_c/main_server.cpp create mode 100644 mcux/CMakeLists.txt create mode 100644 mcux/Kconfig create mode 100644 prj.conf create mode 100644 scripts/kconfig/diffconfig create mode 100644 scripts/kconfig/guiconfig.py create mode 100644 scripts/kconfig/hardenconfig.py create mode 100644 scripts/kconfig/hardened.csv create mode 100644 scripts/kconfig/kconfig.py create mode 100644 scripts/kconfig/kconfigfunctions.py create mode 100644 scripts/kconfig/kconfiglib.py create mode 100644 scripts/kconfig/lint.py create mode 100644 scripts/kconfig/menuconfig.py create mode 100644 test/CMakeLists.txt create mode 100644 test/README.md delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_annotations/test_unit_test_common/client/CommonClient.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_annotations/test_unit_test_common/common/Constants.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_annotations/test_unit_test_common/interfaces/ICommon.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_annotations/test_unit_test_common/server/AbstractCommonService.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/client/CommonClient.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/common/Constants.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/common/enums/enumColor.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/common/enums/myEnum.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/common/enums/numbers.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/interfaces/ICommon.java delete mode 100644 test/java_impl_tests/src/main/java/io/github/embeddedrpc/erpc/tests/test_arrays/test_unit_test_common/server/AbstractCommonService.java create mode 100644 test/prj.conf rename test/python_impl_tests/{scripts/service/test_binary => }/__init__.py (100%) create mode 100644 test/python_impl_tests/conftest.py delete mode 100644 test/python_impl_tests/scripts/common/common.py delete mode 100644 test/python_impl_tests/scripts/conftest.py delete mode 100644 test/python_impl_tests/scripts/server/clientTCP.py delete mode 100644 test/python_impl_tests/scripts/server/clientUART.py delete mode 100644 test/python_impl_tests/scripts/server/connection.py delete mode 100644 test/python_impl_tests/scripts/server/runner.py delete mode 100644 test/python_impl_tests/scripts/server/serverTCP.py delete mode 100644 test/python_impl_tests/scripts/server/serverUART.py delete mode 100644 test/python_impl_tests/scripts/service/__init__.py delete mode 100644 test/python_impl_tests/scripts/test_annotations/test_annotations.py delete mode 100644 test/python_impl_tests/scripts/test_binary/test_binary.py delete mode 100644 test/python_impl_tests/scripts/test_builtin/test_builtin.py delete mode 100644 test/python_impl_tests/scripts/test_const/test_const.py delete mode 100644 test/python_impl_tests/scripts/test_enums/test_enums.py delete mode 100644 test/python_impl_tests/scripts/test_lists/test_lists.py delete mode 100644 test/python_impl_tests/scripts/test_struct/test_struct.py delete mode 100644 test/python_impl_tests/scripts/test_typedef/test_typedef.py delete mode 100644 test/python_impl_tests/scripts/test_unions/test_unions.py rename test/python_impl_tests/{scripts/service/test_builtin => test_annotations}/__init__.py (100%) create mode 100644 test/python_impl_tests/test_annotations/test_annotations.py rename test/python_impl_tests/{scripts/service/test_const => test_arbitrator}/__init__.py (100%) create mode 100644 test/python_impl_tests/test_arbitrator/test_arbitrator.py rename test/python_impl_tests/{scripts/service/test_enums => test_arrays}/__init__.py (100%) rename test/python_impl_tests/{scripts => }/test_arrays/test_arrays.py (58%) rename test/python_impl_tests/{scripts/service/test_group => test_binary}/__init__.py (100%) create mode 100644 test/python_impl_tests/test_binary/test_binary.py rename test/python_impl_tests/{scripts/service/test_lists => test_builtin}/__init__.py (100%) create mode 100644 test/python_impl_tests/test_builtin/test_builtin.py rename test/python_impl_tests/{scripts/service/test_typedef => test_const}/__init__.py (100%) create mode 100644 test/python_impl_tests/test_const/test_const.py rename test/python_impl_tests/{scripts/service/test_unions => test_enums}/__init__.py (100%) create mode 100644 test/python_impl_tests/test_enums/test_enums.py create mode 100644 test/python_impl_tests/test_lists/__init__.py create mode 100644 test/python_impl_tests/test_lists/test_lists.py create mode 100644 test/python_impl_tests/test_struct/__init__.py create mode 100644 test/python_impl_tests/test_struct/test_struct.py create mode 100644 test/python_impl_tests/test_typedef/__init__.py create mode 100644 test/python_impl_tests/test_typedef/test_typedef.py create mode 100644 test/python_impl_tests/test_unions/__init__.py create mode 100644 test/python_impl_tests/test_unions/test_unions.py create mode 100644 test/test_annotations/CMakeLists.txt create mode 100644 test/test_arbitrator/CMakeLists.txt create mode 100644 test/test_arrays/CMakeLists.txt create mode 100644 test/test_binary/CMakeLists.txt create mode 100644 test/test_builtin/CMakeLists.txt create mode 100644 test/test_callbacks/CMakeLists.txt create mode 100644 test/test_const/CMakeLists.txt create mode 100644 test/test_enums/CMakeLists.txt create mode 100644 test/test_lists/CMakeLists.txt create mode 100644 test/test_shared/CMakeLists.txt create mode 100644 test/test_struct/CMakeLists.txt create mode 100644 test/test_typedef/CMakeLists.txt create mode 100644 test/test_unions/CMakeLists.txt 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..b894621aa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,355 @@ +# 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] + +## [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.13.0...HEAD +[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..6dfa14ea3 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,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 +297,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 +339,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 diff --git a/SCR.txt b/SCR.txt new file mode 100644 index 000000000..aafc9df8c --- /dev/null +++ b/SCR.txt @@ -0,0 +1,37 @@ +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