Merge branch 'fix/cmakev2_component_srcs_and_validation_v6.0' into 'release/v6.0'

Correctly set SRCS property for cmakev2 components and add component validation checks (v6.0)

See merge request espressif/esp-idf!44428
This commit is contained in:
Jiang Jiang Jian
2025-12-21 15:38:55 +08:00
4 changed files with 178 additions and 5 deletions
+4
View File
@@ -6,6 +6,7 @@ include_guard(GLOBAL)
include(utilities)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(component_validation)
#[[api
.. cmakev2:function:: idf_build_set_property
@@ -501,6 +502,9 @@ function(idf_build_library library)
set_property(TARGET "${library}" APPEND PROPERTY INTERFACE_LINK_DEPENDS "${script}")
endforeach()
endforeach()
# Validate components linked to this library
__component_validation_run_checks(LIBRARY "${library}")
endfunction()
#[[
+1 -1
View File
@@ -709,7 +709,7 @@ function(__set_component_cmakev1_properties component_name)
get_target_property(component_sources "${component_real_target}" SOURCES)
if(component_sources)
__get_absolute_paths(PATHS "${component_sources}" BASE_DIR "${component_dir}" OUTPUT sources)
idf_component_set_property("${component_name}" SOURCES "${sources}")
idf_component_set_property("${component_name}" SRCS "${sources}")
idf_component_set_property("${component_name}" COMPONENT_TYPE LIBRARY)
else()
idf_component_set_property("${component_name}" COMPONENT_TYPE CONFIG_ONLY)
+173
View File
@@ -0,0 +1,173 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
# Component validation checks
#
# This module contains checks that validate component source files and include directories
# to ensure they belong to the correct component. These checks run after all components
# have been discovered and processed.
#[[
__component_validation_get_component_for_path(<var> <path>)
*var[out]*
Output variable to store the component name.
*path[in]*
File or directory path to check.
Determine which component a given path belongs to. Returns the component name in ``var``.
#]]
function(__component_validation_get_component_for_path var path)
# Determine the starting directory to check: use the path itself if it's a directory,
# otherwise use its containing directory
set(current_dir "${path}")
if(NOT IS_DIRECTORY "${current_dir}")
get_filename_component(current_dir "${path}" DIRECTORY)
endif()
# Get all component names
idf_build_get_property(component_names COMPONENTS_DISCOVERED)
# Walk up the directory tree from the deepest path towards root and return
# the first component whose COMPONENT_DIR matches exactly. This guarantees
# selecting the deepest matching component without extra heuristics.
while(NOT "${current_dir}" STREQUAL "" AND
NOT "${current_dir}" STREQUAL "/" AND
NOT "${current_dir}" MATCHES "^[A-Za-z]:/$")
foreach(component_name ${component_names})
idf_component_get_property(component_dir ${component_name} COMPONENT_DIR)
if(current_dir STREQUAL component_dir)
set(${var} ${component_name} PARENT_SCOPE)
return()
endif()
endforeach()
get_filename_component(current_dir "${current_dir}" DIRECTORY)
endwhile()
# If no component found, return empty
set(${var} "" PARENT_SCOPE)
endfunction()
#[[
__component_validation_check_sources(<component_name>)
*component_name[in]*
Component name to validate.
Validate that all source files of the specified component belong to that
component's directory tree. Issues warnings if source files belong to other
components.
#]]
function(__component_validation_check_sources component_name)
idf_component_get_property(sources ${component_name} SRCS)
# Skip validation if component does not have any sources
if(NOT sources)
return()
endif()
foreach(src ${sources})
# Check if this source file belongs to another component
__component_validation_get_component_for_path(owner_component ${src})
if(owner_component AND NOT owner_component STREQUAL component_name)
idf_warn(
"Source file '${src}' belongs to component ${owner_component} but is being built by "
"component ${component_name}. It is recommended to build source files by "
"defining component dependencies for ${component_name} "
"via using idf_component_register(REQUIRES ${owner_component}) "
"or idf_component_register(PRIV_REQUIRES ${owner_component}) in the CMakeLists.txt of "
"${component_name}.")
endif()
endforeach()
endfunction()
#[[
__component_validation_check_include_dirs(<component_name>)
*component_name[in]*
Component name to validate.
Validate that all include directories (INCLUDE_DIRS and PRIV_INCLUDE_DIRS
properties) of the specified component belong to that component's directory
tree. Issues warnings if include directories belong to other components.
#]]
function(__component_validation_check_include_dirs component_name)
idf_component_get_property(include_dirs ${component_name} INCLUDE_DIRS)
idf_component_get_property(priv_include_dirs ${component_name} PRIV_INCLUDE_DIRS)
idf_component_get_property(component_dir ${component_name} COMPONENT_DIR)
# Check public include directories
foreach(dir ${include_dirs})
# Check if this include directory belongs to another component
# Normalize to absolute path relative to this component directory
get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir})
__component_validation_get_component_for_path(owner_component ${abs_dir})
if(owner_component AND NOT owner_component STREQUAL component_name)
idf_warn(
"Include directory '${abs_dir}' belongs to component ${owner_component} but is being "
"used by component ${component_name}. It is recommended to define the "
"component dependency for '${component_name}' on the component ${owner_component}, "
"i.e. 'idf_component_register(... REQUIRES ${owner_component})' in the "
"CMakeLists.txt of ${component_name}, and specify the included directory "
"as idf_component_register(... INCLUDE_DIRS <dir relative to component>) "
"in the CMakeLists.txt of component ${owner_component}.")
endif()
endforeach()
# Check private include directories
foreach(dir ${priv_include_dirs})
# Check if this include directory belongs to another component
# Normalize to absolute path relative to this component directory
get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir})
__component_validation_get_component_for_path(owner_component ${abs_dir})
if(owner_component AND NOT owner_component STREQUAL component_name)
idf_warn(
"Private include directory '${abs_dir}' belongs to component ${owner_component} but "
"is being used by component ${component_name}. "
"It is recommended to define the component dependency for ${component_name} "
"on the component ${owner_component}, "
"i.e. 'idf_component_register(... PRIV_REQUIRES ${owner_component})' in the "
"CMakeLists.txt of ${component_name}, "
"and specify the included directory as "
"idf_component_register(... PRIV_INCLUDE_DIRS <dir relative to component>) "
"in the CMakeLists.txt of component ${owner_component}.")
endif()
endforeach()
endfunction()
#[[
__component_validation_run_checks(LIBRARY <library>)
*library[in]*
Library name for which to run validation checks.
Run validation checks for all components linked to the specified library.
#]]
function(__component_validation_run_checks)
set(options)
set(one_value LIBRARY)
set(multi_value)
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
if(NOT DEFINED ARG_LIBRARY)
idf_die("LIBRARY option is required for __component_validation_run_checks")
endif()
# Validate only components linked to the specified library
idf_library_get_property(component_names "${ARG_LIBRARY}" LIBRARY_COMPONENTS_LINKED)
# Run validation checks for each component
foreach(component_name ${component_names})
__component_validation_check_sources(${component_name})
__component_validation_check_include_dirs(${component_name})
endforeach()
endfunction()
@@ -336,7 +336,6 @@ def test_unknown_component_error(idf_py: IdfPyFunc, test_app_copy: Path) -> None
assert "Failed to resolve component 'unknown'" in ret.stderr
@pytest.mark.buildv2_skip('not yet implemented in cmakev2')
def test_component_with_improper_dependency(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
# test for __component_validation_check_include_dirs and __component_validation_check_sources
# Checks that the following warnings are produced:
@@ -377,7 +376,6 @@ def test_component_with_improper_dependency(idf_py: IdfPyFunc, test_app_copy: Pa
assert re_source.search(ret.stderr) is not None, f'Expected source file warning not found in: {ret.stderr}'
@pytest.mark.buildv2_skip('not yet implemented in cmakev2')
def test_component_validation_not_run_in_subprojects(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
# test that component validation doesn't run in subprojects like bootloader
logging.info('Check that component validation warnings are not shown in subprojects')
@@ -419,7 +417,6 @@ def test_component_validation_not_run_in_subprojects(idf_py: IdfPyFunc, test_app
assert ret.returncode == 0, 'Build should complete successfully with validation warnings'
@pytest.mark.buildv2_skip('not yet implemented in cmakev2')
def test_component_validation_private_include_dirs(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
# test that component validation works for private include directories
logging.info('Check that component validation warnings are shown for private include directories')
@@ -450,7 +447,6 @@ def test_component_validation_private_include_dirs(idf_py: IdfPyFunc, test_app_c
)
@pytest.mark.buildv2_skip('not yet implemented in cmakev2')
def test_component_validation_finds_right_component(idf_py: IdfPyFunc, test_app_copy: Path) -> None:
# test that __component_validation_get_component_for_path finds the correct component for a given path
#