Skip to content

Commit

Permalink
feat: support plugin system
Browse files Browse the repository at this point in the history
  • Loading branch information
hfudev committed Dec 27, 2023
1 parent a7a37e3 commit a8f4537
Show file tree
Hide file tree
Showing 5 changed files with 474 additions and 234 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ repos:
- types-PyYAML
- types-toml
- pytest
- pluggy
17 changes: 17 additions & 0 deletions idf_build_apps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@

SESSION_ARGS = SessionArgs()

import pluggy

from .plugin import (
IdfBuildAppsPlugin,
IdfBuildAppsSpec,
idf_build_apps_hookimpl,
idf_build_apps_hookspec,
)

IdfBuildApps = pluggy.PluginManager(__package__)
IdfBuildApps.add_hookspecs(IdfBuildAppsSpec)
IdfBuildApps.register(IdfBuildAppsPlugin())


from .app import (
App,
AppDeserializer,
Expand All @@ -39,4 +53,7 @@
'build_apps',
'setup_logging',
'SESSION_ARGS',
'IdfBuildApps',
'idf_build_apps_hookspec',
'idf_build_apps_hookimpl',
]
237 changes: 3 additions & 234 deletions idf_build_apps/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,17 @@
# SPDX-License-Identifier: Apache-2.0

import argparse
import json
import logging
import os
import re
import shutil
import sys
import textwrap
import typing as t

from . import (
SESSION_ARGS,
)
from .app import (
App,
CMakeApp,
MakeApp,
)
from .build_apps_args import (
BuildAppsArgs,
IdfBuildApps,
)
from .config import (
get_valid_config,
Expand All @@ -29,59 +21,17 @@
ALL_TARGETS,
BuildStatus,
)
from .finder import (
_find_apps,
)
from .junit import (
TestCase,
TestReport,
TestSuite,
)
from .log import (
setup_logging,
)
from .manifest.manifest import (
FolderRule,
Manifest,
)
from .utils import (
InvalidCommand,
files_matches_patterns,
get_parallel_start_stop,
semicolon_separated_str_to_list,
to_absolute_path,
to_list,
)

LOGGER = logging.getLogger(__name__)


def _check_app_dependency(
manifest_rootpath: t.Optional[str] = None,
modified_components: t.Optional[t.List[str]] = None,
modified_files: t.Optional[t.List[str]] = None,
ignore_app_dependencies_filepatterns: t.Optional[t.List[str]] = None,
) -> bool:
# not check since modified_components and modified_files are not passed
if modified_components is None and modified_files is None:
return False

# not check since ignore_app_dependencies_filepatterns is passed and matched
if (
ignore_app_dependencies_filepatterns
and modified_files is not None
and files_matches_patterns(modified_files, ignore_app_dependencies_filepatterns, manifest_rootpath)
):
LOGGER.info(
'Build all apps since patterns %s matches modified files %s',
', '.join(modified_files),
', '.join(ignore_app_dependencies_filepatterns),
)
return False

return True


def find_apps(
paths: t.Union[t.List[str], str],
target: str,
Expand Down Expand Up @@ -138,78 +88,7 @@ def find_apps(
:param include_skipped_apps: include skipped apps or not
:return: list of found apps
"""
if default_build_targets:
default_build_targets = to_list(default_build_targets)
LOGGER.info('Overriding default build targets to %s', default_build_targets)
FolderRule.DEFAULT_BUILD_TARGETS = default_build_targets

if isinstance(build_system, str):
# backwards compatible
if build_system == 'cmake':
build_system = CMakeApp
elif build_system == 'make':
build_system = MakeApp
else:
raise ValueError('Only Support "make" and "cmake"')
app_cls = build_system

# always set the manifest rootpath at the very beginning of find_apps in case ESP-IDF switches the branch.
Manifest.ROOTPATH = to_absolute_path(manifest_rootpath or os.curdir)
Manifest.CHECK_MANIFEST_RULES = check_manifest_rules

if manifest_files:
rules = set()
for _manifest_file in to_list(manifest_files):
LOGGER.debug('Loading manifest file: %s', _manifest_file)
rules.update(Manifest.from_file(_manifest_file).rules)
manifest = Manifest(rules)
App.MANIFEST = manifest

modified_components = to_list(modified_components)
modified_files = to_list(modified_files)
ignore_app_dependencies_filepatterns = to_list(ignore_app_dependencies_filepatterns)
config_rules_str = to_list(config_rules_str)

apps = []
if target == 'all':
targets = ALL_TARGETS
else:
targets = [target]

for target in targets:
for path in to_list(paths):
path = path.strip()
apps.extend(
_find_apps(
path,
target,
app_cls,
recursive,
exclude_list or [],
work_dir=work_dir,
build_dir=build_dir or 'build',
config_rules_str=config_rules_str,
build_log_filename=build_log_filename,
size_json_filename=size_json_filename,
check_warnings=check_warnings,
preserve=preserve,
manifest_rootpath=manifest_rootpath,
check_app_dependencies=_check_app_dependency(
manifest_rootpath=manifest_rootpath,
modified_components=modified_components,
modified_files=modified_files,
ignore_app_dependencies_filepatterns=ignore_app_dependencies_filepatterns,
),
modified_components=modified_components,
modified_files=modified_files,
sdkconfig_defaults_str=sdkconfig_defaults,
include_skipped_apps=include_skipped_apps,
)
)

LOGGER.info(f'Found {len(apps)} apps in total')

return sorted(apps)
return IdfBuildApps.hook.find_apps(**locals())


def build_apps(
Expand Down Expand Up @@ -259,117 +138,7 @@ def build_apps(
:param junitxml: path of the junitxml file
:return: exit code
"""
apps = to_list(apps)
modified_components = to_list(modified_components)
modified_files = to_list(modified_files)
ignore_app_dependencies_filepatterns = to_list(ignore_app_dependencies_filepatterns)

test_suite = TestSuite('build_apps')

ignore_warnings_regexes = []
if ignore_warning_strs:
for s in ignore_warning_strs:
ignore_warnings_regexes.append(re.compile(s))
if ignore_warning_file:
for s in ignore_warning_file:
ignore_warnings_regexes.append(re.compile(s.strip()))
App.IGNORE_WARNS_REGEXES = ignore_warnings_regexes

start, stop = get_parallel_start_stop(len(apps), parallel_count, parallel_index)
LOGGER.info('Total %s apps. running build for app %s-%s', len(apps), start, stop)

build_apps_args = BuildAppsArgs(
parallel_count=parallel_count,
parallel_index=parallel_index,
collect_size_info=collect_size_info,
collect_app_info=collect_app_info,
junitxml=junitxml,
)
for app in apps[start - 1 : stop]: # we use 1-based
app.build_apps_args = build_apps_args

# cleanup collect files if exists at this early-stage
for f in (build_apps_args.collect_app_info, build_apps_args.collect_size_info, build_apps_args.junitxml):
if f and os.path.isfile(f):
os.remove(f)
LOGGER.debug('Remove existing collect file %s', f)
os.mknod(f)

exit_code = 0
for i, app in enumerate(apps):
index = i + 1 # we use 1-based
if index < start or index > stop:
continue

# attrs
app.dry_run = dry_run
app.index = index
app.verbose = build_verbose

LOGGER.info('(%s/%s) Building app: %s', index, len(apps), app)

app.build(
manifest_rootpath=manifest_rootpath,
modified_components=modified_components,
modified_files=modified_files,
check_app_dependencies=_check_app_dependency(
manifest_rootpath, modified_components, modified_files, ignore_app_dependencies_filepatterns
)
if check_app_dependencies is None
else check_app_dependencies,
)
test_suite.add_test_case(TestCase.from_app(app))

if app.build_comment:
LOGGER.info('%s (%s)', app.build_status.value, app.build_comment)
else:
LOGGER.info('%s', app.build_status.value)

if build_apps_args.collect_app_info:
with open(build_apps_args.collect_app_info, 'a') as fw:
fw.write(app.to_json() + '\n')
LOGGER.debug('Recorded app info in %s', build_apps_args.collect_app_info)

if copy_sdkconfig:
try:
shutil.copy(
os.path.join(app.work_dir, 'sdkconfig'),
os.path.join(app.build_path, 'sdkconfig'),
)
except Exception as e:
LOGGER.warning('Copy sdkconfig file from failed: %s', e)
else:
LOGGER.debug('Copied sdkconfig file from %s to %s', app.work_dir, app.build_path)

if app.build_status == BuildStatus.FAILED:
if not keep_going:
return 1
else:
exit_code = 1
elif app.build_status == BuildStatus.SUCCESS:
if build_apps_args.collect_size_info and app.size_json_path:
if os.path.isfile(app.size_json_path):
with open(build_apps_args.collect_size_info, 'a') as fw:
fw.write(
json.dumps(
{
'app_name': app.name,
'config_name': app.config_name,
'target': app.target,
'path': app.size_json_path,
}
)
+ '\n'
)
LOGGER.debug('Recorded size info file path in %s', build_apps_args.collect_size_info)

LOGGER.info('') # add one empty line for separating different builds

if build_apps_args.junitxml:
TestReport([test_suite], build_apps_args.junitxml).create_test_report()
LOGGER.info('Generated junit report for build apps: %s', build_apps_args.junitxml)

return exit_code
return IdfBuildApps.hook.build_apps(**locals())


class IdfBuildAppsCliFormatter(argparse.HelpFormatter):
Expand Down
Loading

0 comments on commit a8f4537

Please sign in to comment.