Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmake: add scope support to script mode #83045

Merged
merged 2 commits into from
Dec 26, 2024

Conversation

pillo79
Copy link
Collaborator

@pillo79 pillo79 commented Dec 16, 2024

In CMake script mode, "targets" are an undefined concept, therefore no properties can be attached to them; those CMake functions are either silently ignored or result in errors. This means that zephyr_set() and any functionality that depends on it (e.g. the yaml module) is not usable in script mode.

This PR works around this limitation as #83199 by using GLOBAL PROPERTY instead of TARGET entities.
Two additional functions are introduced to offer a simplified access to the scope functionality:

  • a zephyr_exists_scope macro to check if a scope exists; and
  • a zephyr_get_scoped function to retrieve scoped property values.

These utility functions are then used in the current code to replace direct property retrieval and scope checks.

The current test suite for the zephyr_get API is modified to also run itself in script mode and detect any future issue.

This, along with #80007, is a prerequisite for the LLEXT EDK refactor in #78727.

@pillo79 pillo79 force-pushed the pr-cmake-scope-support branch 3 times, most recently from 39cb458 to 8a6052c Compare December 16, 2024 16:45
@pillo79 pillo79 marked this pull request as ready for review December 17, 2024 08:54
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Biggest concern for now is introduction of different implementation for normal vs script mode, as well as if(CMAKE_SCRIPT_MODE_FILE) starting to creep into various places in CMake code.

The script mode possibility was introduced for support package_helper, see #41302.

Of course purpose can be extended if there is a need, but from the PR description it is not exactly clear to me why we need this change.

Even with this PR, then yaml_save() is only called from <zephyr-base>/CMakeLists.txt and thus never called from the package helper.

I see some references to other llext related PRs, but even with those it is not fully clear who / why / when the yaml output is required in script mode, as twister itself doesn't seem to be needing it.

Please provide some more description of what is failing and when, and why this is problematic compared to the initial purpose of script mode, so that PR can be reviewed in that context and the extended script mode purpose is clear.

@@ -3359,7 +3359,12 @@ function(zephyr_get_scoped output scope variable)
message(FATAL_ERROR "zephyr_get_scoped(): scope ${scope} doesn't exists.")
endif()

get_property(value TARGET ${scope}_scope PROPERTY ${variable})
if(CMAKE_SCRIPT_MODE_FILE)
set(kind SOURCE)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a big concern here is the use of SOURCE.

The SOURCE scope in CMake properties is following the current directory, now in the script mode case then add_subdirectory() cannot be used, meaning the will not be different directories and thus probablt will work,m but I still find it a problematic use, especially because the directory detail will not be clear to many users of CMake.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out, as I was unsure about what using SOURCE implied. It definitely makes sense to add some note in each function comment block detailing that script mode is supported and if additional limitations apply.

However, in this case that limitation has no practical effect as you already identified.

@@ -3359,7 +3359,12 @@ function(zephyr_get_scoped output scope variable)
message(FATAL_ERROR "zephyr_get_scoped(): scope ${scope} doesn't exists.")
endif()

get_property(value TARGET ${scope}_scope PROPERTY ${variable})
if(CMAKE_SCRIPT_MODE_FILE)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not have CMake script mode testing inside functions / throughout the code.

If the code is needed in script mode (thus cannot be stubbed) then it should be written so that there is only one implementation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rationale for different implementations is as follows:

  • Zephyr code heavily relies on current zephyr_set / zephyr_get semantics, so TARGET cannot be changed in project mode without a very significant effort; it would also be a breaking change downstream.
  • Script mode does not have TARGET support, so an alternative implementation has to be chosen to implement.

I totally agree those SCRIPT_MODE conditionals are ugly, but IMHO this is unavoidable with the current monolithic extensions.cmake module (if this was a collection of separate files it would be easier to cherry-pick and provide alternatives, but that is yet another sizable effort). I personally also think having those in each function is better than having a full alternative implementation at the bottom, because there is only one place to look for.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this was a collection of separate files it would be easier to cherry-pick and provide alternatives, but that is yet another sizable effort

another one on my wish list, but before moving all those functions to multiple other locations / individual CMake modules, then we must have proper generated docs, else knowing where to find a given function will become even harder.

Comment on lines 5937 to 5956
# Script mode version, using SOURCE properties
function(zephyr_create_scope scope)
zephyr_exists_scope(scope_exists ${scope})
if(scope_exists)
message(FATAL_ERROR "zephyr_create_scope(${scope}) already exists.")
endif()

set_property(SOURCE ${scope}_scope PROPERTY ZEPHYR_SCOPE TRUE)
endfunction()

# Script mode version, using SOURCE properties
macro(zephyr_exists_scope output scope)
get_property(zephyr_exists_scope_value SOURCE ${scope}_scope PROPERTY ZEPHYR_SCOPE)
if(DEFINED zephyr_exists_scope_value)
unset(zephyr_exists_scope_value)
set(${output} TRUE)
else()
set(${output} FALSE)
endif()
endmacro()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not embark on a path where we have different behavior / implementation in script mode and normal mode.

Current stubbing is to silence warnings which are not required in script mode.
The script mode was introduced for package_helper, #41302, for example for twister to do a partial (and faster) run for filtering purposes, and thereby remove the need for a full CMake build generation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As detailed in my comment above, the LLEXT EDK will become the second case where the extensions module, which is being required by yaml, has to be used in script mode. I personally would leave this last area only to actual stubs.

# <output> : Output variable containing the result of the check
# <scope> : Scope to look up
#
macro(zephyr_exists_scope output scope)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this as macro and not a function ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real need, will convert to set(... PARENT_SCOPE) instead.

Copy link
Collaborator Author

@pillo79 pillo79 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the PR description it is not exactly clear to me why we need this change.

@tejlmand I feel like I'm on a goose chase 🙂 but here's a recap of the full story:

  • In September I have proposed PR llext-edk: rework, add Python export and more optional information #78727, that limited the rework to the EDK files and the call site in the main CMakelists.txt. This was blocked twice, the last time in favor of using the just-introduced YAML support. That lacked a way to expand generator expressions, which is extremely important for the EDK. So I worked on that.

  • I have been working since October to add genex support to the YAML module in PR cmake: support generator expression in yaml module #80007, before reworking the above PR to use it. As you well know, the yaml module heavily depends on the scope functionality in zephyr_set, and since I now need to use it from llext-edk.cmake (which is called in script mode), I need to also add that support. So I worked on that.

  • I submitted this PR as the third attempt at moving forward with something, mentioning the relevant threads, and yet this was blocked again because -among other things- there is no obvious need for it.

I do have a working implementation here, of course. I just pushed an update to the YAML PR yesterday, and I was rewording that PR's description at the time you commented here. Updating production-quality commits and rationales for 3 cascading PRs on every change takes me some time... 😰

I sincerely appreciate your reviews and insightful comments 🙇, and try to apply all of them. In this instance however I really feel it's unfair to dismiss this PR without providing any guidance on how you think the issue should be properly addressed.


  • Do you see any alternative to adding script mode support for zephyr_set et al, to be able to use the yaml module in the LLEXT EDK?
  • If we agree on adding this functionality, modifying the existing functions to use SOURCE and TARGET in the appropriate contexts minimizes code and semantic changes IMO. Do you have any advice on how else to approach this?

Thanks in advance for your time, but please advise how to proceed.

# <output> : Output variable containing the result of the check
# <scope> : Scope to look up
#
macro(zephyr_exists_scope output scope)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real need, will convert to set(... PARENT_SCOPE) instead.

@@ -3359,7 +3359,12 @@ function(zephyr_get_scoped output scope variable)
message(FATAL_ERROR "zephyr_get_scoped(): scope ${scope} doesn't exists.")
endif()

get_property(value TARGET ${scope}_scope PROPERTY ${variable})
if(CMAKE_SCRIPT_MODE_FILE)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rationale for different implementations is as follows:

  • Zephyr code heavily relies on current zephyr_set / zephyr_get semantics, so TARGET cannot be changed in project mode without a very significant effort; it would also be a breaking change downstream.
  • Script mode does not have TARGET support, so an alternative implementation has to be chosen to implement.

I totally agree those SCRIPT_MODE conditionals are ugly, but IMHO this is unavoidable with the current monolithic extensions.cmake module (if this was a collection of separate files it would be easier to cherry-pick and provide alternatives, but that is yet another sizable effort). I personally also think having those in each function is better than having a full alternative implementation at the bottom, because there is only one place to look for.

@@ -3359,7 +3359,12 @@ function(zephyr_get_scoped output scope variable)
message(FATAL_ERROR "zephyr_get_scoped(): scope ${scope} doesn't exists.")
endif()

get_property(value TARGET ${scope}_scope PROPERTY ${variable})
if(CMAKE_SCRIPT_MODE_FILE)
set(kind SOURCE)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out, as I was unsure about what using SOURCE implied. It definitely makes sense to add some note in each function comment block detailing that script mode is supported and if additional limitations apply.

However, in this case that limitation has no practical effect as you already identified.

Comment on lines 5937 to 5956
# Script mode version, using SOURCE properties
function(zephyr_create_scope scope)
zephyr_exists_scope(scope_exists ${scope})
if(scope_exists)
message(FATAL_ERROR "zephyr_create_scope(${scope}) already exists.")
endif()

set_property(SOURCE ${scope}_scope PROPERTY ZEPHYR_SCOPE TRUE)
endfunction()

# Script mode version, using SOURCE properties
macro(zephyr_exists_scope output scope)
get_property(zephyr_exists_scope_value SOURCE ${scope}_scope PROPERTY ZEPHYR_SCOPE)
if(DEFINED zephyr_exists_scope_value)
unset(zephyr_exists_scope_value)
set(${output} TRUE)
else()
set(${output} FALSE)
endif()
endmacro()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As detailed in my comment above, the LLEXT EDK will become the second case where the extensions module, which is being required by yaml, has to be used in script mode. I personally would leave this last area only to actual stubs.

@tejlmand
Copy link
Collaborator

I now need to use it from llext-edk.cmake (which is called in script mode), I need to also add that support.

That was the missing piece of info I needed, for some reason I had missed the combo that llext-edk which is now being updated to use the yaml parser is actually called in script mode. :)

Guess I was to focused on the Zephyr build system itself to remember that the yaml module itself would be re-used.

@tejlmand
Copy link
Collaborator

In this instance however I really feel it's unfair to dismiss this PR without providing any guidance on how you think the issue should be properly addressed.

Sorry, I didn't mean any offense in the nack, I think the work done is great, but to provide the best guidance I needed the actual use-case on where the script mode entered the loop.

So it was with a genuine desire to fully understand the flow and not having to review this code bty traversing the other PRs.
I could see they were connected, but not the exact connection point (llext-edk.cmake)

@tejlmand
Copy link
Collaborator

tejlmand commented Dec 19, 2024

@pillo79 I made a quick draft here #83199 where I switched from using targets and target properties to instead using global properties.

To still support scope creation / checking, which I think is quite important for the ability to catch mistake like scope name typos / naming refactory, then I've kept the feature of tracking created scopes.

Take a look, I think the principle used there can be merged into this work.
And a big bonus is that we avoid script mode checking as the scoping feature now works in both normal and script mode.

Feel free to take and modify #83199 anyway you like so that it suits your needs.

@pillo79 pillo79 force-pushed the pr-cmake-scope-support branch from 8a6052c to 5ab8546 Compare December 19, 2024 10:02
@pillo79
Copy link
Collaborator Author

pillo79 commented Dec 19, 2024

Thanks @tejlmand, I did not dare touch how zephyr_set worked 😁

I amended your commit with the zephyr_get_scoped functionality.
I also left the build_info script mode stub because the yaml module is not loaded by extensions.cmake (should it?)
Ran the cmake.zephyr_get test suite again so this should be good now!

@tejlmand
Copy link
Collaborator

I also left the build_info script mode stub because the yaml module is not loaded by extensions.cmake (should it?)

Good point. I tested my work locally with package helper and some extra local mods for build_info and discovered that we could avoid the stub in those cases 😄

But you're right, we should not load yaml module in extensions.cmake. I would defer that until next step cleanup #83045 (comment)

tejlmand
tejlmand previously approved these changes Dec 19, 2024
tejlmand and others added 2 commits December 19, 2024 13:54
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 `<scope>:<property>` to avoid naming
collisions.
A `scope:<scope-name>` 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 <[email protected]>
Signed-off-by: Luca Burelli <[email protected]>
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 <[email protected]>
@pillo79
Copy link
Collaborator Author

pillo79 commented Dec 19, 2024

this should be good now!

^-- only slightly less effective than "what can possibly go wrong"
💣

Fixed an issue with APPEND property to zephyr_set.
EDIT: also rebased to use the same PR base as #80007 - the only change is this line.

Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch on the argument order swap 🙈

@kartben kartben merged commit d492c84 into zephyrproject-rtos:main Dec 26, 2024
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants