idf-build-apps 2.10.2__tar.gz → 2.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/CHANGELOG.md +17 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/PKG-INFO +1 -1
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/references/config_file.rst +1 -1
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/__init__.py +1 -1
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/app.py +3 -3
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/args.py +45 -22
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/finder.py +12 -1
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/main.py +4 -3
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/manifest/__init__.py +2 -2
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/manifest/manifest.py +71 -2
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/pyproject.toml +1 -1
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/setup.py +1 -1
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/conftest.py +2 -3
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_args.py +138 -3
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_finder.py +122 -5
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.editorconfig +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.git-blame-ignore-revs +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.gitattributes +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.github/dependabot.yml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.github/workflows/publish-pypi.yml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.github/workflows/sync-jira.yml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.github/workflows/test-build-docs.yml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.github/workflows/test-build-idf-apps.yml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.gitignore +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.pre-commit-config.yaml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/.readthedocs.yml +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/LICENSE +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/README.md +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/_apidoc_templates/module.rst_t +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/_apidoc_templates/package.rst_t +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/_apidoc_templates/toc.rst_t +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/_static/espressif-logo.svg +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/_static/theme_overrides.css +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/_templates/layout.html +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/conf_common.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/Makefile +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/conf.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/explanations/build.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/explanations/config_rules.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/explanations/dependency_driven_build.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/explanations/find.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/guides/1.x_to_2.x.md +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/guides/custom_app.md +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/index.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/others/CHANGELOG.md +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/others/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/references/cli.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/references/manifest.rst +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/__main__.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/autocompletions.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/constants.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/junit/__init__.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/junit/report.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/junit/utils.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/log.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/manifest/soc_header.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/py.typed +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/session_args.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/utils.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/vendors/__init__.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/vendors/pydantic_sources.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/yaml/__init__.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/idf_build_apps/yaml/parser.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/license_header.txt +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_app.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_build.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_cmd.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_manifest.py +0 -0
- {idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/tests/test_utils.py +0 -0
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v2.11.0 (2025-06-03)
|
|
6
|
+
|
|
7
|
+
### Feat
|
|
8
|
+
|
|
9
|
+
- support extra_pythonpaths injection during the runtime
|
|
10
|
+
|
|
11
|
+
## v2.10.3 (2025-06-03)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
- app.target have higher precedence than target while `find_apps`
|
|
16
|
+
- respect FolderRule.DEFAULT_BUILD_TARGETS while validating app
|
|
17
|
+
|
|
18
|
+
### Refactor
|
|
19
|
+
|
|
20
|
+
- move `FolderRule.DEFAULT_BUILD_TARGET` into contextvar
|
|
21
|
+
|
|
5
22
|
## v2.10.2 (2025-05-22)
|
|
6
23
|
|
|
7
24
|
### Perf
|
|
@@ -101,7 +101,7 @@ This indicates that in the configuration file, you should specify it with the na
|
|
|
101
101
|
Expand Environment Variables
|
|
102
102
|
******************************
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
All configuration options support environment variables. You can use environment variables in the configuration file by using the syntax ``${VAR_NAME}`` or ``$VAR_NAME``. Undeclared environment variables will be replaced with an empty string. For example:
|
|
105
105
|
|
|
106
106
|
.. code:: toml
|
|
107
107
|
|
|
@@ -39,7 +39,7 @@ from .constants import (
|
|
|
39
39
|
BuildStatus,
|
|
40
40
|
)
|
|
41
41
|
from .manifest.manifest import (
|
|
42
|
-
|
|
42
|
+
DEFAULT_BUILD_TARGETS,
|
|
43
43
|
Manifest,
|
|
44
44
|
)
|
|
45
45
|
from .utils import (
|
|
@@ -436,7 +436,7 @@ class App(BaseModel):
|
|
|
436
436
|
if self.sdkconfig_files_defined_idf_target:
|
|
437
437
|
return [self.sdkconfig_files_defined_idf_target]
|
|
438
438
|
|
|
439
|
-
return
|
|
439
|
+
return DEFAULT_BUILD_TARGETS.get()
|
|
440
440
|
|
|
441
441
|
@property
|
|
442
442
|
def verified_targets(self) -> t.List[str]:
|
|
@@ -820,7 +820,7 @@ class MakeApp(App):
|
|
|
820
820
|
if self.sdkconfig_files_defined_idf_target:
|
|
821
821
|
return [self.sdkconfig_files_defined_idf_target]
|
|
822
822
|
|
|
823
|
-
return ['esp8266', *
|
|
823
|
+
return ['esp8266', *DEFAULT_BUILD_TARGETS.get()]
|
|
824
824
|
|
|
825
825
|
def _build(
|
|
826
826
|
self,
|
|
@@ -29,8 +29,8 @@ from pydantic_settings import (
|
|
|
29
29
|
from typing_extensions import Concatenate, ParamSpec
|
|
30
30
|
|
|
31
31
|
from . import SESSION_ARGS, App, CMakeApp, MakeApp, setup_logging
|
|
32
|
-
from .constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN
|
|
33
|
-
from .manifest.manifest import
|
|
32
|
+
from .constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN
|
|
33
|
+
from .manifest.manifest import DEFAULT_BUILD_TARGETS, Manifest, reset_default_build_targets
|
|
34
34
|
from .utils import InvalidCommand, files_matches_patterns, semicolon_separated_str_to_list, to_absolute_path, to_list
|
|
35
35
|
from .vendors.pydantic_sources import PyprojectTomlConfigSettingsSource, TomlConfigSettingsSource
|
|
36
36
|
|
|
@@ -39,7 +39,6 @@ LOGGER = logging.getLogger(__name__)
|
|
|
39
39
|
|
|
40
40
|
class ValidateMethod(str, enum.Enum):
|
|
41
41
|
TO_LIST = 'to_list'
|
|
42
|
-
EXPAND_VARS = 'expand_vars'
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
@dataclass
|
|
@@ -174,12 +173,17 @@ class BaseArguments(BaseSettings):
|
|
|
174
173
|
if info.field_name and info.field_name in cls.model_fields:
|
|
175
174
|
f = cls.model_fields[info.field_name]
|
|
176
175
|
meta = get_meta(f)
|
|
176
|
+
|
|
177
|
+
# always expand vars for all fields
|
|
178
|
+
if isinstance(v, str):
|
|
179
|
+
v = expand_vars(v)
|
|
180
|
+
elif isinstance(v, list):
|
|
181
|
+
v = [expand_vars(item) if isinstance(item, str) else item for item in v]
|
|
182
|
+
|
|
177
183
|
if meta and meta.validate_method:
|
|
178
184
|
for method in meta.validate_method:
|
|
179
185
|
if method == ValidateMethod.TO_LIST:
|
|
180
186
|
v = to_list(v)
|
|
181
|
-
elif method == ValidateMethod.EXPAND_VARS:
|
|
182
|
-
v = expand_vars(v)
|
|
183
187
|
else:
|
|
184
188
|
raise NotImplementedError(f'Unknown validate method: {method}')
|
|
185
189
|
|
|
@@ -241,9 +245,7 @@ class DependencyDrivenBuildArguments(GlobalArguments):
|
|
|
241
245
|
default=None, # type: ignore
|
|
242
246
|
)
|
|
243
247
|
manifest_rootpath: str = field(
|
|
244
|
-
|
|
245
|
-
validate_method=[ValidateMethod.EXPAND_VARS],
|
|
246
|
-
),
|
|
248
|
+
None,
|
|
247
249
|
description='Root path to resolve the relative paths defined in the manifest files. '
|
|
248
250
|
'By default set to the current directory. Support environment variables.',
|
|
249
251
|
default=os.curdir, # type: ignore
|
|
@@ -420,6 +422,15 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
420
422
|
description='Filter the apps by target. By default set to "all"',
|
|
421
423
|
default='all', # type: ignore
|
|
422
424
|
)
|
|
425
|
+
extra_pythonpaths: t.Optional[t.List[str]] = field(
|
|
426
|
+
FieldMetadata(
|
|
427
|
+
validate_method=[ValidateMethod.TO_LIST],
|
|
428
|
+
nargs='+',
|
|
429
|
+
),
|
|
430
|
+
description='space-separated list of additional Python paths to search for the app classes. '
|
|
431
|
+
'Will be injected into the head of sys.path.',
|
|
432
|
+
default=None, # type: ignore
|
|
433
|
+
)
|
|
423
434
|
build_system: t.Union[str, t.Type[App]] = field(
|
|
424
435
|
None,
|
|
425
436
|
description='Filter the apps by build system. By default set to "cmake". '
|
|
@@ -519,15 +530,17 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
519
530
|
nargs='+',
|
|
520
531
|
),
|
|
521
532
|
description='space-separated list of the default enabled build targets for the apps. '
|
|
522
|
-
'When not specified, the default value is the targets listed by `idf.py --list-targets
|
|
533
|
+
'When not specified, the default value is the targets listed by `idf.py --list-targets`. '
|
|
534
|
+
'Cannot be used together with --enable-preview-targets',
|
|
523
535
|
default=None, # type: ignore
|
|
524
536
|
)
|
|
525
537
|
enable_preview_targets: bool = field(
|
|
526
538
|
FieldMetadata(
|
|
527
539
|
action='store_true',
|
|
528
540
|
),
|
|
529
|
-
description='When enabled,
|
|
530
|
-
'including the preview targets. As the targets defined in `idf.py --list-targets --preview
|
|
541
|
+
description='When enabled, all targets will be enabled by default, '
|
|
542
|
+
'including the preview targets. As the targets defined in `idf.py --list-targets --preview`. '
|
|
543
|
+
'Cannot be used together with --default-build-targets',
|
|
531
544
|
default=False, # type: ignore
|
|
532
545
|
)
|
|
533
546
|
disable_targets: t.Optional[t.List[str]] = field(
|
|
@@ -570,6 +583,14 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
570
583
|
LOGGER.debug('--target is missing. Set --target as "all".')
|
|
571
584
|
self.target = 'all'
|
|
572
585
|
|
|
586
|
+
# Validate mutual exclusivity of enable_preview_targets and default_build_targets
|
|
587
|
+
if self.enable_preview_targets and self.default_build_targets:
|
|
588
|
+
raise InvalidCommand(
|
|
589
|
+
'Cannot specify both --enable-preview-targets and --default-build-targets at the same time. '
|
|
590
|
+
'Please use only one of these options.'
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
reset_default_build_targets() # reset first then judge again
|
|
573
594
|
if self.default_build_targets:
|
|
574
595
|
default_build_targets = []
|
|
575
596
|
for target in self.default_build_targets:
|
|
@@ -582,25 +603,30 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
582
603
|
default_build_targets.append(target)
|
|
583
604
|
self.default_build_targets = default_build_targets
|
|
584
605
|
LOGGER.info('Overriding default build targets to %s', self.default_build_targets)
|
|
585
|
-
|
|
606
|
+
DEFAULT_BUILD_TARGETS.set(self.default_build_targets)
|
|
586
607
|
elif self.enable_preview_targets:
|
|
587
608
|
self.default_build_targets = deepcopy(ALL_TARGETS)
|
|
588
609
|
LOGGER.info('Overriding default build targets to %s', self.default_build_targets)
|
|
589
|
-
|
|
590
|
-
else:
|
|
591
|
-
# restore default build targets
|
|
592
|
-
FolderRule.DEFAULT_BUILD_TARGETS = SUPPORTED_TARGETS
|
|
610
|
+
DEFAULT_BUILD_TARGETS.set(self.default_build_targets) # type: ignore
|
|
593
611
|
|
|
594
|
-
if self.disable_targets and
|
|
612
|
+
if self.disable_targets and DEFAULT_BUILD_TARGETS.get():
|
|
595
613
|
LOGGER.info('Disable targets: %s', self.disable_targets)
|
|
596
614
|
self.default_build_targets = [
|
|
597
|
-
_target for _target in
|
|
615
|
+
_target for _target in DEFAULT_BUILD_TARGETS.get() if _target not in self.disable_targets
|
|
598
616
|
]
|
|
599
|
-
|
|
617
|
+
DEFAULT_BUILD_TARGETS.set(self.default_build_targets)
|
|
600
618
|
|
|
601
619
|
if self.override_sdkconfig_files or self.override_sdkconfig_items:
|
|
602
620
|
SESSION_ARGS.set(self)
|
|
603
621
|
|
|
622
|
+
# update PYTHONPATH
|
|
623
|
+
if self.extra_pythonpaths:
|
|
624
|
+
LOGGER.debug('Adding extra Python paths: %s', self.extra_pythonpaths)
|
|
625
|
+
for path in self.extra_pythonpaths:
|
|
626
|
+
abs_path = to_absolute_path(path)
|
|
627
|
+
if abs_path not in sys.path:
|
|
628
|
+
sys.path.insert(0, abs_path)
|
|
629
|
+
|
|
604
630
|
# load build system
|
|
605
631
|
# here could be a string or a class of type App
|
|
606
632
|
if not isinstance(self.build_system, str):
|
|
@@ -763,7 +789,6 @@ class BuildArguments(FindBuildArguments):
|
|
|
763
789
|
FieldMetadata(
|
|
764
790
|
deprecates={'collect_size_info': {}},
|
|
765
791
|
hidden=True,
|
|
766
|
-
validate_method=[ValidateMethod.EXPAND_VARS],
|
|
767
792
|
),
|
|
768
793
|
description='Record size json filepath of the built apps to the specified file. '
|
|
769
794
|
'Each line is a json string. Can expand placeholders @p. Support environment variables.',
|
|
@@ -775,7 +800,6 @@ class BuildArguments(FindBuildArguments):
|
|
|
775
800
|
FieldMetadata(
|
|
776
801
|
deprecates={'collect_app_info': {}},
|
|
777
802
|
hidden=True,
|
|
778
|
-
validate_method=[ValidateMethod.EXPAND_VARS],
|
|
779
803
|
),
|
|
780
804
|
description='Record serialized app model of the built apps to the specified file. '
|
|
781
805
|
'Each line is a json string. Can expand placeholders @p. Support environment variables.',
|
|
@@ -787,7 +811,6 @@ class BuildArguments(FindBuildArguments):
|
|
|
787
811
|
FieldMetadata(
|
|
788
812
|
deprecates={'junitxml': {}},
|
|
789
813
|
hidden=True,
|
|
790
|
-
validate_method=[ValidateMethod.EXPAND_VARS],
|
|
791
814
|
),
|
|
792
815
|
description='Path to the junitxml file to record the build results. Can expand placeholder @p. '
|
|
793
816
|
'Support environment variables.',
|
|
@@ -18,6 +18,7 @@ from .args import FindArguments
|
|
|
18
18
|
from .constants import (
|
|
19
19
|
BuildStatus,
|
|
20
20
|
)
|
|
21
|
+
from .manifest.manifest import DEFAULT_BUILD_TARGETS
|
|
21
22
|
from .utils import (
|
|
22
23
|
config_rules_from_str,
|
|
23
24
|
to_absolute_path,
|
|
@@ -33,13 +34,23 @@ def _get_apps_from_path(
|
|
|
33
34
|
app_cls: t.Type[App] = CMakeApp,
|
|
34
35
|
args: FindArguments,
|
|
35
36
|
) -> t.List[App]:
|
|
36
|
-
# trigger test
|
|
37
37
|
def _validate_app(_app: App) -> bool:
|
|
38
38
|
if target not in _app.supported_targets:
|
|
39
39
|
LOGGER.debug('=> Ignored. %s only supports targets: %s', _app, ', '.join(_app.supported_targets))
|
|
40
40
|
_app.build_status = BuildStatus.DISABLED
|
|
41
41
|
return args.include_disabled_apps
|
|
42
42
|
|
|
43
|
+
if target == 'all' and _app.target not in DEFAULT_BUILD_TARGETS.get():
|
|
44
|
+
LOGGER.debug(
|
|
45
|
+
'=> Ignored. %s is not in the default build targets: %s', _app.target, DEFAULT_BUILD_TARGETS.get()
|
|
46
|
+
)
|
|
47
|
+
_app.build_status = BuildStatus.DISABLED
|
|
48
|
+
return args.include_disabled_apps
|
|
49
|
+
elif _app.target != target:
|
|
50
|
+
LOGGER.debug('=> Ignored. %s is not for target %s', _app, target)
|
|
51
|
+
_app.build_status = BuildStatus.DISABLED
|
|
52
|
+
return args.include_disabled_apps
|
|
53
|
+
|
|
43
54
|
_app.check_should_build(
|
|
44
55
|
manifest_rootpath=args.manifest_rootpath,
|
|
45
56
|
modified_manifest_rules_folders=args.modified_manifest_rules_folders,
|
|
@@ -31,7 +31,7 @@ from .app import (
|
|
|
31
31
|
AppDeserializer,
|
|
32
32
|
)
|
|
33
33
|
from .autocompletions import activate_completions
|
|
34
|
-
from .constants import
|
|
34
|
+
from .constants import BuildStatus, completion_instructions
|
|
35
35
|
from .finder import (
|
|
36
36
|
_find_apps,
|
|
37
37
|
)
|
|
@@ -41,6 +41,7 @@ from .junit import (
|
|
|
41
41
|
TestSuite,
|
|
42
42
|
)
|
|
43
43
|
from .manifest.manifest import (
|
|
44
|
+
DEFAULT_BUILD_TARGETS,
|
|
44
45
|
Manifest,
|
|
45
46
|
)
|
|
46
47
|
from .utils import (
|
|
@@ -88,8 +89,8 @@ def find_apps(
|
|
|
88
89
|
|
|
89
90
|
apps: t.Set[App] = set()
|
|
90
91
|
if find_arguments.target == 'all':
|
|
91
|
-
targets =
|
|
92
|
-
LOGGER.info('Searching for apps by
|
|
92
|
+
targets = DEFAULT_BUILD_TARGETS.get()
|
|
93
|
+
LOGGER.info('Searching for apps by default build targets: %s', targets)
|
|
93
94
|
else:
|
|
94
95
|
targets = [find_arguments.target]
|
|
95
96
|
LOGGER.info('Searching for apps by target: %s', find_arguments.target)
|
|
@@ -7,11 +7,11 @@ Manifest file
|
|
|
7
7
|
|
|
8
8
|
from esp_bool_parser import register_addition_attribute
|
|
9
9
|
|
|
10
|
-
from .manifest import
|
|
10
|
+
from .manifest import DEFAULT_BUILD_TARGETS
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def folder_rule_attr(target, **kwargs):
|
|
14
|
-
return 1 if target in
|
|
14
|
+
return 1 if target in DEFAULT_BUILD_TARGETS.get() else 0
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
register_addition_attribute('INCLUDE_DEFAULT', folder_rule_attr)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import contextvars
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import typing as t
|
|
7
|
+
import warnings
|
|
6
8
|
from hashlib import sha512
|
|
7
9
|
|
|
8
10
|
from esp_bool_parser import BoolStmt, parse_bool_expr
|
|
@@ -26,6 +28,16 @@ from ..yaml import (
|
|
|
26
28
|
|
|
27
29
|
LOGGER = logging.getLogger(__name__)
|
|
28
30
|
|
|
31
|
+
# Context variable for default build targets
|
|
32
|
+
DEFAULT_BUILD_TARGETS: contextvars.ContextVar[t.List[str]] = contextvars.ContextVar(
|
|
33
|
+
'default_build_targets', default=SUPPORTED_TARGETS
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def reset_default_build_targets() -> None:
|
|
38
|
+
"""Reset DEFAULT_BUILD_TARGETS to the default value (SUPPORTED_TARGETS)"""
|
|
39
|
+
DEFAULT_BUILD_TARGETS.set(SUPPORTED_TARGETS)
|
|
40
|
+
|
|
29
41
|
|
|
30
42
|
class IfClause:
|
|
31
43
|
def __init__(self, stmt: str, temporary: bool = False, reason: t.Optional[str] = None) -> None:
|
|
@@ -78,8 +90,65 @@ class SwitchClause:
|
|
|
78
90
|
)
|
|
79
91
|
|
|
80
92
|
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
def _getattr_default_build_targets(name: str) -> t.Any:
|
|
94
|
+
if name == 'DEFAULT_BUILD_TARGETS':
|
|
95
|
+
warnings.warn(
|
|
96
|
+
'FolderRule.DEFAULT_BUILD_TARGETS is deprecated. Use DEFAULT_BUILD_TARGETS.get() directly.',
|
|
97
|
+
DeprecationWarning,
|
|
98
|
+
stacklevel=2,
|
|
99
|
+
)
|
|
100
|
+
return DEFAULT_BUILD_TARGETS.get()
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _setattr_default_build_targets(name: str, value: t.Any) -> bool:
|
|
105
|
+
if name == 'DEFAULT_BUILD_TARGETS':
|
|
106
|
+
warnings.warn(
|
|
107
|
+
'FolderRule.DEFAULT_BUILD_TARGETS is deprecated. Use DEFAULT_BUILD_TARGETS.set() directly.',
|
|
108
|
+
DeprecationWarning,
|
|
109
|
+
stacklevel=2,
|
|
110
|
+
)
|
|
111
|
+
if not isinstance(value, list):
|
|
112
|
+
raise TypeError('Default build targets must be a list')
|
|
113
|
+
DEFAULT_BUILD_TARGETS.set(value)
|
|
114
|
+
return True
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class _FolderRuleMeta(type):
|
|
119
|
+
"""Metaclass to handle class-level assignments to DEFAULT_BUILD_TARGETS"""
|
|
120
|
+
|
|
121
|
+
def __getattribute__(cls, name):
|
|
122
|
+
result = _getattr_default_build_targets(name)
|
|
123
|
+
if result is not None:
|
|
124
|
+
return result
|
|
125
|
+
return super().__getattribute__(name)
|
|
126
|
+
|
|
127
|
+
def __setattr__(cls, name, value):
|
|
128
|
+
if _setattr_default_build_targets(name, value):
|
|
129
|
+
return
|
|
130
|
+
super().__setattr__(name, value)
|
|
131
|
+
|
|
132
|
+
def __delattr__(cls, name):
|
|
133
|
+
if name == 'DEFAULT_BUILD_TARGETS':
|
|
134
|
+
# Don't actually delete anything, just ignore the deletion
|
|
135
|
+
# This handles monkeypatch teardown issues
|
|
136
|
+
pass
|
|
137
|
+
else:
|
|
138
|
+
super().__delattr__(name)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class FolderRule(metaclass=_FolderRuleMeta):
|
|
142
|
+
def __getattribute__(self, name): # instance attr
|
|
143
|
+
result = _getattr_default_build_targets(name)
|
|
144
|
+
if result is not None:
|
|
145
|
+
return result
|
|
146
|
+
return super().__getattribute__(name)
|
|
147
|
+
|
|
148
|
+
def __setattr__(self, name, value):
|
|
149
|
+
if _setattr_default_build_targets(name, value):
|
|
150
|
+
return
|
|
151
|
+
super().__setattr__(name, value)
|
|
83
152
|
|
|
84
153
|
def __init__(
|
|
85
154
|
self,
|
|
@@ -38,7 +38,7 @@ entry_points = \
|
|
|
38
38
|
{'console_scripts': ['idf-build-apps = idf_build_apps:main.main']}
|
|
39
39
|
|
|
40
40
|
setup(name='idf-build-apps',
|
|
41
|
-
version='2.
|
|
41
|
+
version='2.11.0',
|
|
42
42
|
description='Tools for building ESP-IDF related apps.',
|
|
43
43
|
author=None,
|
|
44
44
|
author_email='Fu Hanxi <fuhanxi@espressif.com>',
|
|
@@ -10,14 +10,13 @@ from idf_build_apps import (
|
|
|
10
10
|
setup_logging,
|
|
11
11
|
)
|
|
12
12
|
from idf_build_apps.args import apply_config_file
|
|
13
|
-
from idf_build_apps.
|
|
14
|
-
from idf_build_apps.manifest.manifest import FolderRule
|
|
13
|
+
from idf_build_apps.manifest.manifest import FolderRule, reset_default_build_targets
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
@pytest.fixture(autouse=True)
|
|
18
17
|
def clean_cls_attr(tmp_path):
|
|
19
18
|
App.MANIFEST = None
|
|
20
|
-
|
|
19
|
+
reset_default_build_targets()
|
|
21
20
|
idf_build_apps.SESSION_ARGS.clean()
|
|
22
21
|
apply_config_file(reset=True)
|
|
23
22
|
os.chdir(tmp_path)
|
|
@@ -17,8 +17,10 @@ from idf_build_apps.args import (
|
|
|
17
17
|
FindBuildArguments,
|
|
18
18
|
expand_vars,
|
|
19
19
|
)
|
|
20
|
-
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN, PREVIEW_TARGETS, SUPPORTED_TARGETS
|
|
20
|
+
from idf_build_apps.constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN, PREVIEW_TARGETS, SUPPORTED_TARGETS
|
|
21
21
|
from idf_build_apps.main import main
|
|
22
|
+
from idf_build_apps.manifest.manifest import DEFAULT_BUILD_TARGETS, FolderRule
|
|
23
|
+
from idf_build_apps.utils import InvalidCommand
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
def test_init_attr_deprecated_by():
|
|
@@ -95,12 +97,19 @@ def test_empty_argument():
|
|
|
95
97
|
assert args.config_rules is None
|
|
96
98
|
|
|
97
99
|
|
|
98
|
-
def test_build_args_expansion():
|
|
100
|
+
def test_build_args_expansion(monkeypatch):
|
|
101
|
+
monkeypatch.setenv('FOO', '2')
|
|
102
|
+
|
|
99
103
|
args = BuildArguments(
|
|
100
|
-
parallel_index=2,
|
|
104
|
+
parallel_index=2,
|
|
105
|
+
parallel_count='$FOO',
|
|
106
|
+
collect_app_info='@p.txt',
|
|
107
|
+
junitxml='x_@p.txt',
|
|
108
|
+
collect_size_info='@p_@p.txt',
|
|
101
109
|
)
|
|
102
110
|
assert args.collect_app_info == '2.txt'
|
|
103
111
|
assert args.junitxml == 'x_2.txt'
|
|
112
|
+
assert args.parallel_count == 2
|
|
104
113
|
|
|
105
114
|
args.parallel_index = 3
|
|
106
115
|
assert args.collect_app_info == '3.txt'
|
|
@@ -162,6 +171,15 @@ modified_files = [
|
|
|
162
171
|
assert args.deactivate_dependency_driven_build_by_components == ['baz']
|
|
163
172
|
|
|
164
173
|
|
|
174
|
+
def test_mutual_exclusivity_validation():
|
|
175
|
+
# Test that both options together raise InvalidCommand
|
|
176
|
+
with pytest.raises(InvalidCommand) as exc_info:
|
|
177
|
+
FindBuildArguments(enable_preview_targets=True, default_build_targets=['esp32'], paths=['.'])
|
|
178
|
+
|
|
179
|
+
assert 'Cannot specify both --enable-preview-targets and --default-build-targets' in str(exc_info.value)
|
|
180
|
+
assert 'Please use only one of these options' in str(exc_info.value)
|
|
181
|
+
|
|
182
|
+
|
|
165
183
|
def test_build_targets_cli(tmp_path, monkeypatch):
|
|
166
184
|
create_project('foo', tmp_path)
|
|
167
185
|
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
@@ -411,6 +429,123 @@ dry_run = false
|
|
|
411
429
|
assert test_suite.findall('testcase')[0].attrib['name'] == 'bar/build'
|
|
412
430
|
|
|
413
431
|
|
|
432
|
+
class TestDefaultBuildTargetsContextVar:
|
|
433
|
+
def test_direct_contextvar_access(self):
|
|
434
|
+
# Test initial value
|
|
435
|
+
assert DEFAULT_BUILD_TARGETS.get() == SUPPORTED_TARGETS
|
|
436
|
+
|
|
437
|
+
# Test setting new values
|
|
438
|
+
test_targets = ['esp32', 'esp32s2']
|
|
439
|
+
DEFAULT_BUILD_TARGETS.set(test_targets)
|
|
440
|
+
assert DEFAULT_BUILD_TARGETS.get() == test_targets
|
|
441
|
+
|
|
442
|
+
# Test setting to ALL_TARGETS
|
|
443
|
+
DEFAULT_BUILD_TARGETS.set(ALL_TARGETS)
|
|
444
|
+
assert DEFAULT_BUILD_TARGETS.get() == ALL_TARGETS
|
|
445
|
+
assert len(DEFAULT_BUILD_TARGETS.get()) == len(SUPPORTED_TARGETS) + len(PREVIEW_TARGETS)
|
|
446
|
+
|
|
447
|
+
def test_folder_rule_backward_compatibility(self):
|
|
448
|
+
# Test initial access
|
|
449
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == SUPPORTED_TARGETS
|
|
450
|
+
|
|
451
|
+
# Test setting via contextvar
|
|
452
|
+
other_targets = ['esp32h2', 'esp32p4']
|
|
453
|
+
DEFAULT_BUILD_TARGETS.set(other_targets)
|
|
454
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == other_targets
|
|
455
|
+
assert DEFAULT_BUILD_TARGETS.get() == other_targets
|
|
456
|
+
|
|
457
|
+
# Test setting via FolderRule
|
|
458
|
+
test_targets = ['esp32c3', 'esp32c6']
|
|
459
|
+
FolderRule.DEFAULT_BUILD_TARGETS = test_targets
|
|
460
|
+
assert DEFAULT_BUILD_TARGETS.get() == test_targets
|
|
461
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == test_targets
|
|
462
|
+
|
|
463
|
+
def test_default_build_targets_option(self):
|
|
464
|
+
"""Test that --default-build-targets option works correctly"""
|
|
465
|
+
test_targets = ['esp32', 'esp32s2', 'esp32c3']
|
|
466
|
+
|
|
467
|
+
args = FindBuildArguments(default_build_targets=test_targets, paths=['.'])
|
|
468
|
+
|
|
469
|
+
assert args.default_build_targets == test_targets
|
|
470
|
+
assert DEFAULT_BUILD_TARGETS.get() == test_targets
|
|
471
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == test_targets
|
|
472
|
+
|
|
473
|
+
def test_enable_preview_targets_option(self):
|
|
474
|
+
"""Test that --enable-preview-targets option works correctly"""
|
|
475
|
+
args = FindBuildArguments(enable_preview_targets=True, paths=['.'])
|
|
476
|
+
|
|
477
|
+
assert args.enable_preview_targets is True
|
|
478
|
+
assert args.default_build_targets == ALL_TARGETS
|
|
479
|
+
assert DEFAULT_BUILD_TARGETS.get() == ALL_TARGETS
|
|
480
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == ALL_TARGETS
|
|
481
|
+
assert len(DEFAULT_BUILD_TARGETS.get()) == len(SUPPORTED_TARGETS) + len(PREVIEW_TARGETS)
|
|
482
|
+
|
|
483
|
+
def test_default_behavior(self):
|
|
484
|
+
"""Test default behavior when no special options are provided"""
|
|
485
|
+
args = FindBuildArguments(paths=['.'])
|
|
486
|
+
|
|
487
|
+
assert args.enable_preview_targets is False
|
|
488
|
+
assert args.default_build_targets is None
|
|
489
|
+
assert DEFAULT_BUILD_TARGETS.get() == SUPPORTED_TARGETS
|
|
490
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == SUPPORTED_TARGETS
|
|
491
|
+
|
|
492
|
+
def test_disable_targets_with_default_build_targets(self):
|
|
493
|
+
"""Test --disable-targets option works with --default-build-targets"""
|
|
494
|
+
args = FindBuildArguments(
|
|
495
|
+
default_build_targets=['esp32', 'esp32s2', 'esp32c3'], disable_targets=['esp32s2'], paths=['.']
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
expected_targets = ['esp32', 'esp32c3']
|
|
499
|
+
assert args.default_build_targets == expected_targets
|
|
500
|
+
assert DEFAULT_BUILD_TARGETS.get() == expected_targets
|
|
501
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == expected_targets
|
|
502
|
+
|
|
503
|
+
def test_disable_targets_with_enable_preview_targets(self):
|
|
504
|
+
"""Test --disable-targets option works with --enable-preview-targets"""
|
|
505
|
+
disabled_target = PREVIEW_TARGETS[0] # Disable first preview target
|
|
506
|
+
|
|
507
|
+
args = FindBuildArguments(enable_preview_targets=True, disable_targets=[disabled_target], paths=['.'])
|
|
508
|
+
|
|
509
|
+
expected_targets = [t for t in ALL_TARGETS if t != disabled_target]
|
|
510
|
+
assert args.default_build_targets == expected_targets
|
|
511
|
+
assert DEFAULT_BUILD_TARGETS.get() == expected_targets
|
|
512
|
+
assert len(DEFAULT_BUILD_TARGETS.get()) == len(ALL_TARGETS) - 1
|
|
513
|
+
|
|
514
|
+
def test_invalid_targets_filtering(self):
|
|
515
|
+
"""Test that invalid targets are filtered out and warnings are logged"""
|
|
516
|
+
invalid_targets = ['esp32', 'invalid_target', 'esp32s2', 'another_invalid']
|
|
517
|
+
|
|
518
|
+
args = FindBuildArguments(default_build_targets=invalid_targets, paths=['.'])
|
|
519
|
+
|
|
520
|
+
# Only valid targets should remain
|
|
521
|
+
expected_targets = ['esp32', 'esp32s2']
|
|
522
|
+
assert args.default_build_targets == expected_targets
|
|
523
|
+
assert DEFAULT_BUILD_TARGETS.get() == expected_targets
|
|
524
|
+
|
|
525
|
+
def test_contextvar_isolation_between_instances(self):
|
|
526
|
+
"""Test that the contextvar behaves correctly across multiple argument instances"""
|
|
527
|
+
# First instance sets default_build_targets
|
|
528
|
+
FindBuildArguments(default_build_targets=['esp32', 'esp32s2'])
|
|
529
|
+
assert DEFAULT_BUILD_TARGETS.get() == ['esp32', 'esp32s2']
|
|
530
|
+
|
|
531
|
+
# Second instance sets enable_preview_targets
|
|
532
|
+
FindBuildArguments(enable_preview_targets=True)
|
|
533
|
+
assert DEFAULT_BUILD_TARGETS.get() == ALL_TARGETS
|
|
534
|
+
|
|
535
|
+
# Third instance uses default behavior
|
|
536
|
+
FindBuildArguments()
|
|
537
|
+
assert DEFAULT_BUILD_TARGETS.get() == SUPPORTED_TARGETS
|
|
538
|
+
|
|
539
|
+
def test_empty_default_build_targets(self):
|
|
540
|
+
"""Test behavior with empty default_build_targets list"""
|
|
541
|
+
args = FindBuildArguments(default_build_targets=[])
|
|
542
|
+
|
|
543
|
+
# Empty list is treated as falsy, so it falls back to default behavior
|
|
544
|
+
assert args.default_build_targets == []
|
|
545
|
+
assert DEFAULT_BUILD_TARGETS.get() == SUPPORTED_TARGETS
|
|
546
|
+
assert FolderRule.DEFAULT_BUILD_TARGETS == SUPPORTED_TARGETS
|
|
547
|
+
|
|
548
|
+
|
|
414
549
|
def test_expand_vars(monkeypatch):
|
|
415
550
|
assert expand_vars('Value is $TEST_VAR') == 'Value is '
|
|
416
551
|
monkeypatch.setenv('TEST_VAR', 'test_value')
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
+
import sys
|
|
5
6
|
import tempfile
|
|
6
7
|
from pathlib import (
|
|
7
8
|
Path,
|
|
@@ -12,6 +13,7 @@ from conftest import (
|
|
|
12
13
|
create_project,
|
|
13
14
|
)
|
|
14
15
|
|
|
16
|
+
import idf_build_apps
|
|
15
17
|
from idf_build_apps.constants import (
|
|
16
18
|
DEFAULT_SDKCONFIG,
|
|
17
19
|
IDF_PATH,
|
|
@@ -24,8 +26,9 @@ from idf_build_apps.manifest.manifest import Manifest
|
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class TestFindWithManifest:
|
|
27
|
-
def test_manifest_rootpath_chdir(self, capsys):
|
|
28
|
-
test_dir =
|
|
29
|
+
def test_manifest_rootpath_chdir(self, capsys, tmp_path):
|
|
30
|
+
test_dir = tmp_path / 'examples' / 'get-started'
|
|
31
|
+
create_project('hello_world', test_dir)
|
|
29
32
|
|
|
30
33
|
yaml_file = test_dir / 'test.yml'
|
|
31
34
|
yaml_file.write_text(
|
|
@@ -37,7 +40,7 @@ examples/get-started:
|
|
|
37
40
|
encoding='utf8',
|
|
38
41
|
)
|
|
39
42
|
|
|
40
|
-
os.chdir(
|
|
43
|
+
os.chdir(tmp_path)
|
|
41
44
|
assert not find_apps(str(test_dir), 'esp32', recursive=True, manifest_files=str(yaml_file))
|
|
42
45
|
assert not capsys.readouterr().err
|
|
43
46
|
|
|
@@ -457,6 +460,21 @@ class TestFindWithSdkconfigFiles:
|
|
|
457
460
|
except: # noqa
|
|
458
461
|
pass
|
|
459
462
|
|
|
463
|
+
def test_build_preview_but_sdkconfig_default(self, tmp_path):
|
|
464
|
+
create_project('foo', tmp_path)
|
|
465
|
+
|
|
466
|
+
apps = find_apps(str(tmp_path / 'foo'), 'all', default_build_targets=['esp32'])
|
|
467
|
+
assert len(apps) == 1
|
|
468
|
+
|
|
469
|
+
with open(tmp_path / 'foo' / 'sdkconfig.defaults', 'w') as f:
|
|
470
|
+
f.write('CONFIG_IDF_TARGET="esp32p4"\n')
|
|
471
|
+
|
|
472
|
+
apps = find_apps(str(tmp_path / 'foo'), 'all', default_build_targets=['esp32'])
|
|
473
|
+
assert len(apps) == 0
|
|
474
|
+
|
|
475
|
+
apps = find_apps(str(tmp_path / 'foo'), 'esp32p4', default_build_targets=['esp32p4'])
|
|
476
|
+
assert len(apps) == 1
|
|
477
|
+
|
|
460
478
|
def test_with_sdkconfig_defaults_idf_target_but_disabled(self, tmp_path):
|
|
461
479
|
manifest_file = tmp_path / 'manifest.yml'
|
|
462
480
|
manifest_file.write_text(
|
|
@@ -540,8 +558,6 @@ class TestFindWithSdkconfigFiles:
|
|
|
540
558
|
assert len(apps) == 0
|
|
541
559
|
|
|
542
560
|
def test_with_sdkconfig_override(self, tmp_path):
|
|
543
|
-
import idf_build_apps
|
|
544
|
-
|
|
545
561
|
create_project('test1', tmp_path)
|
|
546
562
|
(tmp_path / 'test1' / 'sdkconfig.defaults').write_text(
|
|
547
563
|
"""
|
|
@@ -755,3 +771,104 @@ def test_find_apps_with_duplicated_paths(tmp_path):
|
|
|
755
771
|
== len(find_apps(str(tmp_path / 'folder1'), 'esp32', recursive=True))
|
|
756
772
|
== 2
|
|
757
773
|
)
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
class TestFindWithExtraPythonPaths:
|
|
777
|
+
custom_app_code = """
|
|
778
|
+
import os
|
|
779
|
+
import typing as t
|
|
780
|
+
from idf_build_apps import App
|
|
781
|
+
from idf_build_apps.constants import BuildStatus
|
|
782
|
+
from idf_build_apps.utils import Literal
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
class ExtraPathTestApp(App):
|
|
786
|
+
build_system: Literal['extra_path_test'] = 'extra_path_test' # type: ignore
|
|
787
|
+
|
|
788
|
+
@property
|
|
789
|
+
def supported_targets(self) -> t.List[str]:
|
|
790
|
+
return ['esp32', 'esp32s2', 'esp32c3']
|
|
791
|
+
|
|
792
|
+
def build(self, *args, **kwargs):
|
|
793
|
+
if not self.dry_run:
|
|
794
|
+
os.makedirs(self.build_path, exist_ok=True)
|
|
795
|
+
with open(os.path.join(self.build_path, 'extra_path_test_marker.txt'), 'w') as f:
|
|
796
|
+
f.write('Extra path test build successful')
|
|
797
|
+
self.build_status = BuildStatus.SUCCESS
|
|
798
|
+
|
|
799
|
+
@classmethod
|
|
800
|
+
def is_app(cls, path: str) -> bool:
|
|
801
|
+
return True
|
|
802
|
+
"""
|
|
803
|
+
|
|
804
|
+
@pytest.fixture
|
|
805
|
+
def setup_custom_module_and_app(self, tmp_path):
|
|
806
|
+
"""Set up a custom module in a separate directory and a test app"""
|
|
807
|
+
# Create custom module directory (separate from app directory)
|
|
808
|
+
custom_module_dir = tmp_path / 'custom_modules'
|
|
809
|
+
custom_module_dir.mkdir()
|
|
810
|
+
|
|
811
|
+
# Create the custom module file
|
|
812
|
+
custom_module_file = custom_module_dir / 'extra_path_test_module.py'
|
|
813
|
+
custom_module_file.write_text(self.custom_app_code)
|
|
814
|
+
|
|
815
|
+
# Create test app directory
|
|
816
|
+
test_app_dir = tmp_path / 'test_app'
|
|
817
|
+
test_app_dir.mkdir()
|
|
818
|
+
|
|
819
|
+
# Create basic app structure
|
|
820
|
+
main_dir = test_app_dir / 'main'
|
|
821
|
+
main_dir.mkdir()
|
|
822
|
+
(main_dir / 'main.c').write_text('void app_main() {}')
|
|
823
|
+
(main_dir / 'CMakeLists.txt').write_text('idf_component_register(SRCS "main.c")')
|
|
824
|
+
(test_app_dir / 'CMakeLists.txt').write_text(
|
|
825
|
+
'cmake_minimum_required(VERSION 3.16)\ninclude($ENV{IDF_PATH}/tools/cmake/project.cmake)\nproject(test_app)'
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
return {
|
|
829
|
+
'custom_module_dir': str(custom_module_dir),
|
|
830
|
+
'test_app_dir': str(test_app_dir),
|
|
831
|
+
'custom_module_file': str(custom_module_file),
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
def test_extra_pythonpaths_with_custom_build_system(self, setup_custom_module_and_app):
|
|
835
|
+
"""Test that extra_pythonpaths allows loading custom build system classes"""
|
|
836
|
+
setup = setup_custom_module_and_app
|
|
837
|
+
|
|
838
|
+
original_path = sys.path.copy()
|
|
839
|
+
|
|
840
|
+
try:
|
|
841
|
+
# First verify that without extra_pythonpaths, the module cannot be found
|
|
842
|
+
with pytest.raises(ImportError, match=r'Failed to import module extra_path_test_module'):
|
|
843
|
+
find_apps(
|
|
844
|
+
paths=[setup['test_app_dir']],
|
|
845
|
+
target='esp32',
|
|
846
|
+
build_system='extra_path_test_module:ExtraPathTestApp',
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
# Now test with extra_pythonpaths - this should work
|
|
850
|
+
apps = find_apps(
|
|
851
|
+
paths=[setup['test_app_dir']],
|
|
852
|
+
target='esp32',
|
|
853
|
+
build_system='extra_path_test_module:ExtraPathTestApp',
|
|
854
|
+
extra_pythonpaths=[setup['custom_module_dir']],
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
# Verify we found the app
|
|
858
|
+
assert len(apps) == 1
|
|
859
|
+
app = apps[0]
|
|
860
|
+
|
|
861
|
+
# Verify it's using our custom class
|
|
862
|
+
assert app.build_system == 'extra_path_test'
|
|
863
|
+
assert app.__class__.__name__ == 'ExtraPathTestApp'
|
|
864
|
+
|
|
865
|
+
# Verify the custom module dir was added to sys.path
|
|
866
|
+
assert setup['custom_module_dir'] in sys.path
|
|
867
|
+
|
|
868
|
+
# Test building the app
|
|
869
|
+
app.build(dry_run=True)
|
|
870
|
+
assert app.build_status == BuildStatus.SUCCESS
|
|
871
|
+
|
|
872
|
+
finally:
|
|
873
|
+
# Restore original sys.path
|
|
874
|
+
sys.path[:] = original_path
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{idf_build_apps-2.10.2 → idf_build_apps-2.11.0}/docs/en/explanations/dependency_driven_build.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|