Merge branch 'feat/core_components_v5.3' into 'release/v5.3'

feat: add `COMPONENT_SOURCE` property to component targets (v5.3)

See merge request espressif/esp-idf!34184
This commit is contained in:
Sergei Silnov
2025-01-21 21:50:18 +08:00
9 changed files with 254 additions and 42 deletions

View File

@ -294,7 +294,14 @@ When CMake runs to configure the project, it logs the components included in the
Multiple Components with the Same Name Multiple Components with the Same Name
-------------------------------------- --------------------------------------
When ESP-IDF is collecting all the components to compile, it will do this in the order specified by ``COMPONENT_DIRS``; by default, this means ESP-IDF's internal components first (``IDF_PATH/components``), then any components in directories specified in ``EXTRA_COMPONENT_DIRS``, and finally the project's components (``PROJECT_DIR/components``). If two or more of these directories contain component sub-directories with the same name, the component in the last place searched is used. This allows, for example, overriding ESP-IDF components with a modified version by copying that component from the ESP-IDF components directory to the project components directory and then modifying it there. If used in this way, the ESP-IDF directory itself can remain untouched. When ESP-IDF is collecting all the components to compile, the search precedence is as follows (from highest to lowest):
* Project components
* Components from ``EXTRA_COMPONENT_DIRS``
* Project managed components, downloaded by the IDF Component Manager into ``PROJECT_DIR/managed_components``, unless the IDF Component Manager is disabled.
* ESP-IDF components (``IDF_PATH/components``)
If two or more of these directories contain component sub-directories with the same name, the component with higher precedence is used. This allows, for example, overriding ESP-IDF components with a modified version by copying that component from the ESP-IDF components directory to the project components directory and then modifying it there. If used in this way, the ESP-IDF directory itself can remain untouched.
.. note:: .. note::
@ -1243,12 +1250,26 @@ Set a :ref:`build property <cmake-build-properties>` *property* with value *val*
.. code-block:: none .. code-block:: none
idf_build_component(component_dir) idf_build_component(component_dir [component_source])
Present a directory *component_dir* that contains a component to the build system. Relative paths are converted to absolute paths with respect to current directory. Present a directory *component_dir* that contains a component to the build system. Relative paths are converted to absolute paths with respect to current directory.
All calls to this command must be performed before `idf_build_process`.
This command does not guarantee that the component will be processed during build (see the `COMPONENTS` argument description for `idf_build_process`) An optional *component_source* argument can be specified to indicate the source of the component. (default: "project_components")
This argument determines the overriding priority for components with the same name. For detailed information, see :ref:`cmake-components-same-name`.
This argument supports the following values (from highest to lowest priority):
- "project_components" - project components
- "project_extra_components" - components from ``EXTRA_COMPONENT_DIRS``
- "project_managed_components" - custom project dependencies managed by the IDF Component Manager
- "idf_components" - ESP-IDF built-in components, typically under :idf:`/components`
For instance, if a component named "json" is present as both "idf_components", and "project_components", the component as "project_components" takes precedence over the one as "idf_components".
.. warning::
All calls to this command must be performed before `idf_build_process`. This command does not guarantee that the component will be processed during build (see the `COMPONENTS` argument description for `idf_build_process`).
.. code-block:: none .. code-block:: none
@ -1408,6 +1429,7 @@ These are properties that describe a component. Values of component properties c
- COMPONENT_LIB - name for created component static/interface library; set by ``idf_build_component`` and library itself is created by ``idf_component_register`` - COMPONENT_LIB - name for created component static/interface library; set by ``idf_build_component`` and library itself is created by ``idf_component_register``
- COMPONENT_NAME - name of the component; set by ``idf_build_component`` based on the component directory name - COMPONENT_NAME - name of the component; set by ``idf_build_component`` based on the component directory name
- COMPONENT_TYPE - type of the component, whether LIBRARY or CONFIG_ONLY. A component is of type LIBRARY if it specifies source files or embeds a file - COMPONENT_TYPE - type of the component, whether LIBRARY or CONFIG_ONLY. A component is of type LIBRARY if it specifies source files or embeds a file
- COMPONENT_SOURCE - source of the component, one of "idf_components", "project_managed_components", "project_components", "project_extra_components". This is used to determine the override precedence of components with the same name.
- EMBED_FILES - list of files to embed in component; set from ``idf_component_register`` EMBED_FILES argument - EMBED_FILES - list of files to embed in component; set from ``idf_component_register`` EMBED_FILES argument
- EMBED_TXTFILES - list of text files to embed in component; set from ``idf_component_register`` EMBED_TXTFILES argument - EMBED_TXTFILES - list of text files to embed in component; set from ``idf_component_register`` EMBED_TXTFILES argument
- INCLUDE_DIRS - list of component include directories; set from ``idf_component_register`` INCLUDE_DIRS argument - INCLUDE_DIRS - list of component include directories; set from ``idf_component_register`` INCLUDE_DIRS argument

View File

@ -294,9 +294,16 @@ ESP-IDF 适用于 Python 3.8 以上版本。
同名组件 同名组件
-------- --------
ESP-IDF 在搜索所有待构建的组件时,会按照 ``COMPONENT_DIRS`` 指定的顺序依次进行,这意味着在默认情况下,首先搜索 ESP-IDF 内部组件(``IDF_PATH/components``),然后是 ``EXTRA_COMPONENT_DIRS`` 中的组件,最后是项目组件(``PROJECT_DIR/components``)。如果这些目录中的两个或者多个包含具有相同名字的组件,则使用搜索到的最后一个位置的组件。这就允许将组件复制到项目目录中再修改以覆盖 ESP-IDF 组件如果使用这种方式ESP-IDF 目录本身可以保持不变。 ESP-IDF 在搜索所有待构建的组件时,会按照以下优先级搜索组件目录(从高到低):
.. 注解:: * 项目目录下的组件
* ``EXTRA_COMPONENT_DIRS`` 中的组件
* 项目目录下 ``managed_components`` 目录中的组件。这些组件由 IDF Component Manager 下载并管理。(除非 IDF Component Manager 被禁用)
* ``IDF_PATH/components`` 目录下的组件
如果有两个及以上同名组件,构建系统会使用优先级更高的组件。这使得我们可以在项目中覆盖 ESP-IDF 提供的组件。只需要复制 ESP-IDF 组件到项目目录下,然后修改它。这样可以在修改组件的同时,不修改 ESP-IDF 的源代码。
.. note::
如果在现有项目中通过将组件移动到一个新位置来覆盖它,项目不会自动看到新组件的路径。请运行 ``idf.py reconfigure`` 命令后(或删除项目构建文件夹)再重新构建。 如果在现有项目中通过将组件移动到一个新位置来覆盖它,项目不会自动看到新组件的路径。请运行 ``idf.py reconfigure`` 命令后(或删除项目构建文件夹)再重新构建。
@ -1243,23 +1250,37 @@ ESP-IDF 构建命令
.. code-block:: none .. code-block:: none
idf_build_component(component_dir) idf_build_component(component_dir [component_source])
向构建系统提交一个包含组件的 *component_dir* 目录。相对路径会被转换为相对于当前目录的绝对路径。 向构建系统提交一个包含组件的 *component_dir* 目录。相对路径会被转换为相对于当前目录的绝对路径。
所有对该命令的调用必须在`idf_build_process`之前执行。
该命令并不保证组件在构建过程中会被处理(参见 `idf_build_process``COMPONENTS` 参数说明 一个可选的 *component_source* 参数可以用于指定组件源。(默认为 "project_components"
这个参数决定了同名组件的优先级。详细信息请参考 :ref:`cmake-components-same-name`
该参数可以指定如下组件源(优先级从高到低排序):
- "project_components" - 项目目录中的组件
- "project_extra_components" - 通过 ``EXTRA_COMPONENT_DIRS`` 指定的额外组件
- "project_managed_components" - 通过 IDF Component Manager 管理的组件
- "idf_components" - ESP-IDF 中的组件,通常在 :idf:`/components` 目录中
举个例子,如果有两个组件,组件名都为 "json"。一个组件源被定义为 "project_components",另一个组件源被定义为 "idf_components",那么 "project_components" 中的 "json" 组件会被优先选择。
.. warning::
所有对该命令的调用必须在 `idf_build_process` 之前执行。该命令并不保证组件在构建过程中会被处理(参见 `idf_build_process``COMPONENTS` 参数说明)。
.. code-block:: none .. code-block:: none
idf_build_process(target idf_build_process(target
[PROJECT_DIR project_dir] [PROJECT_DIR project_dir]
[PROJECT_VER project_ver] [PROJECT_VER project_ver]
[PROJECT_NAME project_name] [PROJECT_NAME project_name]
[SDKCONFIG sdkconfig] [SDKCONFIG sdkconfig]
[SDKCONFIG_DEFAULTS sdkconfig_defaults] [SDKCONFIG_DEFAULTS sdkconfig_defaults]
[BUILD_DIR build_dir] [BUILD_DIR build_dir]
[COMPONENTS component1 component2 ...]) [COMPONENTS component1 component2 ...])
为导入 ESP-IDF 组件执行大量的幕后工作,包括组件配置、库创建、依赖性扩展和解析。在这些功能中,对于用户最重要的可能是通过调用每个组件的 ``idf_component_register`` 来创建库。该命令为每个组件创建库,这些库可以使用别名来访问,其形式为 idf::*component_name* 为导入 ESP-IDF 组件执行大量的幕后工作,包括组件配置、库创建、依赖性扩展和解析。在这些功能中,对于用户最重要的可能是通过调用每个组件的 ``idf_component_register`` 来创建库。该命令为每个组件创建库,这些库可以使用别名来访问,其形式为 idf::*component_name*
这些别名可以用来将组件链接到用户自己的目标、库或可执行文件上。 这些别名可以用来将组件链接到用户自己的目标、库或可执行文件上。
@ -1408,6 +1429,7 @@ ESP-IDF 组件属性
- COMPONENT_LIB - 所创建的组件静态/接口库的名称;由 ``idf_build_component`` 设置,库本身由 ``idf_component_register`` 创建。 - COMPONENT_LIB - 所创建的组件静态/接口库的名称;由 ``idf_build_component`` 设置,库本身由 ``idf_component_register`` 创建。
- COMPONENT_NAME - 组件的名称;由 ``idf_build_component`` 根据组件的目录名设置。 - COMPONENT_NAME - 组件的名称;由 ``idf_build_component`` 根据组件的目录名设置。
- COMPONENT_TYPE - 组件的类型LIBRARY 或 CONFIG_ONLY。如果一个组件指定了源文件或嵌入了一个文件那么它的类型就是 LIBRARY。 - COMPONENT_TYPE - 组件的类型LIBRARY 或 CONFIG_ONLY。如果一个组件指定了源文件或嵌入了一个文件那么它的类型就是 LIBRARY。
- COMPONENT_SOURCE - 组件源。可选值为 "idf_components""project_managed_components""project_components""project_extra_components". 用于决定同名组件的优先级。
- EMBED_FILES - 要嵌入组件的文件列表;由 ``idf_component_register`` EMBED_FILES 参数设置。 - EMBED_FILES - 要嵌入组件的文件列表;由 ``idf_component_register`` EMBED_FILES 参数设置。
- EMBED_TXTFILES - 要嵌入组件的文本文件列表;由 ``idf_component_register`` EMBED_TXTFILES 参数设置。 - EMBED_TXTFILES - 要嵌入组件的文本文件列表;由 ``idf_component_register`` EMBED_TXTFILES 参数设置。
- INCLUDE_DIRS - 组件 include 目录列表;由 ``idf_component_register`` INCLUDE_DIRS 参数设置。 - INCLUDE_DIRS - 组件 include 目录列表;由 ``idf_component_register`` INCLUDE_DIRS 参数设置。

View File

@ -223,7 +223,7 @@ function(__build_init idf_path)
if(IS_DIRECTORY ${component_dir}) if(IS_DIRECTORY ${component_dir})
__component_dir_quick_check(is_component ${component_dir}) __component_dir_quick_check(is_component ${component_dir})
if(is_component) if(is_component)
__component_add(${component_dir} ${prefix}) __component_add(${component_dir} ${prefix} "idf_components")
endif() endif()
endif() endif()
endforeach() endforeach()
@ -253,9 +253,29 @@ endfunction()
# during build (see the COMPONENTS argument description for command idf_build_process) # during build (see the COMPONENTS argument description for command idf_build_process)
# #
# @param[in] component_dir directory of the component # @param[in] component_dir directory of the component
# @param[in, optional] component_source source of the component, defaults to "project_components"
function(idf_build_component component_dir) function(idf_build_component component_dir)
idf_build_get_property(prefix __PREFIX) idf_build_get_property(prefix __PREFIX)
__component_add(${component_dir} ${prefix} 0)
# if argvc is 1, then component_source is not specified
# this should only happen when users call this function directly
if(${ARGC} EQUAL 1)
set(component_source "project_components")
else()
set(component_source ${ARGV1})
endif()
# component_source must be one of the following (sorted by the override order):
set(valid_component_sources "idf_components"
"project_managed_components"
"project_extra_components"
"project_components")
if(NOT component_source IN_LIST valid_component_sources)
message(FATAL_ERROR "Invalid component source '${component_source}'.")
endif()
__component_add(${component_dir} ${prefix} ${component_source})
endfunction() endfunction()
# #
@ -449,7 +469,7 @@ endfunction()
# if PROJECT_DIR is set and CMAKE_SOURCE_DIR/sdkconfig if not # if PROJECT_DIR is set and CMAKE_SOURCE_DIR/sdkconfig if not
# @param[in, optional] SDKCONFIG_DEFAULTS (single value) config defaults file to use for the build; defaults # @param[in, optional] SDKCONFIG_DEFAULTS (single value) config defaults file to use for the build; defaults
# to none (Kconfig defaults or previously generated config are used) # to none (Kconfig defaults or previously generated config are used)
# @param[in, optional] BUILD_DIR (single value) directory for build artifacts; defautls to CMAKE_BINARY_DIR # @param[in, optional] BUILD_DIR (single value) directory for build artifacts; defaults to CMAKE_BINARY_DIR
# @param[in, optional] COMPONENTS (multivalue) select components to process among the components # @param[in, optional] COMPONENTS (multivalue) select components to process among the components
# known by the build system # known by the build system
# (added via `idf_build_component`). This argument is used to trim the build. # (added via `idf_build_component`). This argument is used to trim the build.

View File

@ -44,7 +44,7 @@ function(__component_get_target var name_or_alias)
idf_build_get_property(component_targets __COMPONENT_TARGETS) idf_build_get_property(component_targets __COMPONENT_TARGETS)
# Assume first that the paramters is an alias. # Assume first that the parameters is an alias.
string(REPLACE "::" "_" name_or_alias "${name_or_alias}") string(REPLACE "::" "_" name_or_alias "${name_or_alias}")
set(component_target ___${name_or_alias}) set(component_target ___${name_or_alias})
@ -138,7 +138,7 @@ endfunction()
# Add a component to process in the build. The components are keeped tracked of in property # Add a component to process in the build. The components are keeped tracked of in property
# __COMPONENT_TARGETS in component target form. # __COMPONENT_TARGETS in component target form.
# #
function(__component_add component_dir prefix) function(__component_add component_dir prefix component_source)
# For each component, two entities are created: a component target and a component library. The # For each component, two entities are created: a component target and a component library. The
# component library is created during component registration (the actual static/interface library). # component library is created during component registration (the actual static/interface library).
# On the other hand, component targets are created early in the build # On the other hand, component targets are created early in the build
@ -186,6 +186,9 @@ function(__component_add component_dir prefix)
__component_set_property(${component_target} COMPONENT_NAME ${component_name}) __component_set_property(${component_target} COMPONENT_NAME ${component_name})
__component_set_property(${component_target} COMPONENT_DIR ${component_dir}) __component_set_property(${component_target} COMPONENT_DIR ${component_dir})
__component_set_property(${component_target} COMPONENT_ALIAS ${component_alias}) __component_set_property(${component_target} COMPONENT_ALIAS ${component_alias})
if(component_source)
__component_set_property(${component_target} COMPONENT_SOURCE ${component_source})
endif()
__component_set_property(${component_target} __PREFIX ${prefix}) __component_set_property(${component_target} __PREFIX ${prefix})

View File

@ -61,7 +61,7 @@ if(NOT "$ENV{IDF_COMPONENT_MANAGER}" EQUAL "0")
idf_build_set_property(IDF_COMPONENT_MANAGER 1) idf_build_set_property(IDF_COMPONENT_MANAGER 1)
endif() endif()
# Set component manager interface version # Set component manager interface version
idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 2) idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 3)
# #
# Parse and store the VERSION argument provided to the project() command. # Parse and store the VERSION argument provided to the project() command.
@ -427,11 +427,11 @@ function(__project_init components_var test_components_var)
idf_build_set_property(CXX_COMPILE_OPTIONS "${extra_cxxflags}" APPEND) idf_build_set_property(CXX_COMPILE_OPTIONS "${extra_cxxflags}" APPEND)
idf_build_set_property(COMPILE_OPTIONS "${extra_cppflags}" APPEND) idf_build_set_property(COMPILE_OPTIONS "${extra_cppflags}" APPEND)
function(__project_component_dir component_dir) function(__project_component_dir component_dir component_source)
get_filename_component(component_dir "${component_dir}" ABSOLUTE) get_filename_component(component_dir "${component_dir}" ABSOLUTE)
# The directory itself is a valid idf component # The directory itself is a valid idf component
if(EXISTS ${component_dir}/CMakeLists.txt) if(EXISTS ${component_dir}/CMakeLists.txt)
idf_build_component(${component_dir}) idf_build_component(${component_dir} ${component_source})
else() else()
idf_build_get_property(exclude_dirs EXTRA_COMPONENT_EXCLUDE_DIRS) idf_build_get_property(exclude_dirs EXTRA_COMPONENT_EXCLUDE_DIRS)
# otherwise, check whether the subfolders are potential idf components # otherwise, check whether the subfolders are potential idf components
@ -440,7 +440,7 @@ function(__project_init components_var test_components_var)
if(IS_DIRECTORY ${component_dir} AND NOT ${component_dir} IN_LIST exclude_dirs) if(IS_DIRECTORY ${component_dir} AND NOT ${component_dir} IN_LIST exclude_dirs)
__component_dir_quick_check(is_component ${component_dir}) __component_dir_quick_check(is_component ${component_dir})
if(is_component) if(is_component)
idf_build_component(${component_dir}) idf_build_component(${component_dir} ${component_source})
endif() endif()
endif() endif()
endforeach() endforeach()
@ -458,11 +458,11 @@ function(__project_init components_var test_components_var)
if(NOT EXISTS ${component_abs_path}) if(NOT EXISTS ${component_abs_path})
message(FATAL_ERROR "Directory specified in COMPONENT_DIRS doesn't exist: ${component_abs_path}") message(FATAL_ERROR "Directory specified in COMPONENT_DIRS doesn't exist: ${component_abs_path}")
endif() endif()
__project_component_dir(${component_dir}) __project_component_dir(${component_dir} "project_components")
endforeach() endforeach()
else() else()
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/main") if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/main")
__project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main") __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main" "project_components")
endif() endif()
paths_with_spaces_to_list(EXTRA_COMPONENT_DIRS) paths_with_spaces_to_list(EXTRA_COMPONENT_DIRS)
@ -471,12 +471,12 @@ function(__project_init components_var test_components_var)
if(NOT EXISTS ${component_abs_path}) if(NOT EXISTS ${component_abs_path})
message(FATAL_ERROR "Directory specified in EXTRA_COMPONENT_DIRS doesn't exist: ${component_abs_path}") message(FATAL_ERROR "Directory specified in EXTRA_COMPONENT_DIRS doesn't exist: ${component_abs_path}")
endif() endif()
__project_component_dir("${component_dir}") __project_component_dir("${component_dir}" "project_extra_components")
endforeach() endforeach()
# Look for components in the usual places: CMAKE_CURRENT_LIST_DIR/main, # Look for components in the usual places: CMAKE_CURRENT_LIST_DIR/main,
# extra component dirs, and CMAKE_CURRENT_LIST_DIR/components # extra component dirs, and CMAKE_CURRENT_LIST_DIR/components
__project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components") __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components" "project_components")
endif() endif()
# For bootloader components, we only need to set-up the Kconfig files. # For bootloader components, we only need to set-up the Kconfig files.
@ -528,7 +528,7 @@ function(__project_init components_var test_components_var)
set(include 0) set(include 0)
endif() endif()
if(include AND EXISTS ${component_dir}/test) if(include AND EXISTS ${component_dir}/test)
__component_add(${component_dir}/test ${component_name}) __component_add(${component_dir}/test ${component_name} "project_components")
list(APPEND test_components ${component_name}::test) list(APPEND test_components ${component_name}::test)
endif() endif()
endif() endif()

View File

@ -27,7 +27,7 @@ endfunction()
function(__component_get_target var name_or_alias) function(__component_get_target var name_or_alias)
idf_build_get_property(component_targets __COMPONENT_TARGETS) idf_build_get_property(component_targets __COMPONENT_TARGETS)
# Assume first that the paramters is an alias. # Assume first that the parameters is an alias.
string(REPLACE "::" "_" name_or_alias "${name_or_alias}") string(REPLACE "::" "_" name_or_alias "${name_or_alias}")
set(component_target ___${name_or_alias}) set(component_target ___${name_or_alias})
@ -119,7 +119,40 @@ function(__component_get_requirements)
endfunction() endfunction()
set(CMAKE_BUILD_EARLY_EXPANSION 1) set(CMAKE_BUILD_EARLY_EXPANSION 1)
# smaller number means lower priority
set(__TARGETS_IDF_COMPONENTS "") # 0
set(__TARGETS_PROJECT_MANAGED_COMPONENTS "") # 1
set(__TARGETS_PROJECT_EXTRA_COMPONENTS "") # 2
set(__TARGETS_PROJECT_COMPONENTS "") # 3
foreach(__component_target ${__component_targets}) foreach(__component_target ${__component_targets})
__component_get_property(__component_source ${__component_target} COMPONENT_SOURCE)
if("${__component_source}" STREQUAL "idf_components")
list(APPEND __TARGETS_IDF_COMPONENTS ${__component_target})
elseif("${__component_source}" STREQUAL "project_managed_components")
list(APPEND __TARGETS_PROJECT_MANAGED_COMPONENTS ${__component_target})
elseif("${__component_source}" STREQUAL "project_extra_components")
list(APPEND __TARGETS_PROJECT_EXTRA_COMPONENTS ${__component_target})
elseif("${__component_source}" STREQUAL "project_components")
list(APPEND __TARGETS_PROJECT_COMPONENTS ${__component_target})
else()
message(FATAL_ERROR "Unknown component source for component ${__component_target}: ${__component_source}")
endif()
endforeach()
# priority higher ones goes first
set(__sorted_component_targets "")
foreach(__target IN LISTS __TARGETS_PROJECT_COMPONENTS
__TARGETS_PROJECT_EXTRA_COMPONENTS
__TARGETS_PROJECT_MANAGED_COMPONENTS
__TARGETS_IDF_COMPONENTS)
__component_get_property(__component_name ${__target} COMPONENT_NAME)
list(APPEND __sorted_component_targets ${__target})
endforeach()
foreach(__component_target ${__sorted_component_targets})
set(__component_requires "") set(__component_requires "")
set(__component_priv_requires "") set(__component_priv_requires "")
set(__component_registered 0) set(__component_registered 0)
@ -141,11 +174,14 @@ foreach(__component_target ${__component_targets})
list(REMOVE_ITEM __component_priv_requires ${__component_alias} ${__component_name}) list(REMOVE_ITEM __component_priv_requires ${__component_alias} ${__component_name})
endif() endif()
__component_get_property(__component_source ${__component_target} COMPONENT_SOURCE)
set(__contents set(__contents
"__component_set_property(${__component_target} REQUIRES \"${__component_requires}\") "__component_set_property(${__component_target} REQUIRES \"${__component_requires}\")
__component_set_property(${__component_target} PRIV_REQUIRES \"${__component_priv_requires}\") __component_set_property(${__component_target} PRIV_REQUIRES \"${__component_priv_requires}\")
__component_set_property(${__component_target} __COMPONENT_REGISTERED ${__component_registered}) __component_set_property(${__component_target} __COMPONENT_REGISTERED ${__component_registered})
__component_set_property(${__component_target} INCLUDE_DIRS \"${__component_include_dirs}\")" __component_set_property(${__component_target} INCLUDE_DIRS \"${__component_include_dirs}\")
__component_set_property(${__component_target} __COMPONENT_SOURCE \"${__component_source}\")"
) )
if(__component_kconfig) if(__component_kconfig)

View File

@ -51,7 +51,7 @@ def _session_work_dir(request: FixtureRequest) -> typing.Generator[typing.Tuple[
work_dir = request.config.getoption('--work-dir') work_dir = request.config.getoption('--work-dir')
if work_dir: if work_dir:
work_dir = os.path.join(work_dir, datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S')) work_dir = os.path.join(work_dir, datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d_%H-%M-%S'))
logging.debug(f'using work directory: {work_dir}') logging.debug(f'using work directory: {work_dir}')
os.makedirs(work_dir, exist_ok=True) os.makedirs(work_dir, exist_ok=True)
clean_dir = None clean_dir = None

View File

@ -17,3 +17,4 @@ markers =
test_app_copy: specify relative path of the app to copy, and the prefix of the destination directory name test_app_copy: specify relative path of the app to copy, and the prefix of the destination directory name
idf_copy: specify the prefix of the destination directory where IDF should be copied idf_copy: specify the prefix of the destination directory where IDF should be copied
force_temp_work_dir: force temporary folder as the working directory force_temp_work_dir: force temporary folder as the working directory
with_idf_components: automatically create/delete components under IDF_PATH

View File

@ -2,8 +2,10 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
import json import json
import logging import logging
import os
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import Generator
import pytest import pytest
from test_build_system_helpers import append_to_file from test_build_system_helpers import append_to_file
@ -12,6 +14,31 @@ from test_build_system_helpers import IdfPyFunc
from test_build_system_helpers import replace_in_file from test_build_system_helpers import replace_in_file
@pytest.fixture(autouse=True)
def create_idf_components(request: pytest.FixtureRequest) -> Generator:
"""
Auto create/remove components under IDF_PATH.
Use it by applying the marker to the test function:
>>> @pytest.mark.with_idf_components(['cmp1', 'cmp2', ...])
"""
mark = request.node.get_closest_marker('with_idf_components')
if mark is None:
yield
else:
idf_path = Path(os.environ['IDF_PATH'])
created_paths = []
for name in mark.args[0]:
(idf_path / 'components' / name).mkdir(parents=True)
(idf_path / 'components' / name / 'CMakeLists.txt').write_text('idf_component_register()')
created_paths.append(str((idf_path / 'components' / name).as_posix()))
yield
for path in created_paths:
shutil.rmtree(path)
def test_component_extra_dirs(idf_py: IdfPyFunc, test_app_copy: Path) -> None: def test_component_extra_dirs(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
logging.info('Setting EXTRA_COMPONENT_DIRS works') logging.info('Setting EXTRA_COMPONENT_DIRS works')
shutil.move(test_app_copy / 'main', test_app_copy / 'different_main' / 'main') shutil.move(test_app_copy / 'main', test_app_copy / 'different_main' / 'main')
@ -88,7 +115,7 @@ def test_component_properties_are_set(idf_py: IdfPyFunc, test_app_copy: Path) ->
def test_component_overriden_dir(idf_py: IdfPyFunc, test_app_copy: Path, default_idf_env: EnvDict) -> None: def test_component_overriden_dir(idf_py: IdfPyFunc, test_app_copy: Path, default_idf_env: EnvDict) -> None:
logging.info('Getting component overriden dir') logging.info('Getting component overridden dir')
(test_app_copy / 'components' / 'hal').mkdir(parents=True) (test_app_copy / 'components' / 'hal').mkdir(parents=True)
(test_app_copy / 'components' / 'hal' / 'CMakeLists.txt').write_text('\n'.join([ (test_app_copy / 'components' / 'hal' / 'CMakeLists.txt').write_text('\n'.join([
'idf_component_get_property(overriden_dir ${COMPONENT_NAME} COMPONENT_OVERRIDEN_DIR)', 'idf_component_get_property(overriden_dir ${COMPONENT_NAME} COMPONENT_OVERRIDEN_DIR)',
@ -96,7 +123,7 @@ def test_component_overriden_dir(idf_py: IdfPyFunc, test_app_copy: Path, default
ret = idf_py('reconfigure') ret = idf_py('reconfigure')
idf_path = Path(default_idf_env.get('IDF_PATH')) idf_path = Path(default_idf_env.get('IDF_PATH'))
# no registration, overrides registration as well # no registration, overrides registration as well
assert 'overriden_dir:{}'.format((idf_path / 'components' / 'hal').as_posix()) in ret.stdout, 'Failed to get overriden dir' assert 'overriden_dir:{}'.format((idf_path / 'components' / 'hal').as_posix()) in ret.stdout, 'Failed to get overridden dir'
append_to_file((test_app_copy / 'components' / 'hal' / 'CMakeLists.txt'), '\n'.join([ append_to_file((test_app_copy / 'components' / 'hal' / 'CMakeLists.txt'), '\n'.join([
'', '',
'idf_component_register(KCONFIG ${overriden_dir}/Kconfig)', 'idf_component_register(KCONFIG ${overriden_dir}/Kconfig)',
@ -106,19 +133,100 @@ def test_component_overriden_dir(idf_py: IdfPyFunc, test_app_copy: Path, default
assert 'kconfig:{}'.format((idf_path / 'components' / 'hal').as_posix()) in ret.stdout, 'Failed to verify original `main` directory' assert 'kconfig:{}'.format((idf_path / 'components' / 'hal').as_posix()) in ret.stdout, 'Failed to verify original `main` directory'
def test_components_prioritizer_over_extra_components_dir(idf_py: IdfPyFunc, test_app_copy: Path) -> None: def test_project_components_overrides_extra_components(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
logging.info('Project components prioritized over EXTRA_COMPONENT_DIRS') logging.info('Project components override components defined in EXTRA_COMPONENT_DIRS')
(test_app_copy / 'extra_dir' / 'my_component').mkdir(parents=True) (test_app_copy / 'extra_dir' / 'my_component').mkdir(parents=True)
(test_app_copy / 'extra_dir' / 'my_component' / 'CMakeLists.txt').write_text('idf_component_register()') (test_app_copy / 'extra_dir' / 'my_component' / 'CMakeLists.txt').write_text('idf_component_register()')
replace_in_file(test_app_copy / 'CMakeLists.txt', replace_in_file(test_app_copy / 'CMakeLists.txt',
'# placeholder_before_include_project_cmake', '# placeholder_before_include_project_cmake',
'set(EXTRA_COMPONENT_DIRS extra_dir)') 'set(EXTRA_COMPONENT_DIRS extra_dir)')
ret = idf_py('reconfigure') idf_py('reconfigure')
assert str((test_app_copy / 'extra_dir' / 'my_component').as_posix()) in ret.stdout, 'Unable to find component specified in EXTRA_COMPONENT_DIRS' with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / 'extra_dir' / 'my_component').as_posix()) in data.get('build_component_paths')
(test_app_copy / 'components' / 'my_component').mkdir(parents=True) (test_app_copy / 'components' / 'my_component').mkdir(parents=True)
(test_app_copy / 'components' / 'my_component' / 'CMakeLists.txt').write_text('idf_component_register()') (test_app_copy / 'components' / 'my_component' / 'CMakeLists.txt').write_text('idf_component_register()')
ret = idf_py('reconfigure') idf_py('reconfigure')
assert str((test_app_copy / 'components' / 'my_component').as_posix()) in ret.stdout, 'Project components should be prioritized over EXTRA_COMPONENT_DIRS' with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / 'components' / 'my_component').as_posix()) in data.get('build_component_paths')
assert str((test_app_copy / 'extra_dir' / 'my_component').as_posix()) not in data.get('build_component_paths')
def test_extra_components_overrides_managed_components(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
logging.info('components defined in EXTRA_COMPONENT_DIRS override managed components')
(test_app_copy / 'main' / 'idf_component.yml').write_text("""
dependencies:
example/cmp: "*"
""")
idf_py('reconfigure')
with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / 'managed_components' / 'example__cmp').as_posix()) in data.get(
'build_component_paths')
(test_app_copy / 'extra_dir' / 'cmp').mkdir(parents=True)
(test_app_copy / 'extra_dir' / 'cmp' / 'CMakeLists.txt').write_text('idf_component_register()')
replace_in_file(test_app_copy / 'CMakeLists.txt',
'# placeholder_before_include_project_cmake',
'set(EXTRA_COMPONENT_DIRS extra_dir)')
idf_py('reconfigure')
with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / 'extra_dir' / 'cmp').as_posix()) in data.get('build_component_paths')
assert str((test_app_copy / 'managed_components' / 'example__cmp').as_posix()) not in data.get(
'build_component_paths')
@pytest.mark.with_idf_components(['cmp'])
def test_managed_components_overrides_idf_components(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
logging.info('Managed components override components defined in IDF_PATH/components')
# created idf component 'cmp' in marker
idf_path = Path(os.environ['IDF_PATH'])
idf_py('reconfigure')
with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((idf_path / 'components' / 'cmp').as_posix()) in data.get(
'build_component_paths')
(test_app_copy / 'main' / 'idf_component.yml').write_text("""
dependencies:
example/cmp: "*"
""")
idf_py('reconfigure')
with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / 'managed_components' / 'example__cmp').as_posix()) in data.get(
'build_component_paths')
assert str((idf_path / 'components' / 'cmp').as_posix()) not in data.get(
'build_component_paths')
def test_manifest_local_source_overrides_extra_components(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
(test_app_copy / '..' / 'extra_dir' / 'cmp').mkdir(parents=True)
(test_app_copy / '..' / 'extra_dir' / 'cmp' / 'CMakeLists.txt').write_text('idf_component_register()')
replace_in_file(test_app_copy / 'CMakeLists.txt',
'# placeholder_before_include_project_cmake',
'set(EXTRA_COMPONENT_DIRS ../extra_dir)')
idf_py('reconfigure')
with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / '..' / 'extra_dir' / 'cmp').resolve().as_posix()) in data.get('build_component_paths')
(test_app_copy / '..' / 'cmp').mkdir(parents=True)
(test_app_copy / '..' / 'cmp' / 'CMakeLists.txt').write_text('idf_component_register()')
with open(test_app_copy / 'main' / 'idf_component.yml', 'w') as f:
f.write("""
dependencies:
cmp:
path: '../../cmp'
""")
idf_py('reconfigure')
with open(test_app_copy / 'build' / 'project_description.json', 'r') as f:
data = json.load(f)
assert str((test_app_copy / '..' / 'cmp').resolve().as_posix()) in data.get('build_component_paths')
assert str((test_app_copy / '..' / 'extra_dir' / 'cmp').resolve().as_posix()) not in data.get('build_component_paths')
def test_exclude_components_not_passed(idf_py: IdfPyFunc, test_app_copy: Path) -> None: def test_exclude_components_not_passed(idf_py: IdfPyFunc, test_app_copy: Path) -> None: