idf-build-apps 2.12.3__tar.gz → 2.13.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.
Files changed (72) hide show
  1. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.github/workflows/publish-pypi.yml +1 -1
  2. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.github/workflows/test-build-docs.yml +1 -1
  3. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.github/workflows/test-build-idf-apps.yml +1 -1
  4. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.pre-commit-config.yaml +2 -2
  5. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/CHANGELOG.md +12 -0
  6. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/PKG-INFO +1 -1
  7. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/__init__.py +1 -1
  8. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/app.py +12 -13
  9. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/args.py +46 -36
  10. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/finder.py +11 -2
  11. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/pyproject.toml +10 -2
  12. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/setup.py +1 -1
  13. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_args.py +177 -55
  14. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_finder.py +1 -0
  15. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.editorconfig +0 -0
  16. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.git-blame-ignore-revs +0 -0
  17. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.gitattributes +0 -0
  18. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.github/dependabot.yml +0 -0
  19. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.github/workflows/sync-jira.yml +0 -0
  20. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.gitignore +0 -0
  21. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/.readthedocs.yml +0 -0
  22. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/CONTRIBUTING.md +0 -0
  23. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/LICENSE +0 -0
  24. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/README.md +0 -0
  25. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/_apidoc_templates/module.rst_t +0 -0
  26. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/_apidoc_templates/package.rst_t +0 -0
  27. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/_apidoc_templates/toc.rst_t +0 -0
  28. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/_static/espressif-logo.svg +0 -0
  29. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/_static/theme_overrides.css +0 -0
  30. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/_templates/layout.html +0 -0
  31. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/conf_common.py +0 -0
  32. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/Makefile +0 -0
  33. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/conf.py +0 -0
  34. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/explanations/build.rst +0 -0
  35. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/explanations/build_status.rst +0 -0
  36. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/explanations/config_rules.rst +0 -0
  37. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/explanations/dependency_driven_build.rst +0 -0
  38. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/explanations/find.rst +0 -0
  39. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/guides/1.x_to_2.x.md +0 -0
  40. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/guides/custom_app.md +0 -0
  41. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/index.rst +0 -0
  42. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/others/CHANGELOG.md +0 -0
  43. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/others/CONTRIBUTING.md +0 -0
  44. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/references/cli.rst +0 -0
  45. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/references/config_file.rst +0 -0
  46. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/docs/en/references/manifest.rst +0 -0
  47. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/__main__.py +0 -0
  48. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/autocompletions.py +0 -0
  49. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/constants.py +0 -0
  50. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/junit/__init__.py +0 -0
  51. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/junit/report.py +0 -0
  52. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/junit/utils.py +0 -0
  53. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/log.py +0 -0
  54. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/main.py +0 -0
  55. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/manifest/__init__.py +0 -0
  56. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/manifest/manifest.py +0 -0
  57. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/manifest/soc_header.py +0 -0
  58. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/py.typed +0 -0
  59. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/session_args.py +0 -0
  60. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/utils.py +0 -0
  61. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/vendors/__init__.py +0 -0
  62. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/vendors/pydantic_sources.py +0 -0
  63. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/yaml/__init__.py +0 -0
  64. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/idf_build_apps/yaml/parser.py +0 -0
  65. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/license_header.txt +0 -0
  66. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/conftest.py +0 -0
  67. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_app.py +0 -0
  68. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_build.py +0 -0
  69. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_cmd.py +0 -0
  70. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_disable_reasons.py +0 -0
  71. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_manifest.py +0 -0
  72. {idf_build_apps-2.12.3 → idf_build_apps-2.13.0}/tests/test_utils.py +0 -0
@@ -10,7 +10,7 @@ jobs:
10
10
  runs-on: ubuntu-22.04
11
11
  steps:
12
12
  - uses: actions/checkout@v5
13
- - uses: actions/setup-python@v5
13
+ - uses: actions/setup-python@v6
14
14
  with:
15
15
  python-version: "3.7"
16
16
  - name: Publish packages
@@ -9,7 +9,7 @@ jobs:
9
9
  steps:
10
10
  - uses: actions/checkout@v5
11
11
  - name: Set up Python
12
- uses: actions/setup-python@v5
12
+ uses: actions/setup-python@v6
13
13
  with:
14
14
  python-version: '3.7'
15
15
  - name: Install dependencies
@@ -21,7 +21,7 @@ jobs:
21
21
  runs-on: ubuntu-22.04
22
22
  steps:
23
23
  - uses: actions/checkout@v5
24
- - uses: actions/setup-python@v5
24
+ - uses: actions/setup-python@v6
25
25
  with:
26
26
  python-version: '3.7'
27
27
  - run: |
@@ -17,13 +17,13 @@ repos:
17
17
  - --use-current-year
18
18
  exclude: 'idf_build_apps/vendors/'
19
19
  - repo: https://github.com/astral-sh/ruff-pre-commit
20
- rev: 'v0.12.8'
20
+ rev: 'v0.13.0'
21
21
  hooks:
22
22
  - id: ruff
23
23
  args: ['--fix']
24
24
  - id: ruff-format
25
25
  - repo: https://github.com/pre-commit/mirrors-mypy
26
- rev: 'v1.17.1'
26
+ rev: 'v1.18.1'
27
27
  hooks:
28
28
  - id: mypy
29
29
  args: ['--warn-unused-ignores']
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## v2.13.0 (2025-09-26)
6
+
7
+ ### ✨ New Features
8
+
9
+ - support `--additional-build-targets` *(Fu Hanxi - dbbd8d3)*
10
+
11
+ ### 🐛 Bug Fixes
12
+
13
+ - **find_apps**: ignore target-specific sdkconfig files even without setting CONFIG_IDF_TARGET *(Fu Hanxi - 1861a7a)*
14
+ - record `checked_should_build` *(Fu Hanxi - f37fff1)*
15
+
16
+
5
17
  ## v2.12.3 (2025-09-16)
6
18
 
7
19
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: idf-build-apps
3
- Version: 2.12.3
3
+ Version: 2.13.0
4
4
  Summary: Tools for building ESP-IDF related apps.
5
5
  Author-email: Fu Hanxi <fuhanxi@espressif.com>
6
6
  Requires-Python: >=3.7
@@ -8,7 +8,7 @@ Tools for building ESP-IDF related apps.
8
8
  # ruff: noqa: E402
9
9
  # avoid circular imports
10
10
 
11
- __version__ = '2.12.3'
11
+ __version__ = '2.13.0'
12
12
 
13
13
  from .session_args import (
14
14
  SessionArgs,
@@ -109,6 +109,7 @@ class App(BaseModel):
109
109
  build_status: BuildStatus = BuildStatus.UNKNOWN
110
110
  build_comment: t.Optional[str] = None
111
111
  test_comment: t.Optional[str] = None
112
+ checked_should_build: bool = False
112
113
 
113
114
  _build_duration: float = 0
114
115
  _build_timestamp: t.Optional[datetime] = None
@@ -116,6 +117,7 @@ class App(BaseModel):
116
117
  __EQ_IGNORE_FIELDS__ = [
117
118
  'build_comment',
118
119
  'test_comment',
120
+ 'checked_should_build',
119
121
  ]
120
122
  __EQ_TUNE_FIELDS__ = {
121
123
  'app_dir': lambda x: (os.path.realpath(os.path.expanduser(x))),
@@ -163,9 +165,6 @@ class App(BaseModel):
163
165
  self._kwargs = kwargs
164
166
  self._initialize_hook(**kwargs)
165
167
 
166
- # private attrs, won't be dumped to json
167
- self._checked_should_build = False
168
-
169
168
  self._sdkconfig_files, self._sdkconfig_files_defined_target = self._process_sdkconfig_files()
170
169
 
171
170
  @classmethod
@@ -750,12 +749,12 @@ class App(BaseModel):
750
749
  self.build_comment += '\n'.join(f'- {clause}' for clause in rule.enable)
751
750
 
752
751
  self.build_status = BuildStatus.DISABLED
753
- self._checked_should_build = True
752
+ self.checked_should_build = True
754
753
  return
755
754
 
756
755
  if not check_app_dependencies:
757
756
  self.build_status = BuildStatus.SHOULD_BE_BUILT
758
- self._checked_should_build = True
757
+ self.checked_should_build = True
759
758
  return
760
759
 
761
760
  if (
@@ -765,26 +764,26 @@ class App(BaseModel):
765
764
  ):
766
765
  self.build_status = BuildStatus.SHOULD_BE_BUILT
767
766
  self.build_comment = 'current build modifies the related manifest rules'
768
- self._checked_should_build = True
767
+ self.checked_should_build = True
769
768
  return
770
769
 
771
770
  if self.is_modified(modified_files):
772
771
  self.build_status = BuildStatus.SHOULD_BE_BUILT
773
772
  self.build_comment = 'current build modifies this app'
774
- self._checked_should_build = True
773
+ self.checked_should_build = True
775
774
  return
776
775
 
777
776
  # if didn't modify any components, and no `depends_filepatterns` defined, skip
778
777
  if modified_components == [] and not self.depends_filepatterns:
779
778
  self.build_status = BuildStatus.SKIPPED
780
779
  self.build_comment = 'current build does not modify any components'
781
- self._checked_should_build = True
780
+ self.checked_should_build = True
782
781
  return
783
782
 
784
783
  # if no special rules defined, we left it unknown and decide with idf.py reconfigure
785
784
  if not self.depends_components and not self.depends_filepatterns:
786
785
  # keep unknown
787
- self._checked_should_build = True
786
+ self.checked_should_build = True
788
787
  self.build_comment = 'no special rules defined, run idf.py reconfigure to decide'
789
788
  return
790
789
 
@@ -795,7 +794,7 @@ class App(BaseModel):
795
794
  # depends components?
796
795
  if self.depends_components and modified_components is not None:
797
796
  if set(self.depends_components).intersection(set(modified_components)):
798
- self._checked_should_build = True
797
+ self.checked_should_build = True
799
798
  self.build_status = BuildStatus.SHOULD_BE_BUILT
800
799
  self.build_comment = (
801
800
  f'Requires components: {", ".join(self.depends_components)}. '
@@ -806,7 +805,7 @@ class App(BaseModel):
806
805
  # or depends file patterns?
807
806
  if self.depends_filepatterns and modified_files is not None:
808
807
  if files_matches_patterns(modified_files, self.depends_filepatterns, manifest_rootpath):
809
- self._checked_should_build = True
808
+ self.checked_should_build = True
810
809
  self.build_status = BuildStatus.SHOULD_BE_BUILT
811
810
  self.build_comment = (
812
811
  f'Requires file patterns: {", ".join(self.depends_filepatterns)}. '
@@ -817,7 +816,7 @@ class App(BaseModel):
817
816
  # special rules defined, but not matched
818
817
  self.build_status = BuildStatus.SKIPPED
819
818
  self.build_comment = 'current build does not modify any components or files required by this app'
820
- self._checked_should_build = True
819
+ self.checked_should_build = True
821
820
 
822
821
  def check_should_test(self) -> None:
823
822
  """Check if testing is disabled for this app and set test_disable_reason."""
@@ -955,7 +954,7 @@ class CMakeApp(App):
955
954
  check_app_dependencies=check_app_dependencies,
956
955
  )
957
956
 
958
- if not self._checked_should_build:
957
+ if not self.checked_should_build:
959
958
  self.check_should_build(
960
959
  manifest_rootpath=manifest_rootpath,
961
960
  modified_components=modified_components,
@@ -29,7 +29,7 @@ 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
32
+ from .constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN, PREVIEW_TARGETS, SUPPORTED_TARGETS
33
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
@@ -563,17 +563,22 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
563
563
  nargs='+',
564
564
  ),
565
565
  description='space-separated list of the default enabled build targets for the apps. '
566
- 'When not specified, the default value is the targets listed by `idf.py --list-targets`. '
567
- 'Cannot be used together with --enable-preview-targets',
566
+ 'When not specified, the default value is the targets listed by `idf.py --list-targets`.',
567
+ default=None, # type: ignore
568
+ )
569
+ additional_build_targets: t.Optional[t.List[str]] = field(
570
+ FieldMetadata(
571
+ validate_method=[ValidateMethod.TO_LIST],
572
+ nargs='+',
573
+ ),
574
+ description='space-separated list of additional build targets to add to the default enabled build targets',
568
575
  default=None, # type: ignore
569
576
  )
570
577
  enable_preview_targets: bool = field(
571
578
  FieldMetadata(
572
579
  action='store_true',
573
580
  ),
574
- description='When enabled, all targets will be enabled by default, '
575
- 'including the preview targets. As the targets defined in `idf.py --list-targets --preview`. '
576
- 'Cannot be used together with --default-build-targets',
581
+ description='When enabled, PREVIEW_TARGETS will be added to the default enabled build targets',
577
582
  default=False, # type: ignore
578
583
  )
579
584
  disable_targets: t.Optional[t.List[str]] = field(
@@ -616,39 +621,44 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
616
621
  LOGGER.debug('--target is missing. Set --target as "all".')
617
622
  self.target = 'all'
618
623
 
619
- # Validate mutual exclusivity of enable_preview_targets and default_build_targets
620
- if self.enable_preview_targets and self.default_build_targets:
621
- raise InvalidCommand(
622
- 'Cannot specify both --enable-preview-targets and --default-build-targets at the same time. '
623
- 'Please use only one of these options.'
624
- )
625
-
626
624
  reset_default_build_targets() # reset first then judge again
625
+
626
+ # Build the target set by combining the options
627
+ default_build_targets: t.List[str] = []
628
+ # Step 1: Determine base targets
627
629
  if self.default_build_targets:
628
- default_build_targets = []
629
- for target in self.default_build_targets:
630
- if target not in ALL_TARGETS:
631
- LOGGER.warning(
632
- f'Ignoring... Unrecognizable target {target} specified with "--default-build-targets". '
633
- f'Current ESP-IDF available targets: {ALL_TARGETS}'
634
- )
635
- elif target not in default_build_targets:
636
- default_build_targets.append(target)
637
- self.default_build_targets = default_build_targets
638
- LOGGER.info('Overriding default build targets to %s', self.default_build_targets)
639
- DEFAULT_BUILD_TARGETS.set(self.default_build_targets)
640
- elif self.enable_preview_targets:
641
- self.default_build_targets = deepcopy(ALL_TARGETS)
642
- LOGGER.info('Overriding default build targets to %s', self.default_build_targets)
643
- DEFAULT_BUILD_TARGETS.set(self.default_build_targets)
644
-
645
- if self.disable_targets and DEFAULT_BUILD_TARGETS.get():
646
- LOGGER.info('Disable targets: %s', self.disable_targets)
647
- self.default_build_targets = [
648
- _target for _target in DEFAULT_BUILD_TARGETS.get() if _target not in self.disable_targets
649
- ]
650
- DEFAULT_BUILD_TARGETS.set(self.default_build_targets)
630
+ LOGGER.info('--default-build-targets is set, using `%s`', self.default_build_targets)
631
+ default_build_targets = deepcopy(self.default_build_targets)
632
+ elif SUPPORTED_TARGETS:
633
+ LOGGER.info('Using default SUPPORTED_TARGETS: %s', SUPPORTED_TARGETS)
634
+ default_build_targets = deepcopy(SUPPORTED_TARGETS)
635
+
636
+ if self.enable_preview_targets:
637
+ LOGGER.info('--enable-preview-targets is set, adding preview targets `%s`', PREVIEW_TARGETS)
638
+ default_build_targets.extend(PREVIEW_TARGETS)
639
+
640
+ if self.additional_build_targets:
641
+ LOGGER.info('--additional-build-targets is set, adding `%s`', self.additional_build_targets)
642
+ default_build_targets.extend(self.additional_build_targets)
643
+
644
+ res = []
645
+ for _t in set(default_build_targets):
646
+ if _t not in ALL_TARGETS:
647
+ LOGGER.warning(
648
+ f'Ignoring... Unrecognizable target {_t} specified. '
649
+ f'Current ESP-IDF available targets: {ALL_TARGETS}'
650
+ )
651
+ continue
652
+
653
+ if self.disable_targets and _t in self.disable_targets:
654
+ LOGGER.info(f'Ignoring... Target {_t} is in the disabled targets list.')
655
+ continue
656
+
657
+ res.append(_t)
658
+ self.default_build_targets = sorted(res)
659
+ DEFAULT_BUILD_TARGETS.set(self.default_build_targets)
651
660
 
661
+ # Override sdkconfig files/items
652
662
  if self.override_sdkconfig_files or self.override_sdkconfig_items:
653
663
  SESSION_ARGS.set(self)
654
664
 
@@ -16,6 +16,7 @@ from .app import (
16
16
  )
17
17
  from .args import FindArguments
18
18
  from .constants import (
19
+ ALL_TARGETS,
19
20
  BuildStatus,
20
21
  )
21
22
  from .utils import (
@@ -26,6 +27,14 @@ from .utils import (
26
27
  LOGGER = logging.getLogger(__name__)
27
28
 
28
29
 
30
+ def _is_target_specific(filepath: str) -> bool:
31
+ for target in ALL_TARGETS:
32
+ if filepath.endswith(f'.{target}'):
33
+ return True
34
+
35
+ return False
36
+
37
+
29
38
  def _get_apps_from_path(
30
39
  path: str,
31
40
  target: str,
@@ -55,8 +64,8 @@ def _get_apps_from_path(
55
64
  sdkconfig_paths_matched = True # skip the next block for no wildcard config rules
56
65
 
57
66
  for sdkconfig_path in sdkconfig_paths:
58
- if sdkconfig_path.endswith(f'.{target}'):
59
- LOGGER.debug('=> Skipping sdkconfig %s which is target-specific', sdkconfig_path)
67
+ if _is_target_specific(sdkconfig_path):
68
+ LOGGER.debug('=> Skipping sdkconfig file `%s` which is target-specific', sdkconfig_path)
60
69
  continue
61
70
 
62
71
  # Figure out the config name
@@ -64,9 +64,17 @@ changelog = "https://github.com/espressif/idf-build-apps/blob/main/CHANGELOG.md"
64
64
  idf-build-apps = "idf_build_apps:main.main"
65
65
 
66
66
  [tool.commitizen]
67
- name = "cz_conventional_commits"
68
- version = "2.12.3"
67
+ name = "czespressif"
68
+ version = "2.13.0"
69
+
69
70
  tag_format = "v$version"
71
+ version_scheme = "pep440"
72
+
73
+ update_changelog_on_bump = true
74
+ changelog_merge_prerelease = true
75
+
76
+ types_in_changelog = ["BREAKING CHANGE", "feat", "fix", "refactor", "change", "perf", "docs"]
77
+
70
78
  version_files = [
71
79
  "idf_build_apps/__init__.py",
72
80
  ]
@@ -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.12.3',
41
+ version='2.13.0',
42
42
  description='Tools for building ESP-IDF related apps.',
43
43
  author=None,
44
44
  author_email='Fu Hanxi <fuhanxi@espressif.com>',
@@ -17,10 +17,9 @@ from idf_build_apps.args import (
17
17
  FindBuildArguments,
18
18
  expand_vars,
19
19
  )
20
- from idf_build_apps.constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN, PREVIEW_TARGETS, SUPPORTED_TARGETS
20
+ from idf_build_apps.constants import 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
+ from idf_build_apps.manifest.manifest import DEFAULT_BUILD_TARGETS, FolderRule, reset_default_build_targets
24
23
 
25
24
 
26
25
  def test_init_attr_deprecated_by():
@@ -171,13 +170,42 @@ modified_files = [
171
170
  assert args.deactivate_dependency_driven_build_by_components == ['baz']
172
171
 
173
172
 
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=['.'])
173
+ def test_combination_validation():
174
+ """Test that target options can now be combined"""
175
+ # Mock targets for consistent testing
176
+ mock_supported = ['esp32', 'esp32s2']
177
+ mock_preview = ['esp32h2']
178
+ mock_all = mock_supported + mock_preview
178
179
 
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)
180
+ import idf_build_apps.args
181
+ import idf_build_apps.constants
182
+
183
+ original_supported = idf_build_apps.constants.SUPPORTED_TARGETS
184
+ original_preview = idf_build_apps.constants.PREVIEW_TARGETS
185
+ original_all = idf_build_apps.constants.ALL_TARGETS
186
+
187
+ try:
188
+ idf_build_apps.constants.SUPPORTED_TARGETS = mock_supported
189
+ idf_build_apps.constants.PREVIEW_TARGETS = mock_preview
190
+ idf_build_apps.constants.ALL_TARGETS = mock_all
191
+ idf_build_apps.args.SUPPORTED_TARGETS = mock_supported
192
+ idf_build_apps.args.PREVIEW_TARGETS = mock_preview
193
+ idf_build_apps.args.ALL_TARGETS = mock_all
194
+
195
+ # Test combination: default + preview
196
+ args = FindBuildArguments(enable_preview_targets=True, default_build_targets=['esp32'], paths=['.'])
197
+
198
+ # Should combine: ['esp32'] + ['esp32h2'] = ['esp32', 'esp32h2']
199
+ expected = ['esp32', 'esp32h2']
200
+ assert args.default_build_targets == expected
201
+
202
+ finally:
203
+ idf_build_apps.constants.SUPPORTED_TARGETS = original_supported
204
+ idf_build_apps.constants.PREVIEW_TARGETS = original_preview
205
+ idf_build_apps.constants.ALL_TARGETS = original_all
206
+ idf_build_apps.args.SUPPORTED_TARGETS = original_supported
207
+ idf_build_apps.args.PREVIEW_TARGETS = original_preview
208
+ idf_build_apps.args.ALL_TARGETS = original_all
181
209
 
182
210
 
183
211
  def test_build_targets_cli(tmp_path, monkeypatch):
@@ -430,9 +458,34 @@ dry_run = false
430
458
 
431
459
 
432
460
  class TestDefaultBuildTargetsContextVar:
461
+ SUPPORTED_TARGETS = ['esp32', 'esp32s2']
462
+ PREVIEW_TARGETS = ['esp32h2', 'esp32p4']
463
+ ALL_TARGETS = SUPPORTED_TARGETS + PREVIEW_TARGETS + ['esp32s3', 'esp32c3']
464
+
465
+ @pytest.fixture(autouse=True)
466
+ def setup(self, monkeypatch):
467
+ import idf_build_apps.args
468
+ import idf_build_apps.constants
469
+ import idf_build_apps.manifest.manifest
470
+
471
+ # reset_default_build_targets() need .manifest.manifest
472
+ monkeypatch.setattr(idf_build_apps.manifest.manifest, 'SUPPORTED_TARGETS', self.SUPPORTED_TARGETS)
473
+ monkeypatch.setattr(idf_build_apps.constants, 'SUPPORTED_TARGETS', self.SUPPORTED_TARGETS)
474
+ monkeypatch.setattr(idf_build_apps.args, 'SUPPORTED_TARGETS', self.SUPPORTED_TARGETS)
475
+
476
+ monkeypatch.setattr(idf_build_apps.constants, 'PREVIEW_TARGETS', self.PREVIEW_TARGETS)
477
+ monkeypatch.setattr(idf_build_apps.args, 'PREVIEW_TARGETS', self.PREVIEW_TARGETS)
478
+
479
+ monkeypatch.setattr(idf_build_apps.manifest.manifest, 'ALL_TARGETS', self.SUPPORTED_TARGETS)
480
+ monkeypatch.setattr(idf_build_apps.constants, 'ALL_TARGETS', self.ALL_TARGETS)
481
+ monkeypatch.setattr(idf_build_apps.args, 'ALL_TARGETS', self.ALL_TARGETS)
482
+
483
+ reset_default_build_targets()
484
+
485
+ yield
486
+
433
487
  def test_direct_contextvar_access(self):
434
- # Test initial value
435
- assert DEFAULT_BUILD_TARGETS.get() == SUPPORTED_TARGETS
488
+ assert DEFAULT_BUILD_TARGETS.get() == self.SUPPORTED_TARGETS
436
489
 
437
490
  # Test setting new values
438
491
  test_targets = ['esp32', 'esp32s2']
@@ -440,14 +493,10 @@ class TestDefaultBuildTargetsContextVar:
440
493
  assert DEFAULT_BUILD_TARGETS.get() == test_targets
441
494
 
442
495
  # 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)
496
+ DEFAULT_BUILD_TARGETS.set(self.ALL_TARGETS)
497
+ assert DEFAULT_BUILD_TARGETS.get() == self.ALL_TARGETS
446
498
 
447
499
  def test_folder_rule_backward_compatibility(self):
448
- # Test initial access
449
- assert FolderRule.DEFAULT_BUILD_TARGETS == SUPPORTED_TARGETS
450
-
451
500
  # Test setting via contextvar
452
501
  other_targets = ['esp32h2', 'esp32p4']
453
502
  DEFAULT_BUILD_TARGETS.set(other_targets)
@@ -460,37 +509,29 @@ class TestDefaultBuildTargetsContextVar:
460
509
  assert DEFAULT_BUILD_TARGETS.get() == test_targets
461
510
  assert FolderRule.DEFAULT_BUILD_TARGETS == test_targets
462
511
 
463
- def test_default_build_targets_option(self):
464
- """Test that --default-build-targets option works correctly"""
465
- test_targets = ['esp32', 'esp32s2', 'esp32c3']
512
+ def test_default_behavior(self):
513
+ args = FindBuildArguments(paths=['.'])
514
+
515
+ assert args.enable_preview_targets is False
516
+ assert args.default_build_targets == self.SUPPORTED_TARGETS
517
+ assert DEFAULT_BUILD_TARGETS.get() == self.SUPPORTED_TARGETS
466
518
 
519
+ def test_default_build_targets_option(self):
520
+ test_targets = ['esp32', 'esp32c3', 'esp32s2']
467
521
  args = FindBuildArguments(default_build_targets=test_targets, paths=['.'])
468
522
 
469
523
  assert args.default_build_targets == test_targets
470
524
  assert DEFAULT_BUILD_TARGETS.get() == test_targets
471
- assert FolderRule.DEFAULT_BUILD_TARGETS == test_targets
472
525
 
473
526
  def test_enable_preview_targets_option(self):
474
- """Test that --enable-preview-targets option works correctly"""
475
527
  args = FindBuildArguments(enable_preview_targets=True, paths=['.'])
476
528
 
477
529
  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
530
+ expected_targets = sorted(self.SUPPORTED_TARGETS + self.PREVIEW_TARGETS)
531
+ assert args.default_build_targets == expected_targets
532
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
491
533
 
492
534
  def test_disable_targets_with_default_build_targets(self):
493
- """Test --disable-targets option works with --default-build-targets"""
494
535
  args = FindBuildArguments(
495
536
  default_build_targets=['esp32', 'esp32s2', 'esp32c3'], disable_targets=['esp32s2'], paths=['.']
496
537
  )
@@ -498,23 +539,19 @@ class TestDefaultBuildTargetsContextVar:
498
539
  expected_targets = ['esp32', 'esp32c3']
499
540
  assert args.default_build_targets == expected_targets
500
541
  assert DEFAULT_BUILD_TARGETS.get() == expected_targets
501
- assert FolderRule.DEFAULT_BUILD_TARGETS == expected_targets
502
542
 
503
543
  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
-
544
+ disabled_target = 'esp32h2'
507
545
  args = FindBuildArguments(enable_preview_targets=True, disable_targets=[disabled_target], paths=['.'])
508
546
 
509
- expected_targets = [t for t in ALL_TARGETS if t != disabled_target]
547
+ expected_targets = sorted(
548
+ [_t for _t in [*self.SUPPORTED_TARGETS, *self.PREVIEW_TARGETS] if _t != disabled_target]
549
+ )
510
550
  assert args.default_build_targets == expected_targets
511
551
  assert DEFAULT_BUILD_TARGETS.get() == expected_targets
512
- assert len(DEFAULT_BUILD_TARGETS.get()) == len(ALL_TARGETS) - 1
513
552
 
514
553
  def test_invalid_targets_filtering(self):
515
- """Test that invalid targets are filtered out and warnings are logged"""
516
554
  invalid_targets = ['esp32', 'invalid_target', 'esp32s2', 'another_invalid']
517
-
518
555
  args = FindBuildArguments(default_build_targets=invalid_targets, paths=['.'])
519
556
 
520
557
  # Only valid targets should remain
@@ -525,25 +562,110 @@ class TestDefaultBuildTargetsContextVar:
525
562
  def test_contextvar_isolation_between_instances(self):
526
563
  """Test that the contextvar behaves correctly across multiple argument instances"""
527
564
  # First instance sets default_build_targets
528
- FindBuildArguments(default_build_targets=['esp32', 'esp32s2'])
565
+ FindBuildArguments(default_build_targets=['esp32', 'esp32s2'], paths=['.'])
529
566
  assert DEFAULT_BUILD_TARGETS.get() == ['esp32', 'esp32s2']
530
567
 
531
- # Second instance sets enable_preview_targets
532
- FindBuildArguments(enable_preview_targets=True)
533
- assert DEFAULT_BUILD_TARGETS.get() == ALL_TARGETS
568
+ # Second instance sets enable_preview_targets (should combine with SUPPORTED_TARGETS)
569
+ FindBuildArguments(enable_preview_targets=True, paths=['.'])
570
+ assert DEFAULT_BUILD_TARGETS.get() == sorted(self.SUPPORTED_TARGETS + self.PREVIEW_TARGETS)
534
571
 
535
- # Third instance uses default behavior
536
- FindBuildArguments()
537
- assert DEFAULT_BUILD_TARGETS.get() == SUPPORTED_TARGETS
572
+ FindBuildArguments(paths=['.'])
573
+ assert DEFAULT_BUILD_TARGETS.get() == self.SUPPORTED_TARGETS
538
574
 
539
575
  def test_empty_default_build_targets(self):
540
- """Test behavior with empty default_build_targets list"""
541
576
  args = FindBuildArguments(default_build_targets=[])
542
577
 
543
578
  # 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
579
+ assert args.default_build_targets == self.SUPPORTED_TARGETS
580
+ assert DEFAULT_BUILD_TARGETS.get() == self.SUPPORTED_TARGETS
581
+
582
+ def test_additional_build_targets_option(self):
583
+ """Test that --additional-build-targets option works correctly"""
584
+ additional_targets = ['esp32h2', 'esp32p4']
585
+ args = FindBuildArguments(additional_build_targets=additional_targets, paths=['.'])
586
+
587
+ expected_targets = sorted(self.SUPPORTED_TARGETS + additional_targets)
588
+ assert args.additional_build_targets == additional_targets
589
+ assert args.default_build_targets == expected_targets
590
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
591
+
592
+ def test_additional_build_targets_with_existing_target(self):
593
+ existing_target = 'esp32' # From self.SUPPORTED_TARGETS
594
+ new_target = 'esp32h2' # From self.ALL_TARGETS but not in self.SUPPORTED_TARGETS
595
+ additional_targets = [existing_target, new_target]
596
+
597
+ args = FindBuildArguments(additional_build_targets=additional_targets, paths=['.'])
598
+
599
+ # Should only add the new target, not duplicate the existing one
600
+ expected_targets = sorted([*self.SUPPORTED_TARGETS, new_target])
601
+ assert args.additional_build_targets == additional_targets
602
+ assert args.default_build_targets == expected_targets
603
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
604
+
605
+ def test_additional_build_targets_with_invalid_target(self):
606
+ invalid_targets = [
607
+ 'esp32',
608
+ 'invalid_target',
609
+ 'esp32h2',
610
+ ] # esp32 is already supported, invalid_target doesn't exist
611
+
612
+ args = FindBuildArguments(additional_build_targets=invalid_targets, paths=['.'])
613
+
614
+ # Should only add valid targets not already in SUPPORTED_TARGETS
615
+ expected_targets = sorted([*self.SUPPORTED_TARGETS, 'esp32h2'])
616
+ assert args.additional_build_targets == invalid_targets
617
+ assert args.default_build_targets == expected_targets
618
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
619
+
620
+ def test_disable_targets_with_additional_build_targets(self):
621
+ additional_target = 'esp32h2' # From self.ALL_TARGETS but not in self.SUPPORTED_TARGETS
622
+ disabled_target = 'esp32' # From self.SUPPORTED_TARGETS
623
+
624
+ args = FindBuildArguments(
625
+ additional_build_targets=[additional_target], disable_targets=[disabled_target], paths=['.']
626
+ )
627
+
628
+ expected_targets = ['esp32h2', 'esp32s2']
629
+ assert args.default_build_targets == expected_targets
630
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
631
+
632
+ def test_combination_default_and_additional(self):
633
+ args = FindBuildArguments(
634
+ default_build_targets=['esp32', 'esp32s2'], additional_build_targets=['esp32h2'], paths=['.']
635
+ )
636
+
637
+ expected_targets = ['esp32', 'esp32h2', 'esp32s2']
638
+ assert args.default_build_targets == expected_targets
639
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
640
+
641
+ def test_combination_preview_and_additional(self):
642
+ args = FindBuildArguments(enable_preview_targets=True, additional_build_targets=['esp32p4'], paths=['.'])
643
+
644
+ # Should combine: SUPPORTED_TARGETS + PREVIEW_TARGETS + additional
645
+ # self.PREVIEW_TARGETS contains 'esp32p4', so it's not really additional
646
+ expected_targets = sorted(self.SUPPORTED_TARGETS + self.PREVIEW_TARGETS)
647
+ assert args.default_build_targets == expected_targets
648
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
649
+
650
+ def test_combination_all_three_options(self):
651
+ args = FindBuildArguments(
652
+ default_build_targets=['esp32'],
653
+ enable_preview_targets=True,
654
+ additional_build_targets=['esp32p4', 'esp32c3'],
655
+ paths=['.'],
656
+ )
657
+
658
+ # Should combine: ['esp32'] + PREVIEW_TARGETS + ['esp32p4', 'esp32c3']
659
+ expected_targets = sorted({'esp32', *self.PREVIEW_TARGETS, 'esp32p4', 'esp32c3'})
660
+ assert args.default_build_targets == expected_targets
661
+ assert DEFAULT_BUILD_TARGETS.get() == expected_targets
662
+
663
+ def test_empty_additional_build_targets(self):
664
+ args = FindBuildArguments(additional_build_targets=[], paths=['.'])
665
+
666
+ assert args.additional_build_targets == []
667
+ assert args.default_build_targets == self.SUPPORTED_TARGETS
668
+ assert DEFAULT_BUILD_TARGETS.get() == self.SUPPORTED_TARGETS
547
669
 
548
670
 
549
671
  def test_expand_vars(monkeypatch):
@@ -504,6 +504,7 @@ class TestFindWithSdkconfigFiles:
504
504
  (tmp_path / 'test1' / 'sdkconfig.defaults').touch()
505
505
  (tmp_path / 'test1' / 'sdkconfig.defaults_new').touch()
506
506
  (tmp_path / 'test1' / 'sdkconfig.ci.foo').touch()
507
+ (tmp_path / 'test1' / 'sdkconfig.ci.foo.esp32p4').touch()
507
508
 
508
509
  apps = find_apps(str(tmp_path / 'test1'), 'esp32', recursive=True, config_rules_str='sdkconfig.ci.*=')
509
510
  assert len(apps) == 1
File without changes