Files
esp-idf/tools/cmakev2/component.cmake
T

667 lines
28 KiB
CMake

# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
include_guard(GLOBAL)
include(utilities)
include(build)
include(kconfig)
#[[api
.. cmakev2:function:: idf_component_set_property
.. code-block:: cmake
idf_component_set_property(<component> <property> <value> [APPEND])
:component[in]: Component name, target, target alias or interface.
:property[in]: Property name.
:value[in]: Property value.
:APPEND: Append the value to the property's current value instead of
replacing it.
Set the value of the specified component property. The property is also
added to the internal list of component properties if it isn't already
there.
#]]
function(idf_component_set_property component property value)
set(options APPEND)
set(one_value)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
set(append)
if(ARG_APPEND)
set(append APPEND)
endif()
__get_component_interface_or_die(COMPONENT "${component}" OUTPUT component_interface)
__set_property(TARGET "${component_interface}"
PROPERTY "${property}"
PROPERTIES COMPONENT_PROPERTIES
VALUE "${value}"
${append})
endfunction()
#[[api
.. cmakev2:function:: idf_component_get_property
.. code-block:: cmake
idf_component_get_property(<variable> <component> <property> [GENERATOR_EXPRESSION])
:variable[out]: Variable to store the value in.
:component[in]: Component name, target, target alias or interface.
:property[in]: Property name to get the value of.
:GENERATOR_EXPRESSION: Obtain the generator expression for the property
rather than the actual value.
Retrieve the value of the specified component property.
#]]
function(idf_component_get_property variable component property)
set(options GENERATOR_EXPRESSION)
set(one_value)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
set(genexpr)
if(ARG_GENERATOR_EXPRESSION)
set(genexpr GENERATOR_EXPRESSION)
endif()
__get_component_interface_or_die(COMPONENT "${component}" OUTPUT component_interface)
__get_property(TARGET "${component_interface}"
PROPERTY "${property}"
OUTPUT value
${genexpr})
set(${variable} ${value} PARENT_SCOPE)
endfunction()
#[[
__get_component_paths(PATHS <path>...
[EXCLUDE_PATHS <path>...]
[SOURCE <source>]
[CHECK]
OUTPUT <var>)
:PATHS[in]: List of paths to search for component directories.
:EXCLUDE_PATHS[in,opt]: Optional list of paths to exclude from the search of
component directories.
:SOURCE[in,opt]: Source of the ``PATHS``. If provided, it will be included
in the error message when ``CHECK`` is specified.
:CHECK[in,opt]: Verify whether the paths listed in "PATHS" exist. If any
path is missing, abort the build process.
:OUTPUT[out]: Output variable to store the list of found component
directories.
Search for component directories in the specified ``PATHS``, excluding those
in ``EXCLUDE_PATHS``, and store the list of absolute component paths in the
``OUTPUT`` variable. If ``CHECK`` is specified, ensure that the paths listed
in ``PATHS`` exist, and stop the build process if they do not.
#]]
function(__get_component_paths)
set(options CHECK)
set(one_value SOURCE OUTPUT)
set(multi_value PATHS EXCLUDE_PATHS)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_PATHS)
idf_die("PATHS option is required")
endif()
if(NOT DEFINED ARG_OUTPUT)
idf_die("OUTPUT option is required")
endif()
__get_absolute_paths(PATHS "${ARG_PATHS}" OUTPUT include_paths)
__get_absolute_paths(PATHS "${ARG_EXCLUDE_PATHS}" OUTPUT exclude_paths)
if(ARG_CHECK)
foreach(path IN LISTS include_paths)
if(NOT IS_DIRECTORY "${path}")
if(DEFINED ARG_SOURCE)
idf_die("Directory specified in '${ARG_SOURCE}' doesn't exist: '${path}'")
else()
idf_die("Directory doesn't exist: '${path}'")
endif()
endif()
endforeach()
endif()
set(paths "${include_paths}")
set(component_paths "")
while(paths)
list(POP_FRONT paths path)
if(NOT IS_DIRECTORY "${path}" OR "${path}" IN_LIST exclude_paths)
continue()
endif()
if(EXISTS "${path}/CMakeLists.txt")
list(APPEND component_paths "${path}")
elseif("${path}" IN_LIST include_paths)
file(GLOB dirs "${path}/*")
__get_absolute_paths(PATHS "${dirs}" OUTPUT dirs_abs)
list(APPEND paths "${dirs_abs}")
endif()
endwhile()
set(${ARG_OUTPUT} "${component_paths}" PARENT_SCOPE)
endfunction()
#[[
__get_component_interface(COMPONENT <component>
OUTPUT <variable>)
:COMPONENT[int]: Component name, target, target alias or interface.
:OUTPUT[out]: Output variable to store the component interface.
Identify the component interface target using the ``<component>`` value,
which could be a component name, target, target alias, or interface. Return
the component interface target, or NOTFOUND if the interface cannot be
located.
component interface: <component_prefix>_<component name>
component target: _<component_prefix>_<component name>
component alias: <component_prefix>::<component name>
#]]
function(__get_component_interface)
set(options)
set(one_value COMPONENT OUTPUT)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_COMPONENT)
idf_die("COMPONENT option is required")
endif()
if(NOT DEFINED ARG_OUTPUT)
idf_die("OUTPUT option is required")
endif()
idf_build_get_property(component_names COMPONENTS_DISCOVERED)
idf_build_get_property(component_interfaces COMPONENT_INTERFACES)
idf_build_get_property(component_prefix PREFIX)
set(component_interface NOTFOUND)
if("${ARG_COMPONENT}" IN_LIST component_names)
# The component name is among the discovered components, and the
# component interface is simply the component name with a prefix.
set(component_interface "${component_prefix}_${ARG_COMPONENT}")
else()
# The component name might be an alias, so retrieve the actual target
# name.
__get_real_target(TARGET ${ARG_COMPONENT} OUTPUT real_target)
if("${real_target}" IN_LIST component_interfaces)
# The component name is already a component interface or its alias.
set(component_interface "${real_target}")
else()
string(SUBSTRING "${ARG_COMPONENT}" 1 -1 interface)
if("${interface}" IN_LIST component_interfaces)
# The component name is the actual target of the component.
set(component_interface "${interface}")
endif()
endif()
endif()
# Sanity check
if(NOT "${component_interface}" STREQUAL "NOTFOUND"
AND NOT "${component_interface}" IN_LIST component_interfaces)
idf_warn("Interface target '${component_interface}' found for component "
"'${ARG_COMPONENT}', but it's not present in the component "
"interface list.")
set(component_interface NOTFOUND)
endif()
set(${ARG_OUTPUT} ${component_interface} PARENT_SCOPE)
endfunction()
#[[
__get_component_interface_or_die(COMPONENT <component>
OUTPUT <variable>)
:COMPONENT[in]: Component name, target, target alias or interface.
:OUTPUT[out]: Output variable to store the component interface.
A simple wrapper for ``__get_component_interface`` that aborts the build
process if the component interface is not found.
#]]
function(__get_component_interface_or_die)
set(options)
set(one_value COMPONENT OUTPUT)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_COMPONENT)
idf_die("COMPONENT option is required")
endif()
if(NOT DEFINED ARG_OUTPUT)
idf_die("OUTPUT option is required")
endif()
__get_component_interface(COMPONENT "${ARG_COMPONENT}" OUTPUT component_interface)
if("${component_interface}" STREQUAL "NOTFOUND")
idf_die("Component interface for component '${ARG_COMPONENT}' does not exist")
endif()
set(${ARG_OUTPUT} ${component_interface} PARENT_SCOPE)
endfunction()
#[[
__get_component_priority(SOURCE <source>
OUTPUT <variable>)
:SOURCE[in]: String identifying the component source.
:OUTPUT[out]: Output variable to store the component priority.
Return the priority number of a component, where a higher number indicates a
higher priority, based on the given ``source`` string. If the ``source`` is
not valid, return ``NOTFOUND``.
#]]
function(__get_component_priority)
set(options)
set(one_value SOURCE OUTPUT)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_SOURCE)
idf_die("SOURCE option is required")
endif()
if(NOT DEFINED ARG_OUTPUT)
idf_die("OUTPUT option is required")
endif()
if("${ARG_SOURCE}" STREQUAL "project_components")
set(priority 3)
elseif("${ARG_SOURCE}" STREQUAL "project_extra_components")
set(priority 2)
elseif("${ARG_SOURCE}" STREQUAL "project_managed_components")
set(priority 1)
elseif("${ARG_SOURCE}" STREQUAL "idf_components")
set(priority 0)
else()
set(priority NOTFOUND)
endif()
set(${ARG_OUTPUT} "${priority}" PARENT_SCOPE)
endfunction()
#[[
__init_component(DIRECTORY <path>
PREFIX <prefix>
SOURCE <source>)
:DIRECTORY[in]: ``<path>`` where the component is located.
:PREFIX[in]: Prefix for component target names.
:SOURCE[in]: String identifying the component source.
Initialize the component by creating a component interface target, allowing
properties to be attached to it and also add initial component properties.
At this stage, the component is not included in the build. The actual
component target, named as specified in the `COMPONENT_LIB` property, is
created later when ``add_subdirectory`` is called for the component's
directory based on the project or other component requirements.
Components are identified by the directory name they reside in. This means
that components with the same name might exist in different directory paths.
In such cases, the component with the higher priority is used. Priority is
determined by the component's source, as defined in
``__get_component_priority``. If a component with a higher priority than an
existing one is initialized, its name, targets, and other properties remain
the same. Only the directory, priority, and source are updated in the
already initialized component.
The name of the initialized component is added to the
``COMPONENTS_DISCOVERED`` build property, and its interface target is added
to the ``COMPONENT_INTERFACES`` build property. These two lists are used by
``__get_component_interface``, which searches for a component interface
based on the component name, alias, or targets, enabling the setting and
retrieval of component properties.
#]]
function(__init_component)
set(options)
set(one_value DIRECTORY PREFIX SOURCE)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_DIRECTORY)
idf_die("DIRECTORY option is required")
endif()
if(NOT DEFINED ARG_SOURCE)
idf_die("SOURCE option is required")
endif()
if(NOT DEFINED ARG_PREFIX)
idf_die("PREFIX option is required")
endif()
set(component_source "${ARG_SOURCE}")
set(component_prefix "${ARG_PREFIX}")
get_filename_component(component_directory ${ARG_DIRECTORY} ABSOLUTE)
get_filename_component(component_name ${component_directory} NAME)
__get_component_priority(SOURCE "${component_source}" OUTPUT component_priority)
if(NOT EXISTS "${component_directory}/CMakeLists.txt")
idf_die("Directory '${component_directory}' does not contain a component")
endif()
if("${component_priority}" STREQUAL "NOTFOUND")
idf_die("Unknown component source '${component_source}' "
"for directory '${component_directory}'")
endif()
# Collect Kconfig files from this component directory.
idf_build_get_property(target IDF_TARGET)
__collect_kconfig_files_from_directory("${component_directory}" "${target}"
component_kconfig component_projbuild component_rename)
set(component_project_include "${component_directory}/project_include.cmake")
if(NOT EXISTS "${component_project_include}")
set(component_project_include "")
endif()
# Track manifest presence
if(EXISTS "${component_directory}/idf_component.yml")
idf_build_set_property(__COMPONENTS_WITH_MANIFESTS "${component_directory}" APPEND)
endif()
__get_component_interface(COMPONENT "${component_name}" OUTPUT existing_component_interface)
if(NOT "${existing_component_interface}" STREQUAL "NOTFOUND")
# A component with the same name is already initialized. Check if it
# should be replaced with the component currently being initialized.
idf_component_get_property(existing_component_priority
"${existing_component_interface}"
COMPONENT_PRIORITY)
idf_component_get_property(existing_component_directory
"${existing_component_interface}"
COMPONENT_DIR)
idf_component_get_property(existing_component_source
"${existing_component_interface}"
COMPONENT_SOURCE)
if(${component_priority} EQUAL ${existing_component_priority})
idf_die("Component directory '${component_directory}' has the same "
"priority '${component_source}' as component directory "
"'${existing_component_directory}'")
elseif(${component_priority} LESS ${existing_component_priority})
idf_warn("Component directory '${component_directory}' has lower "
"priority '${component_source}' than component directory "
"'${existing_component_directory}' with priority "
"'${existing_component_source}' and will be ignored")
else()
idf_warn("Component '${component_name}' directory '${component_directory}' "
"with higher priority '${component_source}' will be used instead of "
"component directory '${existing_component_directory}' "
"with lower priority '${existing_component_source}'")
# The newly added component has a higher priority than the existing
# one. Since the component name and targets are identical, update
# the existing component with the new directory and priority.
idf_component_set_property("${component_name}" COMPONENT_DIR "${component_directory}")
idf_component_set_property("${component_name}" COMPONENT_SOURCE "${component_source}")
idf_component_set_property("${component_name}" COMPONENT_PRIORITY ${component_priority})
# Update component properties with new Kconfig files
idf_component_set_property("${component_name}" __KCONFIG "${component_kconfig}")
idf_component_set_property("${component_name}" __KCONFIG_PROJBUILD "${component_projbuild}")
idf_component_set_property("${component_name}" __SDKCONFIG_RENAME "${component_rename}")
# Update component project_include.cmake path.
idf_component_set_property("${component_name}" __PROJECT_INCLUDE "${component_project_include}")
endif()
return()
endif()
set(component_interface "${component_prefix}_${component_name}")
set(component_alias "${component_prefix}::${component_name}")
# Real component library target that needs to be created by the component.
set(component_target "_${component_interface}")
# Interface target is used to attach all component properties and is also
# used when the component is linked to other targets.
add_library("${component_interface}" INTERFACE)
idf_build_set_property(COMPONENTS_DISCOVERED "${component_name}" APPEND)
idf_build_set_property(COMPONENT_INTERFACES "${component_interface}" APPEND)
idf_component_set_property("${component_name}" COMPONENT_LIB "${component_target}")
idf_component_set_property("${component_name}" COMPONENT_TARGET "${component_target}")
idf_component_set_property("${component_name}" COMPONENT_NAME "${component_name}")
idf_component_set_property("${component_name}" COMPONENT_DIR "${component_directory}")
idf_component_set_property("${component_name}" COMPONENT_ALIAS "${component_alias}")
idf_component_set_property("${component_name}" COMPONENT_SOURCE "${component_source}")
idf_component_set_property("${component_name}" COMPONENT_INTERFACE "${component_interface}")
idf_component_set_property("${component_name}" COMPONENT_PRIORITY ${component_priority})
idf_component_set_property("${component_name}" COMPONENT_INCLUDED NO)
idf_component_set_property("${component_name}" __PREFIX "${ARG_PREFIX}")
# Set component properties for Kconfig files.
idf_component_set_property("${component_name}" __KCONFIG "${component_kconfig}")
idf_component_set_property("${component_name}" __KCONFIG_PROJBUILD "${component_projbuild}")
idf_component_set_property("${component_name}" __SDKCONFIG_RENAME "${component_rename}")
# Set component project_include.cmake.
idf_component_set_property("${component_name}" __PROJECT_INCLUDE "${component_project_include}")
endfunction()
#[[
__dump_component_properties(<components>)
:components: List of components whose properties should be displayed.
Dump all properties for the components listed in ``<components>``.
#]]
function(__dump_component_properties components)
foreach(component IN LISTS components)
idf_component_get_property(properties "${component}" COMPONENT_PROPERTIES)
idf_msg("component '${component}' properties: ${properties}")
foreach(property IN LISTS properties)
idf_component_get_property(value "${component}" "${property}")
idf_msg(" ${property}: ${value}")
endforeach()
endforeach()
endfunction()
#[[api
.. cmakev2:function:: idf_component_include
.. code-block:: cmake
idf_component_include(<name>
[INTERFACE <variable>])
:name[in]: Component name.
:INTERFACE[out,opt]: Optional variable where the name of the target
interface for the included component will be stored.
This is a core function of the build system, responsible for including the
specified component, as indicated by the ``<name>`` option, into the build
process. It does this by evaluating the component through the
``add_subdirectory`` call for the component's directory. The component is
responsible for creating its own CMake target, with the target name provided
via the ``COMPONENT_TARGET`` variable. This target is linked to the
component's interface target, which is managed internally by the build
system. The component's interface target is used by other components to
establish their dependencies.
When the "INTERFACE" variable is provided, the name of the included
component interface target will be stored in it.
#]]
function(idf_component_include name)
set(options)
set(one_value INTERFACE)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
# Check that the specified component name is among the discovered and
# recognized components.
idf_build_get_property(components_discovered COMPONENTS_DISCOVERED)
if(NOT "${name}" IN_LIST components_discovered)
idf_die("Component '${name}' not found. Available components: '${components_discovered}'")
endif()
# Check if the component is already included, meaning the add_subdirectory
# has already been called for it and the component has been processed.
idf_build_get_property(components_included COMPONENTS_INCLUDED)
if("${name}" IN_LIST components_included)
idf_dbg("Component '${name}' is already included.")
# Set the parent variable ARG_INTERFACE to include the target name of
# the component interface.
if(DEFINED ARG_INTERFACE)
idf_component_get_property(component_interface "${name}" COMPONENT_INTERFACE)
set(${ARG_INTERFACE} ${component_interface} PARENT_SCOPE)
endif()
return()
endif()
idf_dbg("Including component '${name}'")
idf_component_get_property(component_name "${name}" COMPONENT_NAME)
idf_component_get_property(component_interface "${name}" COMPONENT_INTERFACE)
idf_component_get_property(component_target "${name}" COMPONENT_TARGET)
idf_component_get_property(component_directory "${name}" COMPONENT_DIR)
idf_component_get_property(component_alias "${name}" COMPONENT_ALIAS)
idf_component_get_property(component_lib "${name}" COMPONENT_LIB)
idf_component_get_property(component_version "${name}" COMPONENT_VERSION)
# The following cmakev1 backward-compatible variables are passed to the
# component's CMakeLists.txt.
set(COMPONENT_NAME ${component_name})
set(COMPONENT_DIR ${component_directory})
set(COMPONENT_ALIAS ${component_alias})
set(COMPONENT_LIB ${component_lib})
set(COMPONENT_VERSION ${component_version})
set(ESP_PLATFORM 1)
# The COMPONENT_TARGET is simply an alias for COMPONENT_LIB, which more
# accurately reflects its purpose in the cmakev2 build system. The
# component's responsibility is to create the target with the name
# specified in the COMPONENT_TARGET variable.
set(COMPONENT_TARGET ${component_target})
# To handle potential circular dependencies among components, maintain a
# record of currently evaluated components in the __DEPENDENCY_CHAIN. This
# helps in detecting and reporting circular dependencies, such as
# C1->C2->C1. In this scenario, C2 can still use the C1 interface target,
# but C1 will only be fully evaluated after C2 has been evaluated.
if("${component_name}" IN_LIST __DEPENDENCY_CHAIN)
idf_dbg("Component '${name}' in circular dependency chain '${__DEPENDENCY_CHAIN}'")
return()
endif()
list(APPEND __DEPENDENCY_CHAIN "${name}")
# Evaluate the CMakeLists.txt file of the component.
idf_build_get_property(build_dir BUILD_DIR build_dir)
add_subdirectory("${component_directory}" "${build_dir}/esp-idf/${component_name}" EXCLUDE_FROM_ALL)
# The component has been evaluated; remove it from the dependency chain.
list(POP_BACK __DEPENDENCY_CHAIN)
# Mark the component as included and add it to the COMPONENTS_INCLUDED
# build property. Note that COMPONENTS_INCLUDED is read again because it
# might have been updated further down the dependency chain.
idf_component_set_property("${component_name}" COMPONENT_INCLUDED YES)
idf_build_get_property(components_included COMPONENTS_INCLUDED)
list(APPEND components_included "${component_name}")
idf_build_set_property(COMPONENTS_INCLUDED "${components_included}")
idf_component_get_property(component_interface "${name}" COMPONENT_INTERFACE)
if(DEFINED ARG_INTERFACE)
set(${ARG_INTERFACE} ${component_interface} PARENT_SCOPE)
endif()
if(NOT TARGET "${component_target}")
# The cmakev1 system allows component to return without creating any
# cmake target. For instance, a component might not be supported for a
# given IDF_TARGET. Ideally, we should fail and abort the build process
# in such cases. However, for compatibility reasons, the absence of
# component's target is silently ignored here.
idf_dbg("Component '${name}' lacks a target")
idf_component_set_property("${component_name}" COMPONENT_REAL_TARGET NOTFOUND)
return()
endif()
# The component might use the COMPONENT_TARGET name as an alias for another
# target. In this case, although unlikely, identify the real target and
# store it in the COMPONENT_REAL_TARGET component property.
__get_real_target(TARGET "${component_target}" OUTPUT component_real_target)
idf_component_set_property("${component_name}" COMPONENT_REAL_TARGET "${component_real_target}")
# Create a component interface alias, but only after the real target is
# known, meaning the component is included. The alias has a well-defined
# name and can be used, for example, in generator expressions without
# needing to get the COMPONENT_ALIAS property.
idf_component_get_property(component_alias "${name}" COMPONENT_ALIAS)
add_library("${component_alias}" ALIAS "${component_interface}")
# Set component target type as COMPONENT_REAL_TARGET_TYPE property.
get_target_property(component_real_target_type "${component_real_target}" TYPE)
idf_component_set_property("${component_name}" COMPONENT_REAL_TARGET_TYPE "${component_real_target_type}")
# Link the target created by the component to the component interface
# target.
if("${component_real_target_type}" STREQUAL "INTERFACE_LIBRARY")
target_link_libraries("${component_interface}" INTERFACE "${component_real_target}")
elseif("${component_real_target_type}" STREQUAL "STATIC_LIBRARY")
idf_component_get_property(whole_archive "${component_name}" WHOLE_ARCHIVE)
if(whole_archive)
idf_build_get_property(linker_type LINKER_TYPE)
if(linker_type STREQUAL "GNU")
target_link_libraries("${component_interface}" INTERFACE
"-Wl,--whole-archive" "${component_real_target}" "-Wl,--no-whole-archive")
elseif(linker_type STREQUAL "Darwin")
target_link_libraries("${component_interface}" INTERFACE
"-Wl,-force_load" "${component_real_target}")
endif()
else()
target_link_libraries("${component_interface}" INTERFACE "${component_real_target}")
endif()
else()
idf_die("Unsupported target type '${component_real_target_type}' in component '${component_name}'")
endif()
idf_component_get_property(embed_files "${component_name}" EMBED_FILES)
foreach(file IN LISTS embed_files)
target_add_binary_data(${COMPONENT_TARGET} "${file}" "BINARY")
endforeach()
idf_component_get_property(embed_txtfiles "${component_name}" EMBED_TXTFILES)
foreach(file IN LISTS embed_txtfiles)
target_add_binary_data(${COMPONENT_TARGET} "${file}" "TEXT")
endforeach()
# Components for cmakev1 use the idf_component_register call and are
# managed in cmakev2 through a shim. This shim sets the COMPONENT_FORMAT
# property to CMAKEV1 and applies global compilation options and
# definitions using CMake's directory-scoped function for backward
# compatibility. Therefore, no additional configuration is required.
idf_component_get_property(component_format "${component_name}" COMPONENT_FORMAT)
if("${component_format}" STREQUAL "CMAKEV1")
return()
endif()
# The component was not handled by the idf_component_register shim,
# consider it as a component for cmakev2.
idf_component_set_property("${component_name}" COMPONENT_FORMAT CMAKEV2)
# The component's target is an interface library; nothing more needs to be
# set.
if("${component_real_target_type}" STREQUAL "INTERFACE_LIBRARY")
return()
endif()
idf_build_get_property(include_directories INCLUDE_DIRECTORIES GENERATOR_EXPRESSION)
target_include_directories("${component_real_target}" BEFORE PRIVATE "${include_directories}")
idf_build_get_property(compile_definitions COMPILE_DEFINITIONS GENERATOR_EXPRESSION)
target_compile_definitions("${component_real_target}" PRIVATE "${compile_definitions}")
__get_compile_options(OUTPUT compile_options)
target_compile_options("${component_real_target}" BEFORE PRIVATE "${compile_options}")
endfunction()