2024-01-18 08:13:40 +01:00
|
|
|
#!/usr/bin/env python3
|
2024-08-07 05:46:37 +02:00
|
|
|
import argparse
|
2024-01-18 08:13:40 +01:00
|
|
|
from pathlib import Path
|
|
|
|
import sys
|
|
|
|
|
2024-08-07 05:46:37 +02:00
|
|
|
from helpers import changed_files, git_ls_files
|
|
|
|
|
2024-01-18 08:13:40 +01:00
|
|
|
from esphome.const import (
|
|
|
|
KEY_CORE,
|
|
|
|
KEY_TARGET_FRAMEWORK,
|
|
|
|
KEY_TARGET_PLATFORM,
|
|
|
|
PLATFORM_ESP32,
|
|
|
|
PLATFORM_ESP8266,
|
|
|
|
)
|
2024-08-07 05:46:37 +02:00
|
|
|
from esphome.core import CORE
|
|
|
|
from esphome.loader import get_component, get_platform
|
2024-01-18 08:13:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
def filter_component_files(str):
|
|
|
|
return str.startswith("esphome/components/") | str.startswith("tests/components/")
|
|
|
|
|
|
|
|
|
|
|
|
def extract_component_names_array_from_files_array(files):
|
|
|
|
components = []
|
|
|
|
for file in files:
|
|
|
|
file_parts = file.split("/")
|
|
|
|
if len(file_parts) >= 4:
|
|
|
|
component_name = file_parts[2]
|
|
|
|
if component_name not in components:
|
|
|
|
components.append(component_name)
|
|
|
|
return components
|
|
|
|
|
|
|
|
|
|
|
|
def add_item_to_components_graph(components_graph, parent, child):
|
|
|
|
if not parent.startswith("__") and parent != child:
|
|
|
|
if parent not in components_graph:
|
|
|
|
components_graph[parent] = []
|
|
|
|
if child not in components_graph[parent]:
|
|
|
|
components_graph[parent].append(child)
|
|
|
|
|
|
|
|
|
|
|
|
def create_components_graph():
|
|
|
|
# The root directory of the repo
|
|
|
|
root = Path(__file__).parent.parent
|
|
|
|
components_dir = root / "esphome" / "components"
|
|
|
|
# Fake some directory so that get_component works
|
|
|
|
CORE.config_path = str(root)
|
|
|
|
# Various configuration to capture different outcomes used by `AUTO_LOAD` function.
|
|
|
|
TARGET_CONFIGURATIONS = [
|
|
|
|
{KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: None},
|
|
|
|
{KEY_TARGET_FRAMEWORK: "arduino", KEY_TARGET_PLATFORM: None},
|
|
|
|
{KEY_TARGET_FRAMEWORK: "esp-idf", KEY_TARGET_PLATFORM: None},
|
|
|
|
{KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP32},
|
2024-06-25 06:43:30 +02:00
|
|
|
{KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: PLATFORM_ESP8266},
|
2024-01-18 08:13:40 +01:00
|
|
|
]
|
|
|
|
CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0]
|
|
|
|
|
|
|
|
components_graph = {}
|
|
|
|
|
|
|
|
for path in components_dir.iterdir():
|
|
|
|
if not path.is_dir():
|
|
|
|
continue
|
|
|
|
if not (path / "__init__.py").is_file():
|
|
|
|
continue
|
|
|
|
name = path.name
|
|
|
|
comp = get_component(name)
|
|
|
|
if comp is None:
|
|
|
|
print(
|
|
|
|
f"Cannot find component {name}. Make sure current path is pip installed ESPHome"
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
for dependency in comp.dependencies:
|
2024-04-28 21:44:40 +02:00
|
|
|
add_item_to_components_graph(
|
|
|
|
components_graph, dependency.split(".")[0], name
|
|
|
|
)
|
2024-01-18 08:13:40 +01:00
|
|
|
|
|
|
|
for target_config in TARGET_CONFIGURATIONS:
|
|
|
|
CORE.data[KEY_CORE] = target_config
|
|
|
|
for auto_load in comp.auto_load:
|
|
|
|
add_item_to_components_graph(components_graph, auto_load, name)
|
|
|
|
# restore config
|
|
|
|
CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0]
|
|
|
|
|
|
|
|
for platform_path in path.iterdir():
|
|
|
|
platform_name = platform_path.stem
|
|
|
|
platform = get_platform(platform_name, name)
|
|
|
|
if platform is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
add_item_to_components_graph(components_graph, platform_name, name)
|
|
|
|
|
|
|
|
for dependency in platform.dependencies:
|
2024-04-28 21:44:40 +02:00
|
|
|
add_item_to_components_graph(
|
|
|
|
components_graph, dependency.split(".")[0], name
|
|
|
|
)
|
2024-01-18 08:13:40 +01:00
|
|
|
|
|
|
|
for target_config in TARGET_CONFIGURATIONS:
|
|
|
|
CORE.data[KEY_CORE] = target_config
|
|
|
|
for auto_load in platform.auto_load:
|
|
|
|
add_item_to_components_graph(components_graph, auto_load, name)
|
|
|
|
# restore config
|
|
|
|
CORE.data[KEY_CORE] = TARGET_CONFIGURATIONS[0]
|
|
|
|
|
|
|
|
return components_graph
|
|
|
|
|
|
|
|
|
|
|
|
def find_children_of_component(components_graph, component_name, depth=0):
|
|
|
|
if component_name not in components_graph:
|
|
|
|
return []
|
|
|
|
|
|
|
|
children = []
|
|
|
|
|
|
|
|
for child in components_graph[component_name]:
|
|
|
|
children.append(child)
|
|
|
|
if depth < 10:
|
|
|
|
children.extend(
|
|
|
|
find_children_of_component(components_graph, child, depth + 1)
|
|
|
|
)
|
|
|
|
# Remove duplicate values
|
|
|
|
return list(set(children))
|
|
|
|
|
|
|
|
|
2024-06-25 06:43:30 +02:00
|
|
|
def get_components(files: list[str], get_dependencies: bool = False):
|
|
|
|
components = extract_component_names_array_from_files_array(files)
|
|
|
|
|
|
|
|
if get_dependencies:
|
|
|
|
components_graph = create_components_graph()
|
|
|
|
|
|
|
|
all_components = components.copy()
|
|
|
|
for c in components:
|
|
|
|
all_components.extend(find_children_of_component(components_graph, c))
|
|
|
|
# Remove duplicate values
|
|
|
|
all_changed_components = list(set(all_components))
|
|
|
|
|
|
|
|
return sorted(all_changed_components)
|
|
|
|
|
|
|
|
return sorted(components)
|
|
|
|
|
|
|
|
|
2024-01-18 08:13:40 +01:00
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument(
|
2024-07-02 04:06:33 +02:00
|
|
|
"-c",
|
|
|
|
"--changed",
|
|
|
|
action="store_true",
|
|
|
|
help="List all components required for testing based on changes",
|
2024-01-18 08:13:40 +01:00
|
|
|
)
|
2024-03-16 02:22:34 +01:00
|
|
|
parser.add_argument(
|
|
|
|
"-b", "--branch", help="Branch to compare changed files against"
|
|
|
|
)
|
2024-01-18 08:13:40 +01:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2024-03-16 02:22:34 +01:00
|
|
|
if args.branch and not args.changed:
|
|
|
|
parser.error("--branch requires --changed")
|
|
|
|
|
2024-01-18 08:13:40 +01:00
|
|
|
files = git_ls_files()
|
|
|
|
files = filter(filter_component_files, files)
|
|
|
|
|
|
|
|
if args.changed:
|
2024-03-16 02:22:34 +01:00
|
|
|
if args.branch:
|
|
|
|
changed = changed_files(args.branch)
|
|
|
|
else:
|
|
|
|
changed = changed_files()
|
2024-07-02 04:06:33 +02:00
|
|
|
# If any base test file(s) changed, there's no need to filter out components
|
|
|
|
if not any("tests/test_build_components" in file for file in changed):
|
|
|
|
files = [f for f in files if f in changed]
|
2024-01-18 08:13:40 +01:00
|
|
|
|
2024-06-25 06:43:30 +02:00
|
|
|
for c in get_components(files, args.changed):
|
|
|
|
print(c)
|
2024-01-18 08:13:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|