idf-build-apps 2.4.2__py3-none-any.whl → 2.5.0rc0__py3-none-any.whl

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.
@@ -1,10 +1,10 @@
1
1
  # SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2
2
  # SPDX-License-Identifier: Apache-2.0
3
-
4
3
  import logging
5
4
  import os
5
+ import pickle
6
6
  import typing as t
7
- import warnings
7
+ from hashlib import sha512
8
8
 
9
9
  from pyparsing import (
10
10
  ParseException,
@@ -17,6 +17,7 @@ from ..constants import (
17
17
  from ..utils import (
18
18
  InvalidIfClause,
19
19
  InvalidManifest,
20
+ to_absolute_path,
20
21
  )
21
22
  from ..yaml import (
22
23
  parse,
@@ -127,7 +128,26 @@ class FolderRule:
127
128
  self.depends_filepatterns = _clause_to_switch_or_list(depends_filepatterns)
128
129
 
129
130
  def __hash__(self) -> int:
130
- return hash(self.folder)
131
+ return hash(self.sha)
132
+
133
+ @property
134
+ def sha(self) -> str:
135
+ """
136
+ SHA of the FolderRule instance
137
+
138
+ :return: SHA value
139
+ """
140
+ sha = sha512()
141
+ for obj in [
142
+ self.enable,
143
+ self.disable,
144
+ self.disable_test,
145
+ self.depends_components,
146
+ self.depends_filepatterns,
147
+ ]:
148
+ sha.update(pickle.dumps(obj))
149
+
150
+ return sha.hexdigest()
131
151
 
132
152
  def __repr__(self) -> str:
133
153
  return f'FolderRule({self.folder})'
@@ -207,23 +227,28 @@ class DefaultRule(FolderRule):
207
227
 
208
228
  class Manifest:
209
229
  # could be reassigned later
210
- ROOTPATH = os.curdir
211
230
  CHECK_MANIFEST_RULES = False
212
231
 
213
- def __init__(
214
- self,
215
- rules: t.Iterable[FolderRule],
216
- ) -> None:
232
+ def __init__(self, rules: t.Iterable[FolderRule], *, root_path: str = os.curdir) -> None:
217
233
  self.rules = sorted(rules, key=lambda x: x.folder)
218
234
 
235
+ self._root_path = to_absolute_path(root_path)
236
+
219
237
  @classmethod
220
- def from_files(cls, paths: t.List[str]) -> 'Manifest':
221
- # folder, defined_at dict
238
+ def from_files(cls, paths: t.Iterable[str], *, root_path: str = os.curdir) -> 'Manifest':
239
+ """
240
+ Create a Manifest instance from multiple manifest files
241
+
242
+ :param paths: manifest file paths
243
+ :param root_path: root path for relative paths in manifest files
244
+ :return: Manifest instance
245
+ """
246
+ # folder, defined as dict
222
247
  _known_folders: t.Dict[str, str] = dict()
223
248
 
224
249
  rules: t.List[FolderRule] = []
225
250
  for path in paths:
226
- _manifest = cls.from_file(path)
251
+ _manifest = cls.from_file(path, root_path=root_path)
227
252
 
228
253
  for rule in _manifest.rules:
229
254
  if rule.folder in _known_folders:
@@ -231,43 +256,106 @@ class Manifest:
231
256
  if cls.CHECK_MANIFEST_RULES:
232
257
  raise InvalidManifest(msg)
233
258
  else:
234
- warnings.warn(msg)
259
+ LOGGER.warning(msg)
235
260
 
236
261
  _known_folders[rule.folder] = path
237
262
 
238
263
  rules.extend(_manifest.rules)
239
264
 
240
- return Manifest(rules)
265
+ return Manifest(rules, root_path=root_path)
241
266
 
242
267
  @classmethod
243
- def from_file(cls, path: str) -> 'Manifest':
268
+ def from_file(cls, path: str, *, root_path: str = os.curdir) -> 'Manifest':
269
+ """
270
+ Create a Manifest instance from a manifest file
271
+
272
+ :param path: path to the manifest file
273
+ :param root_path: root path for relative paths in manifest file
274
+ :return: Manifest instance
275
+ """
244
276
  manifest_dict = parse(path)
245
277
 
246
278
  rules: t.List[FolderRule] = []
247
279
  for folder, folder_rule in manifest_dict.items():
248
- # not a folder, but a anchor
280
+ # not a folder, but an anchor
249
281
  if folder.startswith('.'):
250
282
  continue
251
283
 
252
284
  if not os.path.isabs(folder):
253
- folder = os.path.join(cls.ROOTPATH, folder)
285
+ folder = os.path.join(root_path, folder)
254
286
 
255
287
  if not os.path.exists(folder):
256
288
  msg = f'Folder "{folder}" does not exist. Please check your manifest file {path}'
257
289
  if cls.CHECK_MANIFEST_RULES:
258
290
  raise InvalidManifest(msg)
259
291
  else:
260
- warnings.warn(msg)
292
+ LOGGER.warning(msg)
261
293
 
262
294
  try:
263
295
  rules.append(FolderRule(folder, **folder_rule if folder_rule else {}))
264
296
  except InvalidIfClause as e:
265
297
  raise InvalidManifest(f'Invalid manifest file {path}: {e}')
266
298
 
267
- return Manifest(rules)
299
+ return Manifest(rules, root_path=root_path)
300
+
301
+ def dump_sha_values(self, sha_filepath: str) -> None:
302
+ """
303
+ Dump the (relative path of the folder, SHA of the FolderRule instance) pairs
304
+ for all rules to the file in format: ``<relative_path>:<SHA>``
305
+
306
+ :param sha_filepath: output file path
307
+ :return: None
308
+ """
309
+ with open(sha_filepath, 'w') as fw:
310
+ for rule in self.rules:
311
+ fw.write(f'{os.path.relpath(rule.folder, self._root_path)}:{rule.sha}\n')
312
+
313
+ def diff_sha_with_filepath(self, sha_filepath: str, use_abspath: bool = False) -> t.Set[str]:
314
+ """
315
+ Compare the SHA recorded in the file with the current Manifest instance.
316
+
317
+ :param sha_filepath: dumped SHA file path
318
+ :param use_abspath: whether to return the absolute path of the folders
319
+ :return: Set of folders that have different SHA values
320
+ """
321
+ recorded__rel_folder__sha__dict = dict()
322
+ with open(sha_filepath) as fr:
323
+ for line in fr:
324
+ line = line.strip()
325
+ if line:
326
+ try:
327
+ folder, sha_value = line.strip().rsplit(':', maxsplit=1)
328
+ except ValueError:
329
+ raise InvalidManifest(f'Invalid line in SHA file: {line}. Expected format: <folder>:<SHA>')
330
+
331
+ recorded__rel_folder__sha__dict[folder] = sha_value
332
+
333
+ self__rel_folder__sha__dict = {os.path.relpath(rule.folder, self._root_path): rule.sha for rule in self.rules}
334
+
335
+ diff_folders = set()
336
+ if use_abspath:
337
+
338
+ def _path(x):
339
+ return os.path.join(self._root_path, x)
340
+ else:
341
+
342
+ def _path(x):
343
+ return x
344
+
345
+ for folder, sha_value in recorded__rel_folder__sha__dict.items():
346
+ if folder not in self__rel_folder__sha__dict:
347
+ diff_folders.add(_path(folder))
348
+ elif sha_value != self__rel_folder__sha__dict[folder]:
349
+ diff_folders.add(_path(folder))
350
+
351
+ for folder in self__rel_folder__sha__dict:
352
+ if folder not in recorded__rel_folder__sha__dict:
353
+ diff_folders.add(_path(folder))
354
+
355
+ return diff_folders
268
356
 
269
- def _most_suitable_rule(self, _folder: str) -> FolderRule:
270
- folder = os.path.abspath(_folder)
357
+ def most_suitable_rule(self, _folder: str) -> FolderRule:
358
+ folder = to_absolute_path(_folder)
271
359
  for rule in self.rules[::-1]:
272
360
  if os.path.commonpath([folder, rule.folder]) == rule.folder:
273
361
  return rule
@@ -277,17 +365,17 @@ class Manifest:
277
365
  def enable_build_targets(
278
366
  self, folder: str, default_sdkconfig_target: t.Optional[str] = None, config_name: t.Optional[str] = None
279
367
  ) -> t.List[str]:
280
- return self._most_suitable_rule(folder).enable_build_targets(default_sdkconfig_target, config_name)
368
+ return self.most_suitable_rule(folder).enable_build_targets(default_sdkconfig_target, config_name)
281
369
 
282
370
  def enable_test_targets(
283
371
  self, folder: str, default_sdkconfig_target: t.Optional[str] = None, config_name: t.Optional[str] = None
284
372
  ) -> t.List[str]:
285
- return self._most_suitable_rule(folder).enable_test_targets(default_sdkconfig_target, config_name)
373
+ return self.most_suitable_rule(folder).enable_test_targets(default_sdkconfig_target, config_name)
286
374
 
287
375
  def depends_components(
288
376
  self, folder: str, default_sdkconfig_target: t.Optional[str] = None, config_name: t.Optional[str] = None
289
377
  ) -> t.List[str]:
290
- res = self._most_suitable_rule(folder).depends_components
378
+ res = self.most_suitable_rule(folder).depends_components
291
379
  if isinstance(res, list):
292
380
  return res
293
381
  return res.get_value(default_sdkconfig_target or '', config_name or '')
@@ -295,7 +383,7 @@ class Manifest:
295
383
  def depends_filepatterns(
296
384
  self, folder: str, default_sdkconfig_target: t.Optional[str] = None, config_name: t.Optional[str] = None
297
385
  ) -> t.List[str]:
298
- res = self._most_suitable_rule(folder).depends_filepatterns
386
+ res = self.most_suitable_rule(folder).depends_filepatterns
299
387
  if isinstance(res, list):
300
388
  return res
301
389
  return res.get_value(default_sdkconfig_target or '', config_name or '')
idf_build_apps/utils.py CHANGED
@@ -21,6 +21,24 @@ from pydantic import BaseModel as _BaseModel
21
21
 
22
22
  LOGGER = logging.getLogger(__name__)
23
23
 
24
+ if sys.version_info < (3, 8):
25
+ from typing_extensions import (
26
+ Literal,
27
+ )
28
+ else:
29
+ from typing import (
30
+ Literal, # noqa
31
+ )
32
+
33
+ if sys.version_info < (3, 11):
34
+ from typing_extensions import (
35
+ Self,
36
+ )
37
+ else:
38
+ from typing import (
39
+ Self, # noqa
40
+ )
41
+
24
42
 
25
43
  class ConfigRule:
26
44
  def __init__(self, file_name: str, config_name: str = '') -> None:
@@ -365,3 +383,7 @@ class BaseModel(_BaseModel):
365
383
  hash_list.append(v)
366
384
 
367
385
  return hash((type(self), *tuple(hash_list)))
386
+
387
+
388
+ def drop_none_kwargs(d: dict) -> dict:
389
+ return {k: v for k, v in d.items() if v is not None}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: idf-build-apps
3
- Version: 2.4.2
3
+ Version: 2.5.0rc0
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
@@ -19,7 +19,7 @@ Requires-Dist: packaging
19
19
  Requires-Dist: toml; python_version < '3.11'
20
20
  Requires-Dist: pydantic~=2.0
21
21
  Requires-Dist: argcomplete>=3
22
- Requires-Dist: typing-extensions ; extra == "dev" and ( python_version < '3.8')
22
+ Requires-Dist: typing-extensions; python_version < '3.11'
23
23
  Requires-Dist: sphinx ; extra == "doc"
24
24
  Requires-Dist: sphinx-rtd-theme ; extra == "doc"
25
25
  Requires-Dist: sphinx_copybutton ; extra == "doc"
@@ -32,7 +32,6 @@ Project-URL: changelog, https://github.com/espressif/idf-build-apps/blob/master/
32
32
  Project-URL: documentation, https://docs.espressif.com/projects/idf-build-apps
33
33
  Project-URL: homepage, https://github.com/espressif/idf-build-apps
34
34
  Project-URL: repository, https://github.com/espressif/idf-build-apps
35
- Provides-Extra: dev
36
35
  Provides-Extra: doc
37
36
  Provides-Extra: test
38
37
 
@@ -113,5 +112,5 @@ Thanks for your contribution! Please refer to our [Contributing Guide](CONTRIBUT
113
112
  [hello-world]: https://github.com/espressif/esp-idf/tree/master/examples/get-started/hello_world
114
113
  [supported-targets]: https://github.com/espressif/esp-idf/tree/v5.0#esp-idf-release-and-soc-compatibility
115
114
  [doc]: https://docs.espressif.com/projects/idf-build-apps/en/latest/
116
- [api-doc]: https://docs.espressif.com/projects/idf-build-apps/en/latest/api/modules.html
115
+ [api-doc]: https://docs.espressif.com/projects/idf-build-apps/en/latest/references/api/modules.html
117
116
 
@@ -0,0 +1,26 @@
1
+ idf_build_apps/__init__.py,sha256=RPw7wTPPVr_WwSjWSYSAAdmKxRoFeZfkmvSrEE8cLcQ,653
2
+ idf_build_apps/__main__.py,sha256=8E-5xHm2MlRun0L88XJleNh5U50dpE0Q1nK5KqomA7I,182
3
+ idf_build_apps/app.py,sha256=T4quqTJ1F40QyAfr5aNZcPpvRBJOfc10hXCk1RMG8fo,37637
4
+ idf_build_apps/args.py,sha256=VSnLDwFmgSNDzldzqdw0frd6gmCwhsd6ri76V296x00,35757
5
+ idf_build_apps/autocompletions.py,sha256=g-bx0pzXoFKI0VQqftkHyGVWN6MLjuFOdozeuAf45yo,2138
6
+ idf_build_apps/config.py,sha256=TvJ-puAhR7r7SYdAWsK5XdOTAVuw5k8xiqO2jRms_ZA,3586
7
+ idf_build_apps/constants.py,sha256=mOHb74qX8WsA4sm-NoSFpLy4HzjIsc-ApUN6XGtrXXM,3904
8
+ idf_build_apps/finder.py,sha256=evkHtNIhCB3wgCMGPxOC34v-_B4mtZ1VBiZ14IRVuFw,5727
9
+ idf_build_apps/log.py,sha256=pyvT7N4MWzGjIXph5mThQCGBiSt53RNPW0WrFfLr0Kw,2650
10
+ idf_build_apps/main.py,sha256=xw6lmVmAKD1f7aHb5c7qQbAfqmHfh_t_q57Q_z6htLs,15331
11
+ idf_build_apps/session_args.py,sha256=2WDTy40IFAc0KQ57HaeBcYj_k10eUXRKkDOWLrFCaHY,2985
12
+ idf_build_apps/utils.py,sha256=Cibdr7oHhcnl9uxJNfPLksp2nfPCPW0efAwDhX4dTmw,10099
13
+ idf_build_apps/junit/__init__.py,sha256=IxvdaS6eSXp7kZxRuXqyZyGxuA_A1nOW1jF1HMi8Gns,231
14
+ idf_build_apps/junit/report.py,sha256=T7dVU3Sz5tqjfbcFW7wjsb65PDH6C2HFf73ePJqBhMs,6555
15
+ idf_build_apps/junit/utils.py,sha256=j0PYhFTZjXtTwkENdeL4bFJcX24ktf1CsOOVXz65yNo,1297
16
+ idf_build_apps/manifest/__init__.py,sha256=Q2-cb3ngNjnl6_zWhUfzZZB10f_-Rv2JYNck3Lk7UkQ,133
17
+ idf_build_apps/manifest/if_parser.py,sha256=r0pivV9gmniPn3Ia6sTMbW5tFAKInhOXk-Lfd6GokqE,6381
18
+ idf_build_apps/manifest/manifest.py,sha256=c9ooxNe3SOEndsVtJ-xxJNWPF6YlS-LpX_5wmjxCfOc,14003
19
+ idf_build_apps/manifest/soc_header.py,sha256=PzJ37xFspt5f0AXWvAFNA_avHZA9fMXHBrwDYLi3qEI,4344
20
+ idf_build_apps/yaml/__init__.py,sha256=W-3z5no07RQ6eYKGyOAPA8Z2CLiMPob8DD91I4URjrA,162
21
+ idf_build_apps/yaml/parser.py,sha256=Y2OyB4g1DCC7C7jrvpIyZV9lgeCB_XvuB75iGmqiTaM,2093
22
+ idf_build_apps-2.5.0rc0.dist-info/entry_points.txt,sha256=3pVUirUEsb6jsDRikkQWNUt4hqLK2ci1HvW_Vf8b6uE,59
23
+ idf_build_apps-2.5.0rc0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
24
+ idf_build_apps-2.5.0rc0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
25
+ idf_build_apps-2.5.0rc0.dist-info/METADATA,sha256=ghIKlVQjTRflEhKdnQW8LC_bSYhdYuxlIfzN153oR8c,4580
26
+ idf_build_apps-2.5.0rc0.dist-info/RECORD,,
@@ -1,64 +0,0 @@
1
- # SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
2
- # SPDX-License-Identifier: Apache-2.0
3
-
4
- import typing as t
5
-
6
- from pydantic import (
7
- computed_field,
8
- )
9
-
10
- from .utils import (
11
- BaseModel,
12
- )
13
-
14
-
15
- class BuildAppsArgs(BaseModel):
16
- PARALLEL_INDEX_PLACEHOLDER: t.ClassVar[str] = '@p' # replace it with the parallel index
17
-
18
- parallel_index: int = 1
19
- parallel_count: int = 1
20
-
21
- _junitxml: t.Optional[str] = None
22
- _collect_app_info: t.Optional[str] = None
23
- _collect_size_info: t.Optional[str] = None
24
-
25
- def __init__(
26
- self,
27
- *,
28
- collect_app_info: t.Optional[str] = None,
29
- collect_size_info: t.Optional[str] = None,
30
- junitxml: t.Optional[str] = None,
31
- **kwargs,
32
- ):
33
- super().__init__(**kwargs)
34
-
35
- self._junitxml = junitxml
36
- self._collect_app_info = collect_app_info
37
- self._collect_size_info = collect_size_info
38
-
39
- @computed_field # type: ignore
40
- @property
41
- def collect_app_info(self) -> t.Optional[str]:
42
- if self._collect_app_info:
43
- return self.expand(self._collect_app_info)
44
-
45
- return None
46
-
47
- @computed_field # type: ignore
48
- @property
49
- def collect_size_info(self) -> t.Optional[str]:
50
- if self._collect_size_info:
51
- return self.expand(self._collect_size_info)
52
-
53
- return None
54
-
55
- @computed_field # type: ignore
56
- @property
57
- def junitxml(self) -> t.Optional[str]:
58
- if self._junitxml:
59
- return self.expand(self._junitxml)
60
-
61
- return None
62
-
63
- def expand(self, path):
64
- return path.replace(self.PARALLEL_INDEX_PLACEHOLDER, str(self.parallel_index))
@@ -1,26 +0,0 @@
1
- idf_build_apps/__init__.py,sha256=BwKVA9OBak7HwLAOrcfYb-ir3L5sCQDsB_FC7GO4KjE,650
2
- idf_build_apps/__main__.py,sha256=8E-5xHm2MlRun0L88XJleNh5U50dpE0Q1nK5KqomA7I,182
3
- idf_build_apps/app.py,sha256=q9udvZyBG3qWrNxlyuG3iTiLcayOSrSwRuyL2n7QALY,37002
4
- idf_build_apps/autocompletions.py,sha256=g-bx0pzXoFKI0VQqftkHyGVWN6MLjuFOdozeuAf45yo,2138
5
- idf_build_apps/build_apps_args.py,sha256=r6VCJDdCzE873X8OTputYkCBZPgECaKoNlAejfcamJk,1644
6
- idf_build_apps/config.py,sha256=I75uOQGarCWVKGi16ZYpo0qTVU25BUP4eh6-RWCtbvw,2924
7
- idf_build_apps/constants.py,sha256=8Bh99xGFpyLkEovQMglS2KydxoEZesAHQSgdpsUnSTM,4000
8
- idf_build_apps/finder.py,sha256=2TOQ6fq9q3MtsLnG4o0VvA2aMd3Zb6w6CL-U_tQrT3k,6362
9
- idf_build_apps/log.py,sha256=JysogBHoompfW9E9w0U4wZH7tt8dBdfoh-stPXQz1hw,2573
10
- idf_build_apps/main.py,sha256=wKDt5iQKjpDmxJecPJMMMYb2kDI2qIRH_Qlkg3u6jlY,39199
11
- idf_build_apps/session_args.py,sha256=2WDTy40IFAc0KQ57HaeBcYj_k10eUXRKkDOWLrFCaHY,2985
12
- idf_build_apps/utils.py,sha256=dYxNXPe7FRWxzUFlTzOb3G-LpKpRUrNWdQrsO5MXAB8,9702
13
- idf_build_apps/junit/__init__.py,sha256=IxvdaS6eSXp7kZxRuXqyZyGxuA_A1nOW1jF1HMi8Gns,231
14
- idf_build_apps/junit/report.py,sha256=T7dVU3Sz5tqjfbcFW7wjsb65PDH6C2HFf73ePJqBhMs,6555
15
- idf_build_apps/junit/utils.py,sha256=j0PYhFTZjXtTwkENdeL4bFJcX24ktf1CsOOVXz65yNo,1297
16
- idf_build_apps/manifest/__init__.py,sha256=Q2-cb3ngNjnl6_zWhUfzZZB10f_-Rv2JYNck3Lk7UkQ,133
17
- idf_build_apps/manifest/if_parser.py,sha256=r0pivV9gmniPn3Ia6sTMbW5tFAKInhOXk-Lfd6GokqE,6381
18
- idf_build_apps/manifest/manifest.py,sha256=P5ZaUd72A_HOVF6iuwap__Bw-w7WI72ugiTURm9PNNQ,10708
19
- idf_build_apps/manifest/soc_header.py,sha256=PzJ37xFspt5f0AXWvAFNA_avHZA9fMXHBrwDYLi3qEI,4344
20
- idf_build_apps/yaml/__init__.py,sha256=W-3z5no07RQ6eYKGyOAPA8Z2CLiMPob8DD91I4URjrA,162
21
- idf_build_apps/yaml/parser.py,sha256=Y2OyB4g1DCC7C7jrvpIyZV9lgeCB_XvuB75iGmqiTaM,2093
22
- idf_build_apps-2.4.2.dist-info/entry_points.txt,sha256=3pVUirUEsb6jsDRikkQWNUt4hqLK2ci1HvW_Vf8b6uE,59
23
- idf_build_apps-2.4.2.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
24
- idf_build_apps-2.4.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
25
- idf_build_apps-2.4.2.dist-info/METADATA,sha256=wk_YDe56T2b3qXhknNIRzcBtCpvcDQCqku22o7IbYIU,4608
26
- idf_build_apps-2.4.2.dist-info/RECORD,,