diff --git a/tools/test_apps/system/g1_components/CMakeLists.txt b/tools/test_apps/system/g1_components/CMakeLists.txt index 81555b42e5..eeaabadbaf 100644 --- a/tools/test_apps/system/g1_components/CMakeLists.txt +++ b/tools/test_apps/system/g1_components/CMakeLists.txt @@ -110,3 +110,16 @@ if(NOT "${expected_components}" STREQUAL "${build_components}") "Expected: ${expected_components}. " "Actual: ${build_components}") endif() + +set(comp_deps_dot "${CMAKE_BINARY_DIR}/component_deps.dot") + +execute_process( + COMMAND ${CMAKE_COMMAND} -E echo "Checking dependency violations" + COMMAND python "${CMAKE_SOURCE_DIR}/check_dependencies.py" --component_deps_file ${comp_deps_dot} + RESULT_VARIABLE result +) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "Found unexpected componend dependencies while running check_dependencies.py, " + "please update the dependency list in the script according to the error output.") +endif() diff --git a/tools/test_apps/system/g1_components/README.md b/tools/test_apps/system/g1_components/README.md index 261c59f5cc..a57b58b036 100644 --- a/tools/test_apps/system/g1_components/README.md +++ b/tools/test_apps/system/g1_components/README.md @@ -16,6 +16,9 @@ Once all the unwanted dependencies of G1 components are removed, the `extra_comp Please note, if an extra component B is already added to the build as a dependency of component A (A -> B), the build of this example will not fail when a new dependency onto the same component is added (C -> B). It will only fail when a new component is added to the build. Diff-ing `build/component_deps.dot` (see below) against a known-good one could be an alternative, but it will likely be quite fragile. + +In addition the script `check_dependencies.py` will also check that no G1 components get new dependencies to components that do not belong in this group. If an extra component B is already added to the build as a dependency of component A (A -> B), the build of this script will fail when a new dependency onto the same component is added (C -> B). + # Using this test app To check G1 components using this app, run: @@ -39,6 +42,22 @@ it means that the list of extra components added to G1 build has changed compare Please try very hard not to increase the number of components in `extra_components_which_shouldnt_be_included`. +If you get the error: +``` +Found the following new dependency violations: +[' -> '] + +``` +it means that a new dependency to an extra component that was already in the build was added. If this is unavoidable then you can update the list of `expected_dep_violations` in the script to include this new dependency. + +If you get the error: +``` +The following dependencies are list as violations, but were not found in the component_deps.dot file: +{'': ['']} +``` +Then it means a previous extra component dependency no longer exists, and we should remove it from the list `expected_dep_violations` in the script. + + # Component dependencies graph (`component_deps.dot`) When this project is configured, `component_deps.dot` file in the build directory is generated. This file contains a Graphviz graph showing the component dependencies. You can visualize this graph (using `dot` tool or online at https://dreampuf.github.io/GraphvizOnline/) to see why an extra component got added. You can also build the project for the base branch, to compare the graph to a known good one. diff --git a/tools/test_apps/system/g1_components/check_dependencies.py b/tools/test_apps/system/g1_components/check_dependencies.py new file mode 100644 index 0000000000..aebaf4b8c0 --- /dev/null +++ b/tools/test_apps/system/g1_components/check_dependencies.py @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import argparse +import logging +from typing import Dict +from typing import List +from typing import Tuple + +g1_g0_components = ['hal', 'cxx', 'newlib', 'freertos', 'esp_hw_support', 'heap', 'log', 'soc', 'esp_rom', + 'esp_common', 'esp_system', 'xtensa', 'riscv', 'spi_flash', 'esp_mm'] + +expected_dep_violations = {'esp_system': ['esp_timer', 'bootloader_support', 'esp_pm'], + 'spi_flash': ['bootloader_support', 'app_update', 'esp_driver_gpio'], + 'esp_hw_support': ['efuse', 'bootloader_support', 'esp_driver_gpio', 'esp_timer', 'esp_pm'], + 'cxx': ['pthread']} + + +def parse_dependencies(file_path: str) -> Tuple[Dict[str, List[str]], List[str]]: + new_dependency_errors = [] + + with open(file_path, 'r') as file: + for line in file: + line = line.strip(' ;') + + if line: + parts = line.split(' -> ') + + if (len(parts) >= 2): + source = parts[0] + target = parts[1].split()[0] # Extracting the target component + logging.debug(f'Parsed dependency: {source} -> {target}') + + # Check that g1/g0 dependencies are either on the list of expected violations + # or dependencies to other g1/g0 components + if source in g1_g0_components and target not in g1_g0_components: + if target in expected_dep_violations[source]: + logging.debug(f'Removing dependency {target} from {source} in list of expected violations') + expected_dep_violations[source].remove(target) + else: + new_dependency_errors.append(f'{source} -> {target}') + + # Any leftover dependencies in the expected_dep_violations are no longer true dependencies and + # can be removed from the list + false_dependencies = {k: v for k, v in expected_dep_violations.items() if len(v) > 0} + + return (false_dependencies, new_dependency_errors) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Check G1 dependencies') + parser.add_argument('--component_deps_file', required=True, type=str, help='The path to the component_deps.dot file') + + args = parser.parse_args() + + (false_dependencies, new_dependency_errors) = parse_dependencies(args.component_deps_file) + + if new_dependency_errors: + print('Found the following new dependency violations:') + print(new_dependency_errors) + exit(1) + + if false_dependencies: + print('The following dependencies are list as violations, but were not found in the component_deps.dot file:') + print(false_dependencies) + print('Please remove them from the violation list') + exit(1) + + print('No new dependency violations found')