Skip to content

Conversation

@pillo79
Copy link
Contributor

@pillo79 pillo79 commented Oct 28, 2025

Not for 4.3.

This PR unifies the implementation of the various zephyr_get_*_for_lang functions and their string variants with a generic zephyr_get_property, so that the same feature set is available to all wrappers. It also adds several new options that expand the possible use cases:

  • AS_STRING: Return the result as a string rather than a list. Equivalent to specifying DELIMITER " ".
  • DELIMITER <delim>: Specify the output delimiter to use for the list entries. Defaults to $<SEMICOLON>.
  • GENEX: Use generator expressions to get the property entries at build time, for targets that are not yet fully configured. Cannot be used with LANG. Also, CMake will complain if the requested property includes $<COMPILE_OPTIONS:...> expressions.
  • LANG <C|CXX|ASM>: When set, the property value is filtered for $<COMPILE_LANGUAGE:lang> entries, and only those matching the given language are kept. Cannot be used with GENEX.
  • PREFIX <prefix>: Specify a prefix to use for each entry in the output list.
  • STRIP_PREFIX: Omit the prefix string, even if given by PREFIX.
  • TARGET <target>: Specify the target to get the property from. Defaults to zephyr_interface.

This work was done while experimenting with alternatives to #98348. It is no longer useful for the EDK, but I think it is still worth considering for merge.

The 'convert_list_of_flags_to_string_of_flags' function was added back
in 2017 with commit 89516fb at the time language support was added
to the CMake build system. The same commit also added all users of that
function; they were directly passed the result of get_property() calls,
so a simple replace was sufficient.

This code was later expanded with the use of generator expressions to
manage delimiters and prefixes, which required replacing the ';'
separator with '$<SEMICOLON>' to properly preserve the list entries.

This made the conversion function invalid, since no ';' can be found in
any 'zephyr_get_*' output, so the conversion function was effectively a
no-op; setting a space as a delimiter is already sufficient to get the
desired result since circa 2020.

Remove this function and 'get_property_and_add_prefix', a macro that was
made redundant by the same value-to-genex conversion.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
@pillo79 pillo79 added this to the v4.4.0 milestone Oct 28, 2025
@pillo79 pillo79 changed the title cmake: refactor zephyr_get_*_for lang functions cmake: refactor zephyr_get_*_for_lang functions Oct 28, 2025
@pillo79 pillo79 marked this pull request as ready for review October 28, 2025 10:51
Copy link
Contributor

@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.

I like the direction, but couple of small adjustments proposed.

# - STRIP_PREFIX: Omit the compiler flag prefix (-I, -D, etc.)
# - DELIMITER <delimiter>: Specify the output delimiter to use

function(zephyr_get_build_prop_for_lang prop lang i)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think cleaning up the current use of zephyr_get_*_for_lang.. is valuable, but if we anyway change current style, then perhaps align closer to CMake.

Like we have zephyr_string and zephyr_get, then perhaps a

zephyr_get_property(<out-var> PROPERTY <prop> LANG <C|CXX|ASM>)

alternative zephyr_get_build_property().

Optional, the _as_string could even be a flag to the function which would default the delimiter to <space> instead of $<SEMICOLON>, as to not need to remember that " " does exactly that.

Copy link
Contributor Author

@pillo79 pillo79 Oct 28, 2025

Choose a reason for hiding this comment

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

I dropped AS_STRING once I understood it could be done with existing options. Looks a lot cleaner with it though, so I'll re-add that.

Comment on lines 248 to 257
macro(generate_build_prop_wrappers item prop)
function(zephyr_get_${item}_for_lang lang i)
zephyr_get_build_prop_for_lang(${prop} ${lang} result_output_list ${ARGN})
set(${i} ${result_output_list} PARENT_SCOPE)
endfunction()
function(zephyr_get_${item}_for_lang_as_string lang i)
zephyr_get_build_prop_for_lang(${prop} ${lang} result_output_list DELIMITER " " ${ARGN})
set(${i} ${result_output_list} PARENT_SCOPE)
endfunction()
endmacro()
Copy link
Contributor

Choose a reason for hiding this comment

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

No, we should not wrap function definition in a macro like this.

It might make it easy for us, but it will make it impossible for ordinary developers to find out where a function like:
zephyr_get_include_directories_for_lang() and friends are defined.

Not to mention the impact this will have on tasks like this: #87083

When we have the property function in place, then let's just deprecate the old style.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I really hate copying code that should be written once only, so this was basically the first thing I did. 😅

I did consider the issue of making the definitions opaque, so I left comments with the generated function names next to each macro invocation for easy grepping. But if #87083 performs actual indexing, I will gladly revert to the verbose sequence of commands. 👍

#
# Options:
# - STRIP_PREFIX: Omit the compiler flag prefix (-I, -D, etc.)
# - DELIMITER <delimiter>: Specify the output delimiter to use
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps specify that default delimiter is $<SEMICOLON> to allow the result to be used in generator expressions.

Copy link
Contributor 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.

Thanks, will update soon with comments.

Comment on lines 248 to 257
macro(generate_build_prop_wrappers item prop)
function(zephyr_get_${item}_for_lang lang i)
zephyr_get_build_prop_for_lang(${prop} ${lang} result_output_list ${ARGN})
set(${i} ${result_output_list} PARENT_SCOPE)
endfunction()
function(zephyr_get_${item}_for_lang_as_string lang i)
zephyr_get_build_prop_for_lang(${prop} ${lang} result_output_list DELIMITER " " ${ARGN})
set(${i} ${result_output_list} PARENT_SCOPE)
endfunction()
endmacro()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I really hate copying code that should be written once only, so this was basically the first thing I did. 😅

I did consider the issue of making the definitions opaque, so I left comments with the generated function names next to each macro invocation for easy grepping. But if #87083 performs actual indexing, I will gladly revert to the verbose sequence of commands. 👍

# - STRIP_PREFIX: Omit the compiler flag prefix (-I, -D, etc.)
# - DELIMITER <delimiter>: Specify the output delimiter to use

function(zephyr_get_build_prop_for_lang prop lang i)
Copy link
Contributor Author

@pillo79 pillo79 Oct 28, 2025

Choose a reason for hiding this comment

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

I dropped AS_STRING once I understood it could be done with existing options. Looks a lot cleaner with it though, so I'll re-add that.

@pillo79 pillo79 force-pushed the pr-cmake-prop-refact branch 3 times, most recently from 6b4ff3f to 2fa4231 Compare October 29, 2025 10:30
@pillo79
Copy link
Contributor Author

pillo79 commented Oct 29, 2025

v2, addressing all comments:

  • Renamed to zephyr_get_property and removed all default build-specific processing:
    • Prefixes are specified in the wrapper functions with PREFIX <...>
    • LANG is optional; when not specified, no filtering is done on the output list
  • Dropped macro wrapper generation in favor of minimal single-call variants. Way easier to read!
  • Made LANG exclusive with GENEX, so that it is clear one cannot support the other; this also prevents using GENEX with wrappers.

Copy link
Contributor

@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.

Thanks for the update 👍

Mostly some minor observations and clarification questions.

string(REPLACE ";" "$<SEMICOLON>" genexp_output_list "${output_list}")
endif()

set(result_output_list "${maybe_prefix}$<JOIN:${genexp_output_list},${args_DELIMITER}${maybe_prefix}>")
Copy link
Contributor

Choose a reason for hiding this comment

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

if args_DELIMITER is a space, then this genex can end up not behaving / expanding as expected.

Has this been verified with more complex lists ?

See also: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#whitespace-and-quoting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is the expected behavior? If the user asks for space as a delimiter, having the char in the${args_DELIMITER} variable will properly expand the genex to a single-space-separated string.

Now, the linked docs mention that the resulting string becomes ambiguous if there were spaces in the data (you may either have too many arguments, or a single quote-enclosed one), but to avoid that... you should use a proper list in the first place (which is returned without AS_STRING).

set(result_output_list "-D$<JOIN:${genexp_output_list},${args_DELIMITER}-D>")
zephyr_get_property(result_output_list
PROPERTY INTERFACE_COMPILE_DEFINITIONS
PREFIX "-D"
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we can simplify.

PREFIX follows the property type, so zephyr_get_property() could have a list of known pairs, like.

  set(prefix_INTERFACE_COMPILE_DEFINITIONS "-D")
  set(prefix_INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "-isystem")
  set(prefix_INTERFACE_INCLUDE_DIRECTORIES "-I")

  if(NOT DEFINED args_PREFIX)
    set(args_PREFIX ${prefix_${args_PROPERTY}})
  endif()

thus supporting all properties possible, by allowing PREFIX, but removing the need for passing PREFIX for known properties, like -D, -I, -isystem etc.

That will also lower the threshold for updating the code from:

zephyr_get_compile_definitions_for_lang(C zephyr_defs)
zephyr_get_include_directories_for_lang(C ZEPHYR_INCLUDES)
etc.

to

zephyr_get_property(zephyr_defs PROPERTY INTERFACE_COMPILE_DEFINITIONS LANG C)
zephyr_get_property(ZEPHYR_INCLUDES PROPERTY ZEPHYR_INCLUDES LANG C)

if we want.

and then in that process, deprecate the old functions.

Thoughts ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had in fact pushed an intermediate version (e0fb12f and parent commits) with a zephyr_get_build_property() and a long series of if()s, but I disliked it for a number of reasons:

  • it is unclear which properties get an automatic prefix and which do not. Some even should never need it (e.g.COMPILE_OPTIONS)
  • that is the only _build-specific thing in the function, everything else applies to any CMake property in the project;
  • obviously ugly with the ifs (but your proposal would be way cleaner!).

In my view, removing the embedded prefix list made the function clean and use-agnostic, and gave a proper meaning to the existing wrappers (collapsing all build-property-specific functionality).

Let me know if you still prefer to incorporate the prefixes in the generic function. If so, I would suggest to call that zephyr_get_build_property as it's doing build-specific stuff as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

but I disliked it for a number of reasons:

yes, seeing it from the function implementation then the if's is really not nice.
I also understand from the concern on the build specific part of the function.

However, when seeing it from the caller, that is if we want to get rid of zephyr_get_*_for_lang functions, then requesting the caller to remember PREFIX -D when using INTERFACE_COMPILE_DEFINITIONS is not nice.
-D ought to be implied by the property name (when seeing things from the caller).

Let me know if you still prefer to incorporate the prefixes in the generic function.

Depends on what you believe the long term support for existing zephyr_get_*_for_lang functions is.

If so, I would suggest to call that zephyr_get_build_property as it's doing build-specific stuff as well.

That might be a better name. Especially as the function description already states:

# Getter function for extracting information from a target. The function
# supports filtering for specific compile languages, prefixes, and

so it is target related, and targets are build specific, as well as LANG is very build oriented.

Copy link
Contributor 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.

Thanks, I understand the concerns, here are my thoughts on them. Let me know how to address the prefix thing.

string(REPLACE ";" "$<SEMICOLON>" genexp_output_list "${output_list}")
endif()

set(result_output_list "${maybe_prefix}$<JOIN:${genexp_output_list},${args_DELIMITER}${maybe_prefix}>")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is the expected behavior? If the user asks for space as a delimiter, having the char in the${args_DELIMITER} variable will properly expand the genex to a single-space-separated string.

Now, the linked docs mention that the resulting string becomes ambiguous if there were spaces in the data (you may either have too many arguments, or a single quote-enclosed one), but to avoid that... you should use a proper list in the first place (which is returned without AS_STRING).

set(result_output_list "-D$<JOIN:${genexp_output_list},${args_DELIMITER}-D>")
zephyr_get_property(result_output_list
PROPERTY INTERFACE_COMPILE_DEFINITIONS
PREFIX "-D"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had in fact pushed an intermediate version (e0fb12f and parent commits) with a zephyr_get_build_property() and a long series of if()s, but I disliked it for a number of reasons:

  • it is unclear which properties get an automatic prefix and which do not. Some even should never need it (e.g.COMPILE_OPTIONS)
  • that is the only _build-specific thing in the function, everything else applies to any CMake property in the project;
  • obviously ugly with the ifs (but your proposal would be way cleaner!).

In my view, removing the embedded prefix list made the function clean and use-agnostic, and gave a proper meaning to the existing wrappers (collapsing all build-property-specific functionality).

Let me know if you still prefer to incorporate the prefixes in the generic function. If so, I would suggest to call that zephyr_get_build_property as it's doing build-specific stuff as well.

@pillo79 pillo79 force-pushed the pr-cmake-prop-refact branch from 2fa4231 to b5e5ce0 Compare November 13, 2025 15:02
Refactored the various 'zephyr_get_*_for_lang*' functions into a single
'zephyr_get_build_property' function that takes the property to get and
the specific output formats as arguments. This avoids differences
between each function, reduces code duplication, and makes it easier to
support additional features in the future.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
Extended the zephyr_get_build_property() CMake function with several
convenience options:

 - GENEX:
      Uses generator expressions to get the property entries at build
      time, for targets that are not yet fully configured.
      Cannot be used with LANG. Also, CMake will complain if the
      requested property includes $<COMPILE_OPTIONS:...> expressions.

 - TARGET <target>:
      Specifies the target to get the property from. Defaults to
      "zephyr_interface" as before.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
@pillo79 pillo79 force-pushed the pr-cmake-prop-refact branch from b5e5ce0 to 8e85b1f Compare November 13, 2025 16:41
@sonarqubecloud
Copy link

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.

3 participants