Skip to content

Commit 4b3a5e9

Browse files
committed
Merge branch 'feat/core_components' into 'master'
feat: add `COMPONENT_SOURCE` property to component targets Closes DOC-7429 See merge request espressif/esp-idf!26586
2 parents 0db772e + 6ff7947 commit 4b3a5e9

File tree

9 files changed

+250
-38
lines changed

9 files changed

+250
-38
lines changed

docs/en/api-guides/build-system.rst

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,14 @@ When CMake runs to configure the project, it logs the components included in the
307307
Multiple Components with the Same Name
308308
--------------------------------------
309309

310-
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.
310+
When ESP-IDF is collecting all the components to compile, the search precedence is as follows (from highest to lowest):
311+
312+
* Project components
313+
* Components from ``EXTRA_COMPONENT_DIRS``
314+
* Project managed components, downloaded by the IDF Component Manager into ``PROJECT_DIR/managed_components``, unless the IDF Component Manager is disabled.
315+
* ESP-IDF components (``IDF_PATH/components``)
316+
317+
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.
311318

312319
.. note::
313320

@@ -1257,12 +1264,26 @@ Set a :ref:`build property <cmake-build-properties>` *property* with value *val*
12571264

12581265
.. code-block:: none
12591266
1260-
idf_build_component(component_dir)
1267+
idf_build_component(component_dir [component_source])
12611268
12621269
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.
1263-
All calls to this command must be performed before `idf_build_process`.
12641270

1265-
This command does not guarantee that the component will be processed during build (see the `COMPONENTS` argument description for `idf_build_process`)
1271+
An optional *component_source* argument can be specified to indicate the source of the component. (default: "project_components")
1272+
1273+
This argument determines the overriding priority for components with the same name. For detailed information, see :ref:`cmake-components-same-name`.
1274+
1275+
This argument supports the following values (from highest to lowest priority):
1276+
1277+
- "project_components" - project components
1278+
- "project_extra_components" - components from ``EXTRA_COMPONENT_DIRS``
1279+
- "project_managed_components" - custom project dependencies managed by the IDF Component Manager
1280+
- "idf_components" - ESP-IDF built-in components, typically under :idf:`/components`
1281+
1282+
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".
1283+
1284+
.. warning::
1285+
1286+
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`).
12661287

12671288
.. code-block:: none
12681289
@@ -1422,6 +1443,7 @@ These are properties that describe a component. Values of component properties c
14221443
- COMPONENT_LIB - name for created component static/interface library; set by ``idf_build_component`` and library itself is created by ``idf_component_register``
14231444
- COMPONENT_NAME - name of the component; set by ``idf_build_component`` based on the component directory name
14241445
- 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
1446+
- 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.
14251447
- EMBED_FILES - list of files to embed in component; set from ``idf_component_register`` EMBED_FILES argument
14261448
- EMBED_TXTFILES - list of text files to embed in component; set from ``idf_component_register`` EMBED_TXTFILES argument
14271449
- INCLUDE_DIRS - list of component include directories; set from ``idf_component_register`` INCLUDE_DIRS argument

docs/zh_CN/api-guides/build-system.rst

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,16 @@ ESP-IDF 适用于 Python 3.8 以上版本。
307307
同名组件
308308
--------
309309

310-
ESP-IDF 在搜索所有待构建的组件时,会按照 ``COMPONENT_DIRS`` 指定的顺序依次进行,这意味着在默认情况下,首先搜索 ESP-IDF 内部组件(``IDF_PATH/components``),然后是 ``EXTRA_COMPONENT_DIRS`` 中的组件,最后是项目组件(``PROJECT_DIR/components``)。如果这些目录中的两个或者多个包含具有相同名字的组件,则使用搜索到的最后一个位置的组件。这就允许将组件复制到项目目录中再修改以覆盖 ESP-IDF 组件,如果使用这种方式,ESP-IDF 目录本身可以保持不变。
310+
ESP-IDF 在搜索所有待构建的组件时,会按照以下优先级搜索组件目录(从高到低):
311311

312-
.. 注解::
312+
* 项目目录下的组件
313+
* ``EXTRA_COMPONENT_DIRS`` 中的组件
314+
* 项目目录下 ``managed_components`` 目录中的组件。这些组件由 IDF Component Manager 下载并管理。(除非 IDF Component Manager 被禁用)
315+
* ``IDF_PATH/components`` 目录下的组件
316+
317+
如果有两个及以上同名组件,构建系统会使用优先级更高的组件。这使得我们可以在项目中覆盖 ESP-IDF 提供的组件。只需要复制 ESP-IDF 组件到项目目录下,然后修改它。这样可以在修改组件的同时,不修改 ESP-IDF 的源代码。
318+
319+
.. note::
313320

314321
如果在现有项目中通过将组件移动到一个新位置来覆盖它,项目不会自动看到新组件的路径。请运行 ``idf.py reconfigure`` 命令后(或删除项目构建文件夹)再重新构建。
315322

@@ -1257,23 +1264,37 @@ ESP-IDF 构建命令
12571264

12581265
.. code-block:: none
12591266
1260-
idf_build_component(component_dir)
1267+
idf_build_component(component_dir [component_source])
12611268
12621269
向构建系统提交一个包含组件的 *component_dir* 目录。相对路径会被转换为相对于当前目录的绝对路径。
1263-
所有对该命令的调用必须在`idf_build_process`之前执行。
12641270

1265-
该命令并不保证组件在构建过程中会被处理(参见 `idf_build_process` 中 `COMPONENTS` 参数说明)
1271+
一个可选的 *component_source* 参数可以用于指定组件源。(默认为 "project_components")
1272+
1273+
这个参数决定了同名组件的优先级。详细信息请参考 :ref:`cmake-components-same-name`。
1274+
1275+
该参数可以指定如下组件源(优先级从高到低排序):
1276+
1277+
- "project_components" - 项目目录中的组件
1278+
- "project_extra_components" - 通过 ``EXTRA_COMPONENT_DIRS`` 指定的额外组件
1279+
- "project_managed_components" - 通过 IDF Component Manager 管理的组件
1280+
- "idf_components" - ESP-IDF 中的组件,通常在 :idf:`/components` 目录中
1281+
1282+
举个例子,如果有两个组件,组件名都为 "json"。一个组件源被定义为 "project_components",另一个组件源被定义为 "idf_components",那么 "project_components" 中的 "json" 组件会被优先选择。
1283+
1284+
.. warning::
1285+
1286+
所有对该命令的调用必须在 `idf_build_process` 之前执行。该命令并不保证组件在构建过程中会被处理(参见 `idf_build_process` 中 `COMPONENTS` 参数说明)。
12661287

12671288
.. code-block:: none
12681289
1269-
idf_build_process(target
1270-
[PROJECT_DIR project_dir]
1271-
[PROJECT_VER project_ver]
1272-
[PROJECT_NAME project_name]
1273-
[SDKCONFIG sdkconfig]
1274-
[SDKCONFIG_DEFAULTS sdkconfig_defaults]
1275-
[BUILD_DIR build_dir]
1276-
[COMPONENTS component1 component2 ...])
1290+
idf_build_process(target
1291+
[PROJECT_DIR project_dir]
1292+
[PROJECT_VER project_ver]
1293+
[PROJECT_NAME project_name]
1294+
[SDKCONFIG sdkconfig]
1295+
[SDKCONFIG_DEFAULTS sdkconfig_defaults]
1296+
[BUILD_DIR build_dir]
1297+
[COMPONENTS component1 component2 ...])
12771298
12781299
为导入 ESP-IDF 组件执行大量的幕后工作,包括组件配置、库创建、依赖性扩展和解析。在这些功能中,对于用户最重要的可能是通过调用每个组件的 ``idf_component_register`` 来创建库。该命令为每个组件创建库,这些库可以使用别名来访问,其形式为 idf::*component_name*。
12791300
这些别名可以用来将组件链接到用户自己的目标、库或可执行文件上。
@@ -1422,6 +1443,7 @@ ESP-IDF 组件属性
14221443
- COMPONENT_LIB - 所创建的组件静态/接口库的名称;由 ``idf_build_component`` 设置,库本身由 ``idf_component_register`` 创建。
14231444
- COMPONENT_NAME - 组件的名称;由 ``idf_build_component`` 根据组件的目录名设置。
14241445
- COMPONENT_TYPE - 组件的类型(LIBRARY 或 CONFIG_ONLY)。如果一个组件指定了源文件或嵌入了一个文件,那么它的类型就是 LIBRARY。
1446+
- COMPONENT_SOURCE - 组件源。可选值为 "idf_components","project_managed_components","project_components","project_extra_components". 用于决定同名组件的优先级。
14251447
- EMBED_FILES - 要嵌入组件的文件列表;由 ``idf_component_register`` EMBED_FILES 参数设置。
14261448
- EMBED_TXTFILES - 要嵌入组件的文本文件列表;由 ``idf_component_register`` EMBED_TXTFILES 参数设置。
14271449
- INCLUDE_DIRS - 组件 include 目录列表;由 ``idf_component_register`` INCLUDE_DIRS 参数设置。

tools/cmake/build.cmake

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ function(__build_init idf_path)
253253
if(IS_DIRECTORY ${component_dir})
254254
__component_dir_quick_check(is_component ${component_dir})
255255
if(is_component)
256-
__component_add(${component_dir} ${prefix})
256+
__component_add(${component_dir} ${prefix} "idf_components")
257257
endif()
258258
endif()
259259
endforeach()
@@ -283,9 +283,29 @@ endfunction()
283283
# during build (see the COMPONENTS argument description for command idf_build_process)
284284
#
285285
# @param[in] component_dir directory of the component
286+
# @param[in, optional] component_source source of the component, defaults to "project_components"
286287
function(idf_build_component component_dir)
287288
idf_build_get_property(prefix __PREFIX)
288-
__component_add(${component_dir} ${prefix} 0)
289+
290+
# if argvc is 1, then component_source is not specified
291+
# this should only happen when users call this function directly
292+
if(${ARGC} EQUAL 1)
293+
set(component_source "project_components")
294+
else()
295+
set(component_source ${ARGV1})
296+
endif()
297+
298+
# component_source must be one of the following (sorted by the override order):
299+
set(valid_component_sources "idf_components"
300+
"project_managed_components"
301+
"project_extra_components"
302+
"project_components")
303+
304+
if(NOT component_source IN_LIST valid_component_sources)
305+
message(FATAL_ERROR "Invalid component source '${component_source}'.")
306+
endif()
307+
308+
__component_add(${component_dir} ${prefix} ${component_source})
289309
endfunction()
290310

291311
#

tools/cmake/component.cmake

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ endfunction()
138138
# Add a component to process in the build. The components are keeped tracked of in property
139139
# __COMPONENT_TARGETS in component target form.
140140
#
141-
function(__component_add component_dir prefix)
141+
function(__component_add component_dir prefix component_source)
142142
# For each component, two entities are created: a component target and a component library. The
143143
# component library is created during component registration (the actual static/interface library).
144144
# On the other hand, component targets are created early in the build
@@ -186,6 +186,9 @@ function(__component_add component_dir prefix)
186186
__component_set_property(${component_target} COMPONENT_NAME ${component_name})
187187
__component_set_property(${component_target} COMPONENT_DIR ${component_dir})
188188
__component_set_property(${component_target} COMPONENT_ALIAS ${component_alias})
189+
if(component_source)
190+
__component_set_property(${component_target} COMPONENT_SOURCE ${component_source})
191+
endif()
189192

190193
__component_set_property(${component_target} __PREFIX ${prefix})
191194

tools/cmake/project.cmake

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ if(NOT "$ENV{IDF_COMPONENT_MANAGER}" EQUAL "0")
6161
idf_build_set_property(IDF_COMPONENT_MANAGER 1)
6262
endif()
6363
# Set component manager interface version
64-
idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 2)
64+
idf_build_set_property(__COMPONENT_MANAGER_INTERFACE_VERSION 3)
6565

6666
#
6767
# Parse and store the VERSION argument provided to the project() command.
@@ -420,11 +420,11 @@ function(__project_init components_var test_components_var)
420420
idf_build_set_property(CXX_COMPILE_OPTIONS "${extra_cxxflags}" APPEND)
421421
idf_build_set_property(COMPILE_OPTIONS "${extra_cppflags}" APPEND)
422422

423-
function(__project_component_dir component_dir)
423+
function(__project_component_dir component_dir component_source)
424424
get_filename_component(component_dir "${component_dir}" ABSOLUTE)
425425
# The directory itself is a valid idf component
426426
if(EXISTS ${component_dir}/CMakeLists.txt)
427-
idf_build_component(${component_dir})
427+
idf_build_component(${component_dir} ${component_source})
428428
else()
429429
idf_build_get_property(exclude_dirs EXTRA_COMPONENT_EXCLUDE_DIRS)
430430
# otherwise, check whether the subfolders are potential idf components
@@ -433,7 +433,7 @@ function(__project_init components_var test_components_var)
433433
if(IS_DIRECTORY ${component_dir} AND NOT ${component_dir} IN_LIST exclude_dirs)
434434
__component_dir_quick_check(is_component ${component_dir})
435435
if(is_component)
436-
idf_build_component(${component_dir})
436+
idf_build_component(${component_dir} ${component_source})
437437
endif()
438438
endif()
439439
endforeach()
@@ -451,11 +451,11 @@ function(__project_init components_var test_components_var)
451451
if(NOT EXISTS ${component_abs_path})
452452
message(FATAL_ERROR "Directory specified in COMPONENT_DIRS doesn't exist: ${component_abs_path}")
453453
endif()
454-
__project_component_dir(${component_dir})
454+
__project_component_dir(${component_dir} "project_components")
455455
endforeach()
456456
else()
457457
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/main")
458-
__project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main")
458+
__project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main" "project_components")
459459
endif()
460460

461461
paths_with_spaces_to_list(EXTRA_COMPONENT_DIRS)
@@ -464,12 +464,12 @@ function(__project_init components_var test_components_var)
464464
if(NOT EXISTS ${component_abs_path})
465465
message(FATAL_ERROR "Directory specified in EXTRA_COMPONENT_DIRS doesn't exist: ${component_abs_path}")
466466
endif()
467-
__project_component_dir("${component_dir}")
467+
__project_component_dir("${component_dir}" "project_extra_components")
468468
endforeach()
469469

470470
# Look for components in the usual places: CMAKE_CURRENT_LIST_DIR/main,
471471
# extra component dirs, and CMAKE_CURRENT_LIST_DIR/components
472-
__project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components")
472+
__project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components" "project_components")
473473
endif()
474474

475475
# For bootloader components, we only need to set-up the Kconfig files.
@@ -521,7 +521,7 @@ function(__project_init components_var test_components_var)
521521
set(include 0)
522522
endif()
523523
if(include AND EXISTS ${component_dir}/test)
524-
__component_add(${component_dir}/test ${component_name})
524+
__component_add(${component_dir}/test ${component_name} "project_components")
525525
list(APPEND test_components ${component_name}::test)
526526
endif()
527527
endif()

tools/cmake/scripts/component_get_requirements.cmake

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ endfunction()
2727
function(__component_get_target var name_or_alias)
2828
idf_build_get_property(component_targets __COMPONENT_TARGETS)
2929

30-
# Assume first that the paramters is an alias.
30+
# Assume first that the parameters is an alias.
3131
string(REPLACE "::" "_" name_or_alias "${name_or_alias}")
3232
set(component_target ___${name_or_alias})
3333

@@ -119,7 +119,40 @@ function(__component_get_requirements)
119119
endfunction()
120120

121121
set(CMAKE_BUILD_EARLY_EXPANSION 1)
122+
123+
# smaller number means lower priority
124+
set(__TARGETS_IDF_COMPONENTS "") # 0
125+
set(__TARGETS_PROJECT_MANAGED_COMPONENTS "") # 1
126+
set(__TARGETS_PROJECT_EXTRA_COMPONENTS "") # 2
127+
set(__TARGETS_PROJECT_COMPONENTS "") # 3
128+
122129
foreach(__component_target ${__component_targets})
130+
__component_get_property(__component_source ${__component_target} COMPONENT_SOURCE)
131+
132+
if("${__component_source}" STREQUAL "idf_components")
133+
list(APPEND __TARGETS_IDF_COMPONENTS ${__component_target})
134+
elseif("${__component_source}" STREQUAL "project_managed_components")
135+
list(APPEND __TARGETS_PROJECT_MANAGED_COMPONENTS ${__component_target})
136+
elseif("${__component_source}" STREQUAL "project_extra_components")
137+
list(APPEND __TARGETS_PROJECT_EXTRA_COMPONENTS ${__component_target})
138+
elseif("${__component_source}" STREQUAL "project_components")
139+
list(APPEND __TARGETS_PROJECT_COMPONENTS ${__component_target})
140+
else()
141+
message(FATAL_ERROR "Unknown component source for component ${__component_target}: ${__component_source}")
142+
endif()
143+
endforeach()
144+
145+
# priority higher ones goes first
146+
set(__sorted_component_targets "")
147+
foreach(__target IN LISTS __TARGETS_PROJECT_COMPONENTS
148+
__TARGETS_PROJECT_EXTRA_COMPONENTS
149+
__TARGETS_PROJECT_MANAGED_COMPONENTS
150+
__TARGETS_IDF_COMPONENTS)
151+
__component_get_property(__component_name ${__target} COMPONENT_NAME)
152+
list(APPEND __sorted_component_targets ${__target})
153+
endforeach()
154+
155+
foreach(__component_target ${__sorted_component_targets})
123156
set(__component_requires "")
124157
set(__component_priv_requires "")
125158
set(__component_registered 0)
@@ -141,11 +174,14 @@ foreach(__component_target ${__component_targets})
141174
list(REMOVE_ITEM __component_priv_requires ${__component_alias} ${__component_name})
142175
endif()
143176

177+
__component_get_property(__component_source ${__component_target} COMPONENT_SOURCE)
178+
144179
set(__contents
145180
"__component_set_property(${__component_target} REQUIRES \"${__component_requires}\")
146181
__component_set_property(${__component_target} PRIV_REQUIRES \"${__component_priv_requires}\")
147182
__component_set_property(${__component_target} __COMPONENT_REGISTERED ${__component_registered})
148-
__component_set_property(${__component_target} INCLUDE_DIRS \"${__component_include_dirs}\")"
183+
__component_set_property(${__component_target} INCLUDE_DIRS \"${__component_include_dirs}\")
184+
__component_set_property(${__component_target} __COMPONENT_SOURCE \"${__component_source}\")"
149185
)
150186

151187
if(__component_kconfig)

tools/test_build_system/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _session_work_dir(request: FixtureRequest) -> typing.Generator[typing.Tuple[
5151
work_dir = request.config.getoption('--work-dir')
5252

5353
if work_dir:
54-
work_dir = os.path.join(work_dir, datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S'))
54+
work_dir = os.path.join(work_dir, datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d_%H-%M-%S'))
5555
logging.debug(f'using work directory: {work_dir}')
5656
os.makedirs(work_dir, exist_ok=True)
5757
clean_dir = None

0 commit comments

Comments
 (0)