mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
153 lines
4.9 KiB
Python
153 lines
4.9 KiB
Python
# SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import typing as t
|
|
|
|
from dynamic_pipelines.constants import BINARY_SIZE_METRIC_NAME
|
|
from idf_build_apps import App
|
|
from idf_build_apps import CMakeApp
|
|
from idf_build_apps.constants import BuildStatus
|
|
from idf_build_apps.utils import rmdir
|
|
from idf_ci_utils import idf_relpath
|
|
|
|
if t.TYPE_CHECKING:
|
|
pass
|
|
|
|
|
|
class IdfCMakeApp(CMakeApp):
|
|
build_system: t.Literal['idf_cmake'] = 'idf_cmake'
|
|
|
|
def _initialize_hook(self, **kwargs: t.Any) -> None:
|
|
# ensure this env var exists
|
|
os.environ['IDF_TARGET'] = self.target
|
|
|
|
super()._initialize_hook(**kwargs)
|
|
|
|
def _post_build(self) -> None:
|
|
super()._post_build()
|
|
|
|
# only upload in CI
|
|
if os.getenv('CI_JOB_ID'):
|
|
result = subprocess.run(
|
|
[
|
|
'idf-ci',
|
|
'gitlab',
|
|
'upload-artifacts',
|
|
self.app_dir,
|
|
'--build-dir',
|
|
self.build_dir,
|
|
],
|
|
stdout=sys.stdout,
|
|
stderr=sys.stderr,
|
|
)
|
|
if result.returncode != 0:
|
|
self.build_status = BuildStatus.FAILED
|
|
self.build_comment = 'Failed to upload artifacts'
|
|
|
|
rmdir(
|
|
self.build_path,
|
|
exclude_file_patterns=['build_log.txt', 'size*.json'],
|
|
)
|
|
|
|
|
|
class Metrics:
|
|
"""
|
|
Represents a metric and its values for source, target, and the differences.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
source_value: float | None = None,
|
|
target_value: float | None = None,
|
|
difference: float | None = None,
|
|
difference_percentage: float | None = None,
|
|
) -> None:
|
|
self.source_value = source_value or 0.0
|
|
self.target_value = target_value or 0.0
|
|
self.difference = difference or 0.0
|
|
self.difference_percentage = difference_percentage or 0.0
|
|
|
|
def to_dict(self) -> dict[str, t.Any]:
|
|
"""
|
|
Converts the Metrics object to a dictionary.
|
|
"""
|
|
return {
|
|
'source_value': self.source_value,
|
|
'target_value': self.target_value,
|
|
'difference': self.difference,
|
|
'difference_percentage': self.difference_percentage,
|
|
}
|
|
|
|
|
|
class AppWithMetricsInfo(IdfCMakeApp):
|
|
metrics: dict[str, Metrics]
|
|
is_new_app: bool
|
|
|
|
def __init__(self, **kwargs: t.Any) -> None:
|
|
super().__init__(**kwargs)
|
|
|
|
self.metrics = {metric_name: metric_data for metric_name, metric_data in kwargs.get('metrics', {}).items()}
|
|
self.is_new_app = kwargs.get('is_new_app', False)
|
|
|
|
class Config:
|
|
arbitrary_types_allowed = True
|
|
|
|
|
|
def enrich_apps_with_metrics_info(
|
|
app_metrics_info_map: dict[str, dict[str, t.Any]], apps: list[App]
|
|
) -> list[AppWithMetricsInfo]:
|
|
def _get_full_attributes(obj: App) -> dict[str, t.Any]:
|
|
"""
|
|
Retrieves all attributes of an object, including properties and computed fields.
|
|
"""
|
|
attributes: dict[str, t.Any] = obj.__dict__.copy()
|
|
for attr in dir(obj):
|
|
if not attr.startswith('_'): # Skip private/internal attributes
|
|
try:
|
|
value = getattr(obj, attr)
|
|
# Include only if it's not already in __dict__
|
|
if attr not in attributes:
|
|
attributes[attr] = value
|
|
except Exception:
|
|
# Skip attributes that raise exceptions (e.g., methods needing args)
|
|
pass
|
|
return attributes
|
|
|
|
default_metrics_structure = {
|
|
BINARY_SIZE_METRIC_NAME: Metrics(
|
|
source_value=0,
|
|
target_value=0,
|
|
difference=0,
|
|
difference_percentage=0.0,
|
|
),
|
|
}
|
|
|
|
apps_with_metrics_info = []
|
|
for app in apps:
|
|
app.app_dir = idf_relpath(app.app_dir)
|
|
key = f'{app.app_dir}_{app.config_name}_{app.target}'
|
|
app_attributes = _get_full_attributes(app)
|
|
|
|
metrics = {metric_name: default_metric for metric_name, default_metric in default_metrics_structure.items()}
|
|
is_new_app = False
|
|
|
|
if key in app_metrics_info_map:
|
|
info = app_metrics_info_map[key]
|
|
for metric_name, metric_data in info.get('metrics', {}).items():
|
|
metrics[metric_name] = Metrics(
|
|
source_value=metric_data.get('source_value', 0),
|
|
target_value=metric_data.get('target_value', 0),
|
|
difference=metric_data.get('difference', 0),
|
|
difference_percentage=metric_data.get('difference_percentage', 0.0),
|
|
)
|
|
|
|
is_new_app = info.get('is_new_app', False)
|
|
|
|
app_attributes.update({'metrics': metrics, 'is_new_app': is_new_app})
|
|
|
|
apps_with_metrics_info.append(AppWithMetricsInfo(**app_attributes))
|
|
|
|
return apps_with_metrics_info
|