idf-build-apps 2.10.1__tar.gz → 2.10.2__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 (71) hide show
  1. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.pre-commit-config.yaml +1 -1
  2. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/CHANGELOG.md +7 -0
  3. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/PKG-INFO +1 -1
  4. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/__init__.py +1 -1
  5. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/manifest/manifest.py +14 -4
  6. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/pyproject.toml +1 -1
  7. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/setup.py +1 -1
  8. idf_build_apps-2.10.2/tests/test_manifest.py +561 -0
  9. idf_build_apps-2.10.1/tests/test_manifest.py +0 -568
  10. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.editorconfig +0 -0
  11. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.git-blame-ignore-revs +0 -0
  12. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.gitattributes +0 -0
  13. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.github/dependabot.yml +0 -0
  14. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.github/workflows/publish-pypi.yml +0 -0
  15. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.github/workflows/sync-jira.yml +0 -0
  16. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.github/workflows/test-build-docs.yml +0 -0
  17. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.github/workflows/test-build-idf-apps.yml +0 -0
  18. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.gitignore +0 -0
  19. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/.readthedocs.yml +0 -0
  20. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/CONTRIBUTING.md +0 -0
  21. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/LICENSE +0 -0
  22. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/README.md +0 -0
  23. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/_apidoc_templates/module.rst_t +0 -0
  24. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/_apidoc_templates/package.rst_t +0 -0
  25. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/_apidoc_templates/toc.rst_t +0 -0
  26. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/_static/espressif-logo.svg +0 -0
  27. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/_static/theme_overrides.css +0 -0
  28. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/_templates/layout.html +0 -0
  29. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/conf_common.py +0 -0
  30. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/Makefile +0 -0
  31. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/conf.py +0 -0
  32. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/explanations/build.rst +0 -0
  33. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/explanations/config_rules.rst +0 -0
  34. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/explanations/dependency_driven_build.rst +0 -0
  35. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/explanations/find.rst +0 -0
  36. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/guides/1.x_to_2.x.md +0 -0
  37. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/guides/custom_app.md +0 -0
  38. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/index.rst +0 -0
  39. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/others/CHANGELOG.md +0 -0
  40. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/others/CONTRIBUTING.md +0 -0
  41. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/references/cli.rst +0 -0
  42. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/references/config_file.rst +0 -0
  43. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/docs/en/references/manifest.rst +0 -0
  44. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/__main__.py +0 -0
  45. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/app.py +0 -0
  46. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/args.py +0 -0
  47. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/autocompletions.py +0 -0
  48. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/constants.py +0 -0
  49. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/finder.py +0 -0
  50. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/junit/__init__.py +0 -0
  51. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/junit/report.py +0 -0
  52. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/junit/utils.py +0 -0
  53. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/log.py +0 -0
  54. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/main.py +0 -0
  55. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/manifest/__init__.py +0 -0
  56. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/manifest/soc_header.py +0 -0
  57. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/py.typed +0 -0
  58. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/session_args.py +0 -0
  59. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/utils.py +0 -0
  60. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/vendors/__init__.py +0 -0
  61. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/vendors/pydantic_sources.py +0 -0
  62. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/yaml/__init__.py +0 -0
  63. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/idf_build_apps/yaml/parser.py +0 -0
  64. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/license_header.txt +0 -0
  65. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/conftest.py +0 -0
  66. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/test_app.py +0 -0
  67. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/test_args.py +0 -0
  68. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/test_build.py +0 -0
  69. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/test_cmd.py +0 -0
  70. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/test_finder.py +0 -0
  71. {idf_build_apps-2.10.1 → idf_build_apps-2.10.2}/tests/test_utils.py +0 -0
@@ -17,7 +17,7 @@ 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.11.6'
20
+ rev: 'v0.11.10'
21
21
  hooks:
22
22
  - id: ruff
23
23
  args: ['--fix']
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## v2.10.2 (2025-05-22)
6
+
7
+ ### Perf
8
+
9
+ - `most_suitable_rule` stop searching till reached root dir
10
+ - pre-compute rules folder, reduced 50% time on `most_suitable_rule`
11
+
5
12
  ## v2.10.1 (2025-05-05)
6
13
 
7
14
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: idf-build-apps
3
- Version: 2.10.1
3
+ Version: 2.10.2
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.10.1'
11
+ __version__ = '2.10.2'
12
12
 
13
13
  from .session_args import (
14
14
  SessionArgs,
@@ -254,9 +254,11 @@ class Manifest:
254
254
 
255
255
  def __init__(self, rules: t.Iterable[FolderRule], *, root_path: str = os.curdir) -> None:
256
256
  self.rules = sorted(rules, key=lambda x: x.folder)
257
-
258
257
  self._root_path = to_absolute_path(root_path)
259
258
 
259
+ # Pre-compute rule paths
260
+ self._rule_paths = {rule.folder: rule for rule in self.rules}
261
+
260
262
  @classmethod
261
263
  def from_files(cls, paths: t.Iterable[PathLike], *, root_path: str = os.curdir) -> 'Manifest':
262
264
  """
@@ -383,9 +385,17 @@ class Manifest:
383
385
 
384
386
  def most_suitable_rule(self, _folder: str) -> FolderRule:
385
387
  folder = to_absolute_path(_folder)
386
- for rule in self.rules[::-1]:
387
- if os.path.commonpath([folder, rule.folder]) == rule.folder:
388
- return rule
388
+
389
+ while True:
390
+ if folder in self._rule_paths:
391
+ return self._rule_paths[folder]
392
+ folder = os.path.dirname(folder)
393
+
394
+ # reached the root path, stop searching
395
+ if folder == self._root_path:
396
+ if folder in self._rule_paths:
397
+ return self._rule_paths[folder]
398
+ break
389
399
 
390
400
  return DefaultRule(folder)
391
401
 
@@ -65,7 +65,7 @@ idf-build-apps = "idf_build_apps:main.main"
65
65
 
66
66
  [tool.commitizen]
67
67
  name = "cz_conventional_commits"
68
- version = "2.10.1"
68
+ version = "2.10.2"
69
69
  tag_format = "v$version"
70
70
  version_files = [
71
71
  "idf_build_apps/__init__.py",
@@ -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.10.1',
41
+ version='2.10.2',
42
42
  description='Tools for building ESP-IDF related apps.',
43
43
  author=None,
44
44
  author_email='Fu Hanxi <fuhanxi@espressif.com>',
@@ -0,0 +1,561 @@
1
+ # SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ import os
4
+
5
+ import esp_bool_parser
6
+ import pytest
7
+ import yaml
8
+ from esp_bool_parser.utils import to_version
9
+
10
+ import idf_build_apps
11
+ from idf_build_apps import setup_logging
12
+ from idf_build_apps.constants import (
13
+ SUPPORTED_TARGETS,
14
+ )
15
+ from idf_build_apps.manifest.manifest import (
16
+ IfClause,
17
+ Manifest,
18
+ )
19
+ from idf_build_apps.utils import (
20
+ InvalidIfClause,
21
+ InvalidManifest,
22
+ )
23
+ from idf_build_apps.yaml import (
24
+ parse,
25
+ )
26
+
27
+
28
+ class TestManifest:
29
+ @pytest.fixture(autouse=True)
30
+ def _setup_version(self, monkeypatch):
31
+ """Setup ESP-IDF version for all tests in this class."""
32
+ monkeypatch.setattr(esp_bool_parser.constants, 'IDF_VERSION', to_version('5.9.0'))
33
+ monkeypatch.setattr(esp_bool_parser.constants, 'IDF_VERSION_MAJOR', 5)
34
+ monkeypatch.setattr(esp_bool_parser.constants, 'IDF_VERSION_MINOR', 9)
35
+ monkeypatch.setattr(esp_bool_parser.constants, 'IDF_VERSION_PATCH', 0)
36
+
37
+ def test_manifest_from_file_warning(self, tmp_path, capsys, monkeypatch):
38
+ setup_logging()
39
+ yaml_file = tmp_path / 'test.yml'
40
+ yaml_file.write_text(
41
+ """
42
+ test1:
43
+ enable:
44
+ - if: IDF_TARGET == "esp32" or IDF_TARGET == "esp32c3"
45
+ - if: IDF_TARGET == "esp32s2" and IDF_VERSION_MAJOR in [4, 5]
46
+ disable_test:
47
+ - if: IDF_TARGET == "esp32c3"
48
+
49
+ test2:
50
+ enable:
51
+ - if: INCLUDE_DEFAULT == 0 and IDF_TARGET == "linux"
52
+ """,
53
+ encoding='utf8',
54
+ )
55
+
56
+ manifest = Manifest.from_file(yaml_file, root_path=tmp_path)
57
+ captured_err = capsys.readouterr().err.splitlines()
58
+ msg_fmt = 'Folder "{}" does not exist. Please check your manifest file {}'
59
+ # two warnings warn test1 test2 not exists
60
+ assert len(captured_err) == 2
61
+ assert msg_fmt.format(os.path.join(tmp_path, 'test1'), yaml_file) in captured_err[0]
62
+ assert msg_fmt.format(os.path.join(tmp_path, 'test2'), yaml_file) in captured_err[1]
63
+
64
+ assert manifest.enable_build_targets('test1') == ['esp32', 'esp32c3', 'esp32s2']
65
+ assert manifest.enable_test_targets('test1') == ['esp32', 'esp32s2']
66
+ assert manifest.enable_build_targets('test2') == ['linux']
67
+ assert manifest.enable_test_targets('test2') == ['linux']
68
+
69
+ monkeypatch.setattr(idf_build_apps.manifest.manifest.Manifest, 'CHECK_MANIFEST_RULES', True)
70
+ with pytest.raises(InvalidManifest, match=msg_fmt.format(os.path.join(tmp_path, 'test1'), yaml_file)):
71
+ Manifest.from_file(yaml_file, root_path=tmp_path)
72
+
73
+ # test with folder that has the same prefix as one of the folders in the manifest
74
+ assert manifest.enable_build_targets('test23') == sorted(SUPPORTED_TARGETS)
75
+
76
+ def test_repr(self, tmp_path):
77
+ yaml_file = tmp_path / 'test.yml'
78
+
79
+ yaml_file.write_text(
80
+ """
81
+ test1:
82
+ enable:
83
+ - if: IDF_TARGET == "esp32c3"
84
+ reason: "None"
85
+ depends_components:
86
+ - if: IDF_VERSION == "1.2.3" or IDF_VERSION_MAJOR == 4
87
+ content: [ "VVV" ]
88
+ - if: CONFIG_NAME == "AAA"
89
+ content: [ "AAA" ]
90
+ - default: ["some_1", "some_2", "some_3"]
91
+
92
+ """,
93
+ encoding='utf8',
94
+ )
95
+
96
+ manifest = Manifest.from_file(yaml_file)
97
+ manifest_rule = manifest.rules[0]
98
+ assert (
99
+ repr(manifest_rule.enable)
100
+ == """[IfClause(stmt='IDF_TARGET == "esp32c3"', temporary=False, reason='None')]"""
101
+ )
102
+ assert (
103
+ repr(manifest_rule.depends_components)
104
+ == """SwitchClause(if_clauses=[IfClause(stmt='IDF_VERSION == "1.2.3" or IDF_VERSION_MAJOR == 4', temporary=False, reason=None), IfClause(stmt='CONFIG_NAME == "AAA"', temporary=False, reason=None)], contents=[['VVV'], ['AAA']], default_clause=['some_1', 'some_2', 'some_3'])""" # noqa
105
+ )
106
+
107
+ def test_manifest_switch_clause(self, tmp_path):
108
+ yaml_file = tmp_path / 'test.yml'
109
+ yaml_file.write_text(
110
+ """
111
+ test1:
112
+ depends_components:
113
+ - if: IDF_VERSION == "5.9.0"
114
+ content: [ "VVV" ]
115
+ - if: CONFIG_NAME == "AAA"
116
+ content: [ "AAA" ]
117
+ - default: ["some_1", "some_2", "some_3"]
118
+
119
+ test2:
120
+ depends_components:
121
+ - if: IDF_TARGET == "esp32"
122
+ content: [ "esp32" ]
123
+ - if: CONFIG_NAME == "AAA"
124
+ content: [ "AAA" ]
125
+ - if: IDF_VERSION == "5.9.0"
126
+ content: [ "VVV" ]
127
+ - default: ["some_1", "some_2", "some_3"]
128
+
129
+ test3:
130
+ depends_components:
131
+ - if: CONFIG_NAME == "AAA"
132
+ content: [ "AAA" ]
133
+ - if: CONFIG_NAME == "BBB"
134
+ content: [ "BBB" ]
135
+ - default: ["some_1", "some_2", "some_3"]
136
+
137
+ test4:
138
+ depends_components:
139
+ - if: CONFIG_NAME == "BBB"
140
+ content: [ "BBB" ]
141
+ - if: CONFIG_NAME == "AAA"
142
+ content: [ "AAA" ]
143
+
144
+ test5:
145
+ depends_components:
146
+ - "some_1"
147
+ - "some_2"
148
+ - "some_3"
149
+
150
+ """,
151
+ encoding='utf8',
152
+ )
153
+
154
+ manifest = Manifest.from_file(yaml_file)
155
+
156
+ assert manifest.depends_components('test1', None, None) == ['VVV']
157
+ assert manifest.depends_components('test1', None, 'AAA') == ['VVV']
158
+
159
+ assert manifest.depends_components('test2', 'esp32', None) == ['esp32']
160
+ assert manifest.depends_components('test2', None, 'AAA') == ['AAA']
161
+ assert manifest.depends_components('test2', 'esp32', 'AAA') == ['esp32']
162
+ assert manifest.depends_components('test2', None, None) == ['VVV']
163
+
164
+ assert manifest.depends_components('test3', 'esp32', 'AAA') == ['AAA']
165
+ assert manifest.depends_components('test3', 'esp32', 'BBB') == ['BBB']
166
+ assert manifest.depends_components('test3', 'esp32', '123123') == ['some_1', 'some_2', 'some_3']
167
+ assert manifest.depends_components('test3', None, None) == ['some_1', 'some_2', 'some_3']
168
+
169
+ assert manifest.depends_components('test4', 'esp32', 'AAA') == ['AAA']
170
+ assert manifest.depends_components('test4', 'esp32', 'BBB') == ['BBB']
171
+ assert manifest.depends_components('test4', 'esp32', '123123') == []
172
+ assert manifest.depends_components('test4', None, None) == []
173
+
174
+ assert manifest.depends_components('test5', 'esp32', 'AAA') == ['some_1', 'some_2', 'some_3']
175
+ assert manifest.depends_components('test5', 'esp32', 'BBB') == ['some_1', 'some_2', 'some_3']
176
+ assert manifest.depends_components('test5', 'esp32', '123123') == ['some_1', 'some_2', 'some_3']
177
+ assert manifest.depends_components('test5', None, None) == ['some_1', 'some_2', 'some_3']
178
+
179
+ def test_manifest_switch_clause_with_postfix(self, tmp_path):
180
+ yaml_file = tmp_path / 'test.yml'
181
+
182
+ yaml_file.write_text(
183
+ """
184
+ .test: &test
185
+ depends_components+:
186
+ - if: CONFIG_NAME == "AAA"
187
+ content: ["NEW_AAA"]
188
+ - if: CONFIG_NAME == "BBB"
189
+ content: ["NEW_BBB"]
190
+ depends_components-:
191
+ - if: CONFIG_NAME == "CCC"
192
+
193
+ test1:
194
+ <<: *test
195
+ depends_components:
196
+ - if: CONFIG_NAME == "AAA"
197
+ content: [ "AAA" ]
198
+ - if: CONFIG_NAME == "CCC"
199
+ content: [ "CCC" ]
200
+ - default: ["DF"]
201
+ """,
202
+ encoding='utf8',
203
+ )
204
+ manifest = Manifest.from_file(yaml_file, root_path=tmp_path)
205
+
206
+ assert manifest.depends_components('test1', None, None) == ['DF']
207
+ assert manifest.depends_components('test1', None, 'CCC') == ['DF']
208
+ assert manifest.depends_components('test1', None, 'AAA') == ['NEW_AAA']
209
+ assert manifest.depends_components('test1', None, 'BBB') == ['NEW_BBB']
210
+
211
+ def test_manifest_switch_clause_wrong_manifest_format(self, tmp_path):
212
+ yaml_file = tmp_path / 'test.yml'
213
+
214
+ yaml_file.write_text(
215
+ """
216
+ test1:
217
+ depends_components:
218
+ - if: IDF_VERSION == "5.9.0"
219
+ content: [ "VVV" ]
220
+ - default: ["some_1", "some_2", "some_3"]
221
+ - hello: 123
222
+
223
+ """,
224
+ encoding='utf8',
225
+ )
226
+ try:
227
+ with pytest.warns(UserWarning, match='Folder ".+" does not exist. Please check your manifest file'):
228
+ Manifest.from_file(yaml_file)
229
+ except InvalidManifest as e:
230
+ assert str(e) == "Only the 'if' and 'default' keywords are supported in switch clause."
231
+
232
+ yaml_file.write_text(
233
+ """
234
+ test1:
235
+ depends_components:
236
+ - if: IDF_VERSION == "5.9.0"
237
+ content: [ "VVV" ]
238
+ - default: ["some_1", "some_2", "some_3"]
239
+ - 123
240
+ - 234
241
+ """,
242
+ encoding='utf8',
243
+ )
244
+ try:
245
+ Manifest.from_file(yaml_file)
246
+ except InvalidManifest as e:
247
+ assert str(e) == 'Current manifest format has to fit either the switch format or the list format.'
248
+
249
+ def test_manifest_with_anchor(self, tmp_path, monkeypatch):
250
+ yaml_file = tmp_path / 'test.yml'
251
+ yaml_file.write_text(
252
+ """
253
+ .base: &base
254
+ depends_components:
255
+ - a
256
+
257
+ foo: &foo
258
+ <<: *base
259
+ disable:
260
+ - if: IDF_TARGET == "esp32"
261
+
262
+ bar:
263
+ <<: *foo
264
+ """,
265
+ encoding='utf8',
266
+ )
267
+
268
+ monkeypatch.setattr(idf_build_apps.manifest.manifest.FolderRule, 'DEFAULT_BUILD_TARGETS', ['esp32'])
269
+ manifest = Manifest.from_file(yaml_file)
270
+ assert manifest.enable_build_targets('bar') == []
271
+
272
+ def test_manifest_with_anchor_and_postfix(self, tmp_path):
273
+ yaml_file = tmp_path / 'test.yml'
274
+
275
+ yaml_file.write_text(
276
+ """
277
+ foo:
278
+ """,
279
+ encoding='utf8',
280
+ )
281
+ manifest = Manifest.from_file(yaml_file)
282
+ assert manifest.enable_build_targets('foo') == sorted(SUPPORTED_TARGETS)
283
+
284
+ yaml_file.write_text(
285
+ """
286
+ .base_depends_components: &base-depends-components
287
+ depends_components:
288
+ - esp_hw_support
289
+ - esp_rom
290
+ - esp_wifi
291
+
292
+ examples/wifi/coexist:
293
+ <<: *base-depends-components
294
+ depends_components+:
295
+ - esp_coex
296
+ depends_components-:
297
+ - esp_rom
298
+ """,
299
+ encoding='utf8',
300
+ )
301
+
302
+ manifest = Manifest.from_file(yaml_file)
303
+ assert manifest.depends_components('examples/wifi/coexist') == ['esp_coex', 'esp_hw_support', 'esp_wifi']
304
+
305
+ yaml_file.write_text(
306
+ """
307
+ .base: &base
308
+ enable:
309
+ - if: IDF_VERSION == "5.2.0"
310
+ - if: IDF_VERSION == "5.3.0"
311
+
312
+ foo:
313
+ <<: *base
314
+ enable+:
315
+ - if: IDF_VERSION == "5.2.0"
316
+ temp: true
317
+ - if: IDF_VERSION == "5.4.0"
318
+ reason: bar
319
+ """,
320
+ encoding='utf8',
321
+ )
322
+ s_manifest_dict = parse(yaml_file)
323
+
324
+ yaml_file.write_text(
325
+ """
326
+ foo:
327
+ enable:
328
+ - if: IDF_VERSION == "5.3.0"
329
+ - if: IDF_VERSION == "5.2.0"
330
+ temp: true
331
+ - if: IDF_VERSION == "5.4.0"
332
+ reason: bar
333
+ """,
334
+ encoding='utf8',
335
+ )
336
+ with open(yaml_file) as f:
337
+ manifest_dict = yaml.safe_load(f) or {}
338
+
339
+ assert s_manifest_dict['foo'] == manifest_dict['foo']
340
+
341
+ yaml_file.write_text(
342
+ """
343
+ .base: &base
344
+ enable:
345
+ - if: IDF_VERSION == "5.3.0"
346
+
347
+ foo:
348
+ <<: *base
349
+ enable+:
350
+ - if: IDF_VERSION == "5.2.0"
351
+ temp: true
352
+ - if: IDF_VERSION == "5.4.0"
353
+ reason: bar
354
+ """,
355
+ encoding='utf8',
356
+ )
357
+
358
+ s_manifest_dict = parse(yaml_file)
359
+
360
+ yaml_file.write_text(
361
+ """
362
+ foo:
363
+ enable:
364
+ - if: IDF_VERSION == "5.3.0"
365
+ - if: IDF_VERSION == "5.2.0"
366
+ temp: true
367
+ - if: IDF_VERSION == "5.4.0"
368
+ reason: bar
369
+ """,
370
+ encoding='utf8',
371
+ )
372
+ with open(yaml_file) as f:
373
+ manifest_dict = yaml.safe_load(f) or {}
374
+
375
+ assert s_manifest_dict['foo'] == manifest_dict['foo']
376
+
377
+ yaml_file.write_text(
378
+ """
379
+ .test: &test
380
+ depends_components:
381
+ - a
382
+ - b
383
+
384
+ foo:
385
+ <<: *test
386
+ depends_components+:
387
+ - c
388
+
389
+ bar:
390
+ <<: *test
391
+ depends_components+:
392
+ - d
393
+ """,
394
+ encoding='utf8',
395
+ )
396
+ s_manifest_dict = parse(yaml_file)
397
+ foo = s_manifest_dict['foo']
398
+ bar = s_manifest_dict['bar']
399
+ assert foo['depends_components'] == ['a', 'b', 'c']
400
+ assert bar['depends_components'] == ['a', 'b', 'd']
401
+ assert id(foo['depends_components']) != id(bar['depends_components'])
402
+
403
+ yaml_file.write_text(
404
+ """
405
+ .test: &test
406
+ depends_components:
407
+ - if: 1
408
+ value: 123
409
+
410
+ foo:
411
+ <<: *test
412
+ depends_components+:
413
+ - if: 2
414
+ value: 234
415
+
416
+ bar:
417
+ <<: *test
418
+ depends_components+:
419
+ - if: 2
420
+ value: 345
421
+ """,
422
+ encoding='utf8',
423
+ )
424
+ s_manifest_dict = parse(yaml_file)
425
+ foo = s_manifest_dict['foo']
426
+ bar = s_manifest_dict['bar']
427
+ assert id(foo['depends_components']) != id(bar['depends_components'])
428
+ print(s_manifest_dict)
429
+
430
+ def test_manifest_postfix_order(self, tmp_path):
431
+ yaml_file = tmp_path / 'test.yml'
432
+ yaml_file.write_text(
433
+ """
434
+ .base_depends_components: &base-depends-components
435
+ depends_components:
436
+ - esp_hw_support
437
+
438
+ examples/wifi/coexist:
439
+ <<: *base-depends-components
440
+ depends_components+:
441
+ - esp_coex
442
+ depends_components-:
443
+ - esp_coex
444
+ """,
445
+ encoding='utf8',
446
+ )
447
+
448
+ manifest = Manifest.from_file(yaml_file)
449
+ assert manifest.depends_components('examples/wifi/coexist') == ['esp_hw_support']
450
+
451
+ def test_from_files_duplicates(self, tmp_path, monkeypatch):
452
+ yaml_file_1 = tmp_path / 'test1.yml'
453
+ yaml_file_1.write_text(
454
+ """
455
+ foo:
456
+ enable:
457
+ - if: IDF_TARGET == "esp32"
458
+ """,
459
+ encoding='utf8',
460
+ )
461
+
462
+ yaml_file_2 = tmp_path / 'test2.yml'
463
+ yaml_file_2.write_text(
464
+ """
465
+ foo:
466
+ enable:
467
+ - if: IDF_TARGET == "esp32"
468
+ """,
469
+ encoding='utf8',
470
+ )
471
+
472
+ monkeypatch.setattr(idf_build_apps.manifest.manifest.Manifest, 'CHECK_MANIFEST_RULES', True)
473
+ folder_path = os.path.join(os.getcwd(), 'foo')
474
+ os.makedirs(folder_path)
475
+
476
+ with pytest.raises(InvalidManifest, match=f'Folder "{folder_path}" is already defined in {yaml_file_1!s}'):
477
+ Manifest.from_files([str(yaml_file_1), str(yaml_file_2)])
478
+
479
+ monkeypatch.setattr(idf_build_apps.manifest.manifest.Manifest, 'CHECK_MANIFEST_RULES', False)
480
+ Manifest.from_files([str(yaml_file_1), str(yaml_file_2)])
481
+
482
+ def test_manifest_dump_sha(self, tmp_path, sha_of_enable_only_esp32):
483
+ yaml_file = tmp_path / 'test.yml'
484
+ yaml_file.write_text(
485
+ """
486
+ foo:
487
+ enable:
488
+ - if: IDF_TARGET == "esp32"
489
+ bar:
490
+ enable:
491
+ - if: IDF_TARGET == "esp32"
492
+ """,
493
+ encoding='utf8',
494
+ )
495
+
496
+ Manifest.from_file(yaml_file).dump_sha_values(str(tmp_path / '.sha'))
497
+
498
+ with open('.sha') as f:
499
+ assert f.readline() == f'bar:{sha_of_enable_only_esp32}\n'
500
+ assert f.readline() == f'foo:{sha_of_enable_only_esp32}\n'
501
+
502
+ def test_manifest_diff_sha(self, tmp_path, sha_of_enable_only_esp32):
503
+ yaml_file = tmp_path / 'test.yml'
504
+ yaml_file.write_text(
505
+ """
506
+ foo:
507
+ enable:
508
+ - if: IDF_TARGET == "esp32"
509
+ - if: IDF_TARGET == "esp32c3"
510
+ bar:
511
+ enable:
512
+ - if: IDF_TARGET == "esp32"
513
+ baz:
514
+ enable:
515
+ - if: IDF_TARGET == "esp32"
516
+ """,
517
+ encoding='utf8',
518
+ )
519
+
520
+ with open('.sha', 'w') as fw:
521
+ fw.write(f'bar:{sha_of_enable_only_esp32}\n')
522
+ fw.write('\n') # test empty line
523
+ fw.write(' ') # test spaces
524
+ fw.write(f'foo:{sha_of_enable_only_esp32}\n')
525
+
526
+ assert Manifest.from_file(yaml_file).diff_sha_with_filepath(str(tmp_path / '.sha')) == {
527
+ 'baz',
528
+ 'foo',
529
+ }
530
+
531
+ def test_folder_rule_introduced_by(self, tmp_path):
532
+ yaml_file = tmp_path / 'test.yml'
533
+ yaml_file.write_text(
534
+ """
535
+ foo:
536
+ enable:
537
+ - if: IDF_TARGET == "esp32"
538
+ - if: IDF_TARGET == "esp32c3"
539
+ bar:
540
+ enable:
541
+ - if: IDF_TARGET == "esp32"
542
+ baz:
543
+ enable:
544
+ - if: IDF_TARGET == "esp32"
545
+ """,
546
+ encoding='utf8',
547
+ )
548
+
549
+ manifest = Manifest.from_file(yaml_file)
550
+ assert manifest.most_suitable_rule('baz').by_manifest_file == str(yaml_file)
551
+
552
+
553
+ class TestIfParser:
554
+ def test_invalid_if_statement(self):
555
+ statement = '1'
556
+ with pytest.raises(InvalidIfClause, match='Invalid if clause: 1'):
557
+ IfClause(statement)
558
+
559
+ def test_temporary_must_with_reason(self):
560
+ with pytest.raises(InvalidIfClause, match='"reason" must be set when "temporary: true"'):
561
+ IfClause(stmt='IDF_TARGET == "esp32"', temporary=True)