From 05dea14cf221840fa28d994071c6fee27ab5322e Mon Sep 17 00:00:00 2001 From: Torsten Rasmussen Date: Thu, 19 Dec 2024 09:40:48 +0100 Subject: [PATCH 1/2] cmake: use GLOBAL property instead TARGET properties for scoping Targets are not available in script mode. To support the Zephyr scoping feature used by snippets and yaml module then this commit moves from using custom targets to use GLOBAL properties for scopes. A scope property is prefixed with `:` to avoid naming collisions. A `scope:` global property is used to track created scopes. Tracking valid scopes ensure that properties are only set on known scopes and thus catches typos / naming errors. Add zephyr_scope_exists() and zephyr_get_scoped() to abstract the implementation details of the scoped property retrieval and refactor current code to use them. Signed-off-by: Torsten Rasmussen Signed-off-by: Luca Burelli --- cmake/modules/extensions.cmake | 66 +++++++++++++++++++++------ cmake/modules/yaml.cmake | 17 +++---- tests/cmake/zephyr_get/CMakeLists.txt | 3 +- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index d5cd84256d2e..0d4c0b21a97e 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -3251,8 +3251,9 @@ function(zephyr_get variable) set(sysbuild_global_${var}) endif() - if(TARGET snippets_scope) - get_property(snippets_${var} TARGET snippets_scope PROPERTY ${var}) + zephyr_scope_exists(scope_defined snippets) + if(scope_defined) + zephyr_get_scoped(snippets_${var} snippets ${var}) endif() endforeach() @@ -3317,11 +3318,54 @@ endfunction(zephyr_get variable) # : Name of new scope. # function(zephyr_create_scope scope) - if(TARGET ${scope}_scope) + zephyr_scope_exists(scope_defined ${scope}) + if(scope_defined) message(FATAL_ERROR "zephyr_create_scope(${scope}) already exists.") endif() - add_custom_target(${scope}_scope) + set_property(GLOBAL PROPERTY scope:${scope} TRUE) +endfunction() + +# Usage: +# zephyr_scope_exists( ) +# +# Check if exists. +# +# : Variable to set with result. +# TRUE if scope exists, FALSE otherwise. +# : Name of scope. +# +function(zephyr_scope_exists result scope) + get_property(scope_defined GLOBAL PROPERTY scope:${scope}) + if(scope_defined) + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Usage: +# zephyr_get_scoped( ) +# +# Get the current value of in a specific , as defined by a +# previous zephyr_set() call. The value will be stored in the var. +# +# : Variable to store the value in +# : Scope for the variable look up +# : Name to look up in the specific scope +# +function(zephyr_get_scoped output scope var) + zephyr_scope_exists(scope_defined ${scope}) + if(NOT scope_defined) + message(FATAL_ERROR "zephyr_get_scoped(): scope ${scope} doesn't exists.") + endif() + + get_property(value GLOBAL PROPERTY ${scope}_scope:${var}) + if(DEFINED value) + set(${output} "${value}" PARENT_SCOPE) + else() + unset(${output} PARENT_SCOPE) + endif() endfunction() # Usage: @@ -3342,7 +3386,8 @@ function(zephyr_set variable) zephyr_check_arguments_required_all(zephyr_set SET_VAR SCOPE) - if(NOT TARGET ${SET_VAR_SCOPE}_scope) + zephyr_scope_exists(scope_defined ${SET_VAR_SCOPE}) + if(NOT scope_defined) message(FATAL_ERROR "zephyr_set(... SCOPE ${SET_VAR_SCOPE}) doesn't exists.") endif() @@ -3350,8 +3395,8 @@ function(zephyr_set variable) set(property_args APPEND) endif() - set_property(TARGET ${SET_VAR_SCOPE}_scope ${property_args} - PROPERTY ${variable} ${SET_VAR_UNPARSED_ARGUMENTS} + set_property(GLOBAL ${property_args} PROPERTY + ${SET_VAR_SCOPE}_scope:${variable} ${SET_VAR_UNPARSED_ARGUMENTS} ) endfunction() @@ -5871,16 +5916,11 @@ if(CMAKE_SCRIPT_MODE_FILE) # This silence the error: 'set_target_properties command is not scriptable' endfunction() - function(zephyr_set variable) - # This silence the error: zephyr_set(... SCOPE ) doesn't exists. - endfunction() - # Build info creates a custom target for handling of build info. # build_info is not needed in script mode but still called by Zephyr CMake # modules. Therefore disable build_info(...) in when including # extensions.cmake in script mode. function(build_info) - # This silence the error: 'YAML context 'build_info' does not exist.' - # 'Remember to create a YAML context' + # This silence the error: 'Unknown CMake command "yaml_context"' endfunction() endif() diff --git a/cmake/modules/yaml.cmake b/cmake/modules/yaml.cmake index 50d6c7cc7462..9ab9b333fa46 100644 --- a/cmake/modules/yaml.cmake +++ b/cmake/modules/yaml.cmake @@ -93,7 +93,8 @@ function(yaml_context) ) endif() - if(TARGET ${ARG_YAML_NAME}_scope) + zephyr_scope_exists(scope_defined ${ARG_YAML_NAME}) + if(scope_defined) list(POP_FRONT ARG_YAML_UNPARSED_ARGUMENTS out-var) set(${out-var} TRUE PARENT_SCOPE) else() @@ -183,7 +184,7 @@ function(yaml_get out_var) zephyr_check_arguments_required_all(${CMAKE_CURRENT_FUNCTION} ARG_YAML NAME KEY) internal_yaml_context_required(NAME ${ARG_YAML_NAME}) - get_property(json_content TARGET ${ARG_YAML_NAME}_scope PROPERTY JSON) + zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON) # We specify error variable to avoid a fatal error. # If key is not found, then type becomes '-NOTFOUND' and value handling is done below. @@ -224,7 +225,7 @@ function(yaml_length out_var) zephyr_check_arguments_required_all(${CMAKE_CURRENT_FUNCTION} ARG_YAML NAME KEY) internal_yaml_context_required(NAME ${ARG_YAML_NAME}) - get_property(json_content TARGET ${ARG_YAML_NAME}_scope PROPERTY JSON) + zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON) string(JSON type ERROR_VARIABLE error TYPE "${json_content}" ${ARG_YAML_KEY}) if(type STREQUAL ARRAY) @@ -262,7 +263,7 @@ function(yaml_set) zephyr_check_arguments_exclusive(${CMAKE_CURRENT_FUNCTION} ARG_YAML VALUE LIST) internal_yaml_context_required(NAME ${ARG_YAML_NAME}) - get_property(json_content TARGET ${ARG_YAML_NAME}_scope PROPERTY JSON) + zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON) set(yaml_key_undefined ${ARG_YAML_KEY}) foreach(k ${yaml_key_undefined}) @@ -335,7 +336,7 @@ function(yaml_remove) zephyr_check_arguments_required_all(${CMAKE_CURRENT_FUNCTION} ARG_YAML NAME KEY) internal_yaml_context_required(NAME ${ARG_YAML_NAME}) - get_property(json_content TARGET ${ARG_YAML_NAME}_scope PROPERTY JSON) + zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON) string(JSON json_content REMOVE "${json_content}" ${ARG_YAML_KEY}) zephyr_set(JSON "${json_content}" SCOPE ${ARG_YAML_NAME}) @@ -359,18 +360,18 @@ function(yaml_save) zephyr_check_arguments_required(${CMAKE_CURRENT_FUNCTION} ARG_YAML NAME) internal_yaml_context_required(NAME ${ARG_YAML_NAME}) - get_target_property(yaml_file ${ARG_YAML_NAME}_scope FILE) + zephyr_get_scoped(yaml_file ${ARG_YAML_NAME} FILE) if(NOT yaml_file) zephyr_check_arguments_required(${CMAKE_CURRENT_FUNCTION} ARG_YAML FILE) endif() - get_property(json_content TARGET ${ARG_YAML_NAME}_scope PROPERTY JSON) + zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON) to_yaml("${json_content}" 0 yaml_out) if(DEFINED ARG_YAML_FILE) set(yaml_file ${ARG_YAML_FILE}) else() - get_property(yaml_file TARGET ${ARG_YAML_NAME}_scope PROPERTY FILE) + zephyr_get_scoped(yaml_file ${ARG_YAML_NAME} FILE) endif() if(EXISTS ${yaml_file}) FILE(RENAME ${yaml_file} ${yaml_file}.bak) diff --git a/tests/cmake/zephyr_get/CMakeLists.txt b/tests/cmake/zephyr_get/CMakeLists.txt index a6516bfba735..c76d910e5540 100644 --- a/tests/cmake/zephyr_get/CMakeLists.txt +++ b/tests/cmake/zephyr_get/CMakeLists.txt @@ -490,7 +490,8 @@ endfunction() function(test_snippets_scope) - if(NOT TARGET snippets_scope) + zephyr_scope_exists(snippets_defined snippets) + if(NOT snippets_defined) zephyr_create_scope(snippets) endif() From 40a72242c2ed8774355c71d330fc9fa830ee2c92 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Mon, 16 Dec 2024 16:47:22 +0100 Subject: [PATCH 2/2] tests: cmake: run zephyr_get() tests in script mode Re-run the zephyr_get() testsuite in script mode after the project mode testsuite has been executed. This is to ensure that the zephyr_get() function works correctly in script mode as well. Signed-off-by: Luca Burelli --- tests/cmake/zephyr_get/CMakeLists.txt | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/cmake/zephyr_get/CMakeLists.txt b/tests/cmake/zephyr_get/CMakeLists.txt index c76d910e5540..43c2e204ec25 100644 --- a/tests/cmake/zephyr_get/CMakeLists.txt +++ b/tests/cmake/zephyr_get/CMakeLists.txt @@ -2,9 +2,17 @@ cmake_minimum_required(VERSION 3.20.0) -find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(zephyr_get_test) -target_sources(app PRIVATE ${ZEPHYR_BASE}/misc/empty_file.c) +if(CMAKE_SCRIPT_MODE_FILE) + # Script mode initialization (re-run) + set(ZEPHYR_BASE ${CMAKE_CURRENT_LIST_DIR}/../../../) + list(APPEND CMAKE_MODULE_PATH "${ZEPHYR_BASE}/cmake/modules") + include(extensions) +else() + # Project mode initialization (main CMake invocation) + find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + project(zephyr_get_test) + target_sources(app PRIVATE ${ZEPHYR_BASE}/misc/empty_file.c) +endif() if(SYSBUILD) get_property(IMAGE_NAME TARGET sysbuild_cache PROPERTY SYSBUILD_NAME) @@ -73,6 +81,9 @@ function(assert_equal variable expected_value) endif() set(info "${TEST_NAME}: ${variable} == '${actual_value}'") + if(CMAKE_SCRIPT_MODE_FILE) + string(PREPEND info "script mode ") + endif() if("${actual_value}" STREQUAL "${expected_value}") message("PASS: ${info}") else() @@ -584,3 +595,8 @@ run_suite( test_merge_reverse test_snippets_scope ) + +if (NOT CMAKE_SCRIPT_MODE_FILE AND NOT SYSBUILD) + # Re-run this testsuite in plain script mode + execute_process(COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_FILE}) +endif()