idf-build-apps 2.5.0rc1__py3-none-any.whl → 2.5.1__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.
- idf_build_apps/__init__.py +1 -1
- idf_build_apps/args.py +492 -668
- idf_build_apps/constants.py +1 -0
- idf_build_apps/finder.py +2 -2
- idf_build_apps/main.py +63 -15
- idf_build_apps/manifest/if_parser.py +20 -11
- idf_build_apps/manifest/manifest.py +19 -7
- idf_build_apps/utils.py +5 -1
- idf_build_apps/vendors/__init__.py +0 -0
- idf_build_apps/vendors/pydantic_sources.py +120 -0
- idf_build_apps/yaml/parser.py +3 -1
- {idf_build_apps-2.5.0rc1.dist-info → idf_build_apps-2.5.1.dist-info}/METADATA +2 -1
- idf_build_apps-2.5.1.dist-info/RECORD +27 -0
- idf_build_apps/config.py +0 -108
- idf_build_apps-2.5.0rc1.dist-info/RECORD +0 -26
- {idf_build_apps-2.5.0rc1.dist-info → idf_build_apps-2.5.1.dist-info}/LICENSE +0 -0
- {idf_build_apps-2.5.0rc1.dist-info → idf_build_apps-2.5.1.dist-info}/WHEEL +0 -0
- {idf_build_apps-2.5.0rc1.dist-info → idf_build_apps-2.5.1.dist-info}/entry_points.txt +0 -0
idf_build_apps/constants.py
CHANGED
idf_build_apps/finder.py
CHANGED
|
@@ -31,7 +31,7 @@ def _get_apps_from_path(
|
|
|
31
31
|
target: str,
|
|
32
32
|
*,
|
|
33
33
|
app_cls: t.Type[App] = CMakeApp,
|
|
34
|
-
args: FindArguments
|
|
34
|
+
args: FindArguments,
|
|
35
35
|
) -> t.List[App]:
|
|
36
36
|
def _validate_app(_app: App) -> bool:
|
|
37
37
|
if target not in _app.supported_targets:
|
|
@@ -133,7 +133,7 @@ def _find_apps(
|
|
|
133
133
|
target: str,
|
|
134
134
|
*,
|
|
135
135
|
app_cls: t.Type[App] = CMakeApp,
|
|
136
|
-
args: FindArguments
|
|
136
|
+
args: FindArguments,
|
|
137
137
|
) -> t.List[App]:
|
|
138
138
|
LOGGER.debug(
|
|
139
139
|
'Looking for %s apps in %s%s with target %s',
|
idf_build_apps/main.py
CHANGED
|
@@ -10,7 +10,6 @@ import os
|
|
|
10
10
|
import sys
|
|
11
11
|
import textwrap
|
|
12
12
|
import typing as t
|
|
13
|
-
from dataclasses import asdict
|
|
14
13
|
|
|
15
14
|
import argcomplete
|
|
16
15
|
from pydantic import (
|
|
@@ -18,7 +17,13 @@ from pydantic import (
|
|
|
18
17
|
create_model,
|
|
19
18
|
)
|
|
20
19
|
|
|
21
|
-
from idf_build_apps.args import
|
|
20
|
+
from idf_build_apps.args import (
|
|
21
|
+
BuildArguments,
|
|
22
|
+
DumpManifestShaArguments,
|
|
23
|
+
FindArguments,
|
|
24
|
+
add_args_to_parser,
|
|
25
|
+
apply_config_file,
|
|
26
|
+
)
|
|
22
27
|
|
|
23
28
|
from .app import (
|
|
24
29
|
App,
|
|
@@ -55,6 +60,7 @@ def find_apps(
|
|
|
55
60
|
target: t.Optional[str] = None,
|
|
56
61
|
*,
|
|
57
62
|
find_arguments: t.Optional[FindArguments] = None,
|
|
63
|
+
config_file: t.Optional[str] = None,
|
|
58
64
|
**kwargs,
|
|
59
65
|
) -> t.List[App]:
|
|
60
66
|
"""
|
|
@@ -62,6 +68,17 @@ def find_apps(
|
|
|
62
68
|
|
|
63
69
|
:return: list of found apps
|
|
64
70
|
"""
|
|
71
|
+
apply_config_file(config_file)
|
|
72
|
+
|
|
73
|
+
# compatible with old usage
|
|
74
|
+
## `preserve`
|
|
75
|
+
if 'preserve' in kwargs:
|
|
76
|
+
LOGGER.warning(
|
|
77
|
+
'Passing "preserve" directly is deprecated. '
|
|
78
|
+
'Pass "no_preserve" instead to disable preserving the build directory'
|
|
79
|
+
)
|
|
80
|
+
kwargs['no_preserve'] = not kwargs.pop('preserve')
|
|
81
|
+
|
|
65
82
|
if find_arguments is None:
|
|
66
83
|
find_arguments = FindArguments(
|
|
67
84
|
paths=to_list(paths), # type: ignore
|
|
@@ -104,21 +121,40 @@ def find_apps(
|
|
|
104
121
|
|
|
105
122
|
|
|
106
123
|
def build_apps(
|
|
107
|
-
apps: t.Union[t.List[App], App, None] = None,
|
|
124
|
+
apps: t.Union[t.List[App], App, None] = None,
|
|
125
|
+
*,
|
|
126
|
+
build_arguments: t.Optional[BuildArguments] = None,
|
|
127
|
+
config_file: t.Optional[str] = None,
|
|
128
|
+
**kwargs,
|
|
108
129
|
) -> int:
|
|
109
130
|
"""
|
|
110
131
|
Build all the specified apps. For all kwargs, please refer to `BuildArguments`
|
|
111
132
|
|
|
112
133
|
:return: exit code
|
|
113
134
|
"""
|
|
114
|
-
|
|
135
|
+
apply_config_file(config_file)
|
|
136
|
+
|
|
137
|
+
# compatible with old usage
|
|
138
|
+
## `check_app_dependencies`
|
|
139
|
+
if 'check_app_dependencies' in kwargs:
|
|
140
|
+
LOGGER.warning(
|
|
141
|
+
'Passing "check_app_dependencies" directly is deprecated. '
|
|
142
|
+
'Pass "modified_components" instead to enable dependency-driven build feature'
|
|
143
|
+
)
|
|
144
|
+
kwargs.pop('check_app_dependencies')
|
|
145
|
+
|
|
115
146
|
if build_arguments is None:
|
|
116
147
|
build_arguments = BuildArguments(
|
|
117
148
|
**kwargs,
|
|
118
149
|
)
|
|
119
150
|
|
|
151
|
+
apps = to_list(apps)
|
|
120
152
|
if apps is None:
|
|
121
|
-
apps = find_apps(
|
|
153
|
+
apps = find_apps(
|
|
154
|
+
find_arguments=FindArguments(
|
|
155
|
+
**{k: v for k, v in build_arguments.model_dump().items() if k in FindArguments.model_fields}
|
|
156
|
+
)
|
|
157
|
+
)
|
|
122
158
|
|
|
123
159
|
test_suite = TestSuite('build_apps')
|
|
124
160
|
|
|
@@ -255,6 +291,9 @@ def get_parser() -> argparse.ArgumentParser:
|
|
|
255
291
|
)
|
|
256
292
|
actions = parser.add_subparsers(dest='action', required=True)
|
|
257
293
|
|
|
294
|
+
common_args = argparse.ArgumentParser(add_help=False)
|
|
295
|
+
common_args.add_argument('--config-file', help='Path to the config file')
|
|
296
|
+
|
|
258
297
|
########
|
|
259
298
|
# Find #
|
|
260
299
|
########
|
|
@@ -266,8 +305,9 @@ def get_parser() -> argparse.ArgumentParser:
|
|
|
266
305
|
'By default, print the found apps in stdout. '
|
|
267
306
|
'To find apps for all chips use the `--target` option with the `all` argument.',
|
|
268
307
|
formatter_class=IdfBuildAppsCliFormatter,
|
|
308
|
+
parents=[common_args],
|
|
269
309
|
)
|
|
270
|
-
|
|
310
|
+
add_args_to_parser(FindArguments, find_parser)
|
|
271
311
|
|
|
272
312
|
#########
|
|
273
313
|
# Build #
|
|
@@ -278,8 +318,9 @@ def get_parser() -> argparse.ArgumentParser:
|
|
|
278
318
|
description='Build the application in the given path or paths for specified chips. '
|
|
279
319
|
'`--path` and `--target` options must be provided.',
|
|
280
320
|
formatter_class=IdfBuildAppsCliFormatter,
|
|
321
|
+
parents=[common_args],
|
|
281
322
|
)
|
|
282
|
-
|
|
323
|
+
add_args_to_parser(BuildArguments, build_parser)
|
|
283
324
|
|
|
284
325
|
###############
|
|
285
326
|
# Completions #
|
|
@@ -314,8 +355,9 @@ def get_parser() -> argparse.ArgumentParser:
|
|
|
314
355
|
'dump-manifest-sha',
|
|
315
356
|
help='Dump the manifest files SHA values. '
|
|
316
357
|
'This could be useful in CI to check if the manifest files are changed.',
|
|
358
|
+
parents=[common_args],
|
|
317
359
|
)
|
|
318
|
-
|
|
360
|
+
add_args_to_parser(DumpManifestShaArguments, dump_manifest_parser)
|
|
319
361
|
|
|
320
362
|
return parser
|
|
321
363
|
|
|
@@ -340,15 +382,20 @@ def main():
|
|
|
340
382
|
handle_completions(args)
|
|
341
383
|
sys.exit(0)
|
|
342
384
|
|
|
343
|
-
|
|
344
|
-
|
|
385
|
+
kwargs = vars(args)
|
|
386
|
+
action = kwargs.pop('action')
|
|
387
|
+
config_file = kwargs.pop('config_file')
|
|
388
|
+
|
|
389
|
+
apply_config_file(config_file)
|
|
390
|
+
|
|
391
|
+
if action == 'dump-manifest-sha':
|
|
392
|
+
arguments = DumpManifestShaArguments(**drop_none_kwargs(kwargs))
|
|
345
393
|
Manifest.from_files(arguments.manifest_files).dump_sha_values(arguments.output)
|
|
346
394
|
sys.exit(0)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
arguments = FindArguments.from_dict(drop_none_kwargs(vars(args)))
|
|
395
|
+
elif action == 'find':
|
|
396
|
+
arguments = FindArguments(**drop_none_kwargs(kwargs))
|
|
350
397
|
else:
|
|
351
|
-
arguments = BuildArguments
|
|
398
|
+
arguments = BuildArguments(**drop_none_kwargs(kwargs))
|
|
352
399
|
|
|
353
400
|
# real call starts here
|
|
354
401
|
# build also needs to find first
|
|
@@ -395,7 +442,8 @@ def main():
|
|
|
395
442
|
for app in failed_apps:
|
|
396
443
|
print(f' {app}')
|
|
397
444
|
|
|
398
|
-
|
|
445
|
+
if ret_code != 0:
|
|
446
|
+
sys.exit(ret_code)
|
|
399
447
|
|
|
400
448
|
|
|
401
449
|
def json_to_app(json_str: str, extra_classes: t.Optional[t.List[t.Type[App]]] = None) -> App:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
3
|
import operator
|
|
5
4
|
import os
|
|
6
5
|
from ast import (
|
|
@@ -16,6 +15,7 @@ from packaging.version import (
|
|
|
16
15
|
from pyparsing import (
|
|
17
16
|
Keyword,
|
|
18
17
|
Literal,
|
|
18
|
+
MatchFirst,
|
|
19
19
|
ParseResults,
|
|
20
20
|
QuotedString,
|
|
21
21
|
Suppress,
|
|
@@ -35,6 +35,7 @@ from ..constants import (
|
|
|
35
35
|
IDF_VERSION_PATCH,
|
|
36
36
|
)
|
|
37
37
|
from ..utils import (
|
|
38
|
+
InvalidIfClause,
|
|
38
39
|
InvalidInput,
|
|
39
40
|
to_version,
|
|
40
41
|
)
|
|
@@ -177,22 +178,31 @@ class BoolExpr(Stmt):
|
|
|
177
178
|
pass
|
|
178
179
|
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
self.left: BoolStmt = t[0][0]
|
|
183
|
-
self.right: BoolStmt = t[0][2]
|
|
181
|
+
def _and(_l, _r):
|
|
182
|
+
return _l and _r
|
|
184
183
|
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
|
|
185
|
+
def _or(_l, _r):
|
|
186
|
+
return _l or _r
|
|
187
187
|
|
|
188
188
|
|
|
189
|
-
class
|
|
189
|
+
class BoolOrAnd(BoolExpr):
|
|
190
190
|
def __init__(self, t: ParseResults):
|
|
191
|
+
if len(t[0]) > 3:
|
|
192
|
+
raise InvalidIfClause(
|
|
193
|
+
'Chaining "and"/"or" is not allowed. Please use paratheses instead. '
|
|
194
|
+
'For example: "a and b and c" should be "(a and b) and c".'
|
|
195
|
+
)
|
|
191
196
|
self.left: BoolStmt = t[0][0]
|
|
192
197
|
self.right: BoolStmt = t[0][2]
|
|
193
198
|
|
|
199
|
+
if t[0][1] == 'and':
|
|
200
|
+
self.operation = _and
|
|
201
|
+
if t[0][1] == 'or':
|
|
202
|
+
self.operation = _or
|
|
203
|
+
|
|
194
204
|
def get_value(self, target: str, config_name: str) -> Any:
|
|
195
|
-
return self.left.get_value(target, config_name)
|
|
205
|
+
return self.operation(self.left.get_value(target, config_name), self.right.get_value(target, config_name))
|
|
196
206
|
|
|
197
207
|
|
|
198
208
|
CAP_WORD = Word(alphas.upper(), nums + alphas.upper() + '_').setParseAction(ChipAttr)
|
|
@@ -225,7 +235,6 @@ OR = Keyword('or')
|
|
|
225
235
|
BOOL_EXPR = infixNotation(
|
|
226
236
|
BOOL_STMT,
|
|
227
237
|
[
|
|
228
|
-
(AND, 2, opAssoc.LEFT,
|
|
229
|
-
(OR, 2, opAssoc.LEFT, BoolOr),
|
|
238
|
+
(MatchFirst((AND, OR)), 2, opAssoc.LEFT, BoolOrAnd),
|
|
230
239
|
],
|
|
231
240
|
)
|
|
@@ -17,6 +17,7 @@ from ..constants import (
|
|
|
17
17
|
from ..utils import (
|
|
18
18
|
InvalidIfClause,
|
|
19
19
|
InvalidManifest,
|
|
20
|
+
PathLike,
|
|
20
21
|
to_absolute_path,
|
|
21
22
|
)
|
|
22
23
|
from ..yaml import (
|
|
@@ -34,14 +35,21 @@ class IfClause:
|
|
|
34
35
|
def __init__(self, stmt: str, temporary: bool = False, reason: t.Optional[str] = None) -> None:
|
|
35
36
|
try:
|
|
36
37
|
self.stmt: BoolStmt = BOOL_EXPR.parseString(stmt)[0]
|
|
37
|
-
except ParseException:
|
|
38
|
-
raise InvalidIfClause(f'Invalid if
|
|
38
|
+
except (ParseException, InvalidIfClause) as ex:
|
|
39
|
+
raise InvalidIfClause(f'Invalid if clause: {stmt}. {ex}')
|
|
39
40
|
|
|
40
41
|
self.temporary = temporary
|
|
41
42
|
self.reason = reason
|
|
42
43
|
|
|
43
44
|
if self.temporary is True and not self.reason:
|
|
44
|
-
raise InvalidIfClause(
|
|
45
|
+
raise InvalidIfClause(
|
|
46
|
+
f'Invalid if clause "{stmt}". '
|
|
47
|
+
f'"reason" must be set when "temporary: true". '
|
|
48
|
+
f'For example:\n'
|
|
49
|
+
f' - if: {stmt}\n'
|
|
50
|
+
f' temporary: true\n'
|
|
51
|
+
f' reason: lack of ci runners'
|
|
52
|
+
)
|
|
45
53
|
|
|
46
54
|
def get_value(self, target: str, config_name: str) -> t.Any:
|
|
47
55
|
return self.stmt.get_value(target, config_name)
|
|
@@ -145,7 +153,7 @@ class FolderRule:
|
|
|
145
153
|
self.depends_components,
|
|
146
154
|
self.depends_filepatterns,
|
|
147
155
|
]:
|
|
148
|
-
sha.update(pickle.dumps(obj))
|
|
156
|
+
sha.update(pickle.dumps(obj, protocol=4)) # protocol 4 by default is set in Python 3.8
|
|
149
157
|
|
|
150
158
|
return sha.hexdigest()
|
|
151
159
|
|
|
@@ -235,7 +243,7 @@ class Manifest:
|
|
|
235
243
|
self._root_path = to_absolute_path(root_path)
|
|
236
244
|
|
|
237
245
|
@classmethod
|
|
238
|
-
def from_files(cls, paths: t.Iterable[
|
|
246
|
+
def from_files(cls, paths: t.Iterable[PathLike], *, root_path: str = os.curdir) -> 'Manifest':
|
|
239
247
|
"""
|
|
240
248
|
Create a Manifest instance from multiple manifest files
|
|
241
249
|
|
|
@@ -244,10 +252,11 @@ class Manifest:
|
|
|
244
252
|
:return: Manifest instance
|
|
245
253
|
"""
|
|
246
254
|
# folder, defined as dict
|
|
247
|
-
_known_folders: t.Dict[str,
|
|
255
|
+
_known_folders: t.Dict[str, PathLike] = dict()
|
|
248
256
|
|
|
249
257
|
rules: t.List[FolderRule] = []
|
|
250
258
|
for path in paths:
|
|
259
|
+
LOGGER.debug('Loading manifest file %s', path)
|
|
251
260
|
_manifest = cls.from_file(path, root_path=root_path)
|
|
252
261
|
|
|
253
262
|
for rule in _manifest.rules:
|
|
@@ -265,7 +274,7 @@ class Manifest:
|
|
|
265
274
|
return Manifest(rules, root_path=root_path)
|
|
266
275
|
|
|
267
276
|
@classmethod
|
|
268
|
-
def from_file(cls, path:
|
|
277
|
+
def from_file(cls, path: PathLike, *, root_path: str = os.curdir) -> 'Manifest':
|
|
269
278
|
"""
|
|
270
279
|
Create a Manifest instance from a manifest file
|
|
271
280
|
|
|
@@ -343,11 +352,14 @@ class Manifest:
|
|
|
343
352
|
return x
|
|
344
353
|
|
|
345
354
|
for folder, sha_value in recorded__rel_folder__sha__dict.items():
|
|
355
|
+
# removed
|
|
346
356
|
if folder not in self__rel_folder__sha__dict:
|
|
347
357
|
diff_folders.add(_path(folder))
|
|
358
|
+
# modified
|
|
348
359
|
elif sha_value != self__rel_folder__sha__dict[folder]:
|
|
349
360
|
diff_folders.add(_path(folder))
|
|
350
361
|
|
|
362
|
+
# new
|
|
351
363
|
for folder in self__rel_folder__sha__dict:
|
|
352
364
|
if folder not in recorded__rel_folder__sha__dict:
|
|
353
365
|
diff_folders.add(_path(folder))
|
idf_build_apps/utils.py
CHANGED
|
@@ -13,6 +13,7 @@ import typing as t
|
|
|
13
13
|
from copy import (
|
|
14
14
|
deepcopy,
|
|
15
15
|
)
|
|
16
|
+
from pathlib import Path
|
|
16
17
|
|
|
17
18
|
from packaging.version import (
|
|
18
19
|
Version,
|
|
@@ -124,7 +125,7 @@ class InvalidManifest(SystemExit):
|
|
|
124
125
|
"""Invalid manifest file"""
|
|
125
126
|
|
|
126
127
|
|
|
127
|
-
def rmdir(path: str, exclude_file_patterns: t.Union[t.List[str], str, None] = None) -> None:
|
|
128
|
+
def rmdir(path: t.Union[Path, str], exclude_file_patterns: t.Union[t.List[str], str, None] = None) -> None:
|
|
128
129
|
if not exclude_file_patterns:
|
|
129
130
|
shutil.rmtree(path, ignore_errors=True)
|
|
130
131
|
return
|
|
@@ -387,3 +388,6 @@ class BaseModel(_BaseModel):
|
|
|
387
388
|
|
|
388
389
|
def drop_none_kwargs(d: dict) -> dict:
|
|
389
390
|
return {k: v for k, v in d.items() if v is not None}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
PathLike = t.Union[str, Path]
|
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2022 Samuel Colvin and other contributors
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Partially copied from https://github.com/pydantic/pydantic-settings v2.5.2
|
|
6
|
+
since python 3.7 version got dropped at pydantic-settings 2.1.0
|
|
7
|
+
but the feature we need introduced in 2.2.0
|
|
8
|
+
|
|
9
|
+
For contributing history please refer to the original github page
|
|
10
|
+
For the full license text refer to
|
|
11
|
+
https://github.com/pydantic/pydantic-settings/blob/9b73e924cab136d876907af0c6836dcca09ac35c/LICENSE
|
|
12
|
+
|
|
13
|
+
Modifications:
|
|
14
|
+
- use toml instead of tomli when python < 3.11
|
|
15
|
+
- stop using global variables
|
|
16
|
+
- fix some warnings
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from abc import ABC, abstractmethod
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
|
24
|
+
|
|
25
|
+
from pydantic_settings import InitSettingsSource
|
|
26
|
+
from pydantic_settings.main import BaseSettings
|
|
27
|
+
|
|
28
|
+
PathType = Union[Path, str, List[Union[Path, str]], Tuple[Union[Path, str], ...]]
|
|
29
|
+
DEFAULT_PATH: PathType = Path('')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ConfigFileSourceMixin(ABC):
|
|
33
|
+
def _read_files(self, files: Optional[PathType]) -> Dict[str, Any]:
|
|
34
|
+
if files is None:
|
|
35
|
+
return {}
|
|
36
|
+
if isinstance(files, (str, os.PathLike)):
|
|
37
|
+
files = [files]
|
|
38
|
+
kwargs: Dict[str, Any] = {}
|
|
39
|
+
for file in files:
|
|
40
|
+
file_path = Path(file).expanduser()
|
|
41
|
+
if file_path.is_file():
|
|
42
|
+
kwargs.update(self._read_file(file_path))
|
|
43
|
+
return kwargs
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def _read_file(self, path: Path) -> Dict[str, Any]:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TomlConfigSettingsSource(InitSettingsSource, ConfigFileSourceMixin):
|
|
51
|
+
"""
|
|
52
|
+
A source class that loads variables from a TOML file
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
settings_cls: Type[BaseSettings],
|
|
58
|
+
toml_file: Optional[PathType] = DEFAULT_PATH,
|
|
59
|
+
):
|
|
60
|
+
self.toml_file_path = toml_file if toml_file != DEFAULT_PATH else settings_cls.model_config.get('toml_file')
|
|
61
|
+
self.toml_data = self._read_files(self.toml_file_path)
|
|
62
|
+
super().__init__(settings_cls, self.toml_data)
|
|
63
|
+
|
|
64
|
+
def _read_file(self, file_path: Path) -> Dict[str, Any]:
|
|
65
|
+
if sys.version_info < (3, 11):
|
|
66
|
+
import toml
|
|
67
|
+
|
|
68
|
+
with open(file_path) as toml_file:
|
|
69
|
+
return toml.load(toml_file)
|
|
70
|
+
else:
|
|
71
|
+
import tomllib
|
|
72
|
+
|
|
73
|
+
with open(file_path, 'rb') as toml_file:
|
|
74
|
+
return tomllib.load(toml_file)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class PyprojectTomlConfigSettingsSource(TomlConfigSettingsSource):
|
|
78
|
+
"""
|
|
79
|
+
A source class that loads variables from a `pyproject.toml` file.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
settings_cls: Type[BaseSettings],
|
|
85
|
+
toml_file: Optional[Path] = None,
|
|
86
|
+
) -> None:
|
|
87
|
+
self.toml_file_path = self._pick_pyproject_toml_file(
|
|
88
|
+
toml_file, settings_cls.model_config.get('pyproject_toml_depth', 0)
|
|
89
|
+
)
|
|
90
|
+
self.toml_table_header: Tuple[str, ...] = settings_cls.model_config.get(
|
|
91
|
+
'pyproject_toml_table_header', ('tool', 'pydantic-settings')
|
|
92
|
+
)
|
|
93
|
+
self.toml_data = self._read_files(self.toml_file_path)
|
|
94
|
+
for key in self.toml_table_header:
|
|
95
|
+
self.toml_data = self.toml_data.get(key, {})
|
|
96
|
+
super(TomlConfigSettingsSource, self).__init__(settings_cls, self.toml_data)
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def _pick_pyproject_toml_file(provided: Optional[Path], depth: int) -> Path:
|
|
100
|
+
"""Pick a `pyproject.toml` file path to use.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
provided: Explicit path provided when instantiating this class.
|
|
104
|
+
depth: Number of directories up the tree to check of a pyproject.toml.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
if provided:
|
|
108
|
+
return provided.resolve()
|
|
109
|
+
rv = Path.cwd() / 'pyproject.toml'
|
|
110
|
+
count = 0
|
|
111
|
+
if not rv.is_file():
|
|
112
|
+
child = rv.parent.parent / 'pyproject.toml'
|
|
113
|
+
while count < depth:
|
|
114
|
+
if child.is_file():
|
|
115
|
+
return child
|
|
116
|
+
if str(child.parent) == rv.root:
|
|
117
|
+
break # end discovery after checking system root once
|
|
118
|
+
child = child.parent.parent / 'pyproject.toml'
|
|
119
|
+
count += 1
|
|
120
|
+
return rv
|
idf_build_apps/yaml/parser.py
CHANGED
|
@@ -5,6 +5,8 @@ import typing as t
|
|
|
5
5
|
|
|
6
6
|
import yaml
|
|
7
7
|
|
|
8
|
+
from ..utils import PathLike
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
def parse_postfixes(manifest_dict: t.Dict):
|
|
10
12
|
for folder, folder_rule in manifest_dict.items():
|
|
@@ -60,7 +62,7 @@ def parse_postfixes(manifest_dict: t.Dict):
|
|
|
60
62
|
manifest_dict[folder] = updated_folder
|
|
61
63
|
|
|
62
64
|
|
|
63
|
-
def parse(path:
|
|
65
|
+
def parse(path: PathLike) -> t.Dict:
|
|
64
66
|
with open(path) as f:
|
|
65
67
|
manifest_dict = yaml.safe_load(f) or {}
|
|
66
68
|
parse_postfixes(manifest_dict)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: idf-build-apps
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.1
|
|
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
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pyyaml
|
|
|
18
18
|
Requires-Dist: packaging
|
|
19
19
|
Requires-Dist: toml; python_version < '3.11'
|
|
20
20
|
Requires-Dist: pydantic~=2.0
|
|
21
|
+
Requires-Dist: pydantic_settings
|
|
21
22
|
Requires-Dist: argcomplete>=3
|
|
22
23
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
23
24
|
Requires-Dist: sphinx ; extra == "doc"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
idf_build_apps/__init__.py,sha256=hPsqVwLnkcGIEQNAReIvWhXRDoov8t70997wSn1zJ0g,650
|
|
2
|
+
idf_build_apps/__main__.py,sha256=8E-5xHm2MlRun0L88XJleNh5U50dpE0Q1nK5KqomA7I,182
|
|
3
|
+
idf_build_apps/app.py,sha256=F-MKOsaz7cJ0H2wsEE4gpO4kkkEdkyFmIZBHDoM2qgs,37359
|
|
4
|
+
idf_build_apps/args.py,sha256=Y4Wwoi8XYLjf8lWmCUm197NSzSBa4eC95QAyx4q7-6Q,31668
|
|
5
|
+
idf_build_apps/autocompletions.py,sha256=g-bx0pzXoFKI0VQqftkHyGVWN6MLjuFOdozeuAf45yo,2138
|
|
6
|
+
idf_build_apps/constants.py,sha256=07ve2FtWuLBuc_6LFzbs1XncB1VNi9HJUqGjQQauRNM,3952
|
|
7
|
+
idf_build_apps/finder.py,sha256=kfZaGWJfPUwWAbaOj_W3Fu97SIIFEsv1R_dJucjbFHw,5691
|
|
8
|
+
idf_build_apps/log.py,sha256=pyvT7N4MWzGjIXph5mThQCGBiSt53RNPW0WrFfLr0Kw,2650
|
|
9
|
+
idf_build_apps/main.py,sha256=Z_hetbOavgCJZQPaP01_jx57fR1w0DefiFz0J2cCwp0,16498
|
|
10
|
+
idf_build_apps/session_args.py,sha256=2WDTy40IFAc0KQ57HaeBcYj_k10eUXRKkDOWLrFCaHY,2985
|
|
11
|
+
idf_build_apps/utils.py,sha256=s4D8P7QA17XcaCUQ_EoiNOW_VpU3cPQgiZVV9KQ8I30,10171
|
|
12
|
+
idf_build_apps/junit/__init__.py,sha256=IxvdaS6eSXp7kZxRuXqyZyGxuA_A1nOW1jF1HMi8Gns,231
|
|
13
|
+
idf_build_apps/junit/report.py,sha256=T7dVU3Sz5tqjfbcFW7wjsb65PDH6C2HFf73ePJqBhMs,6555
|
|
14
|
+
idf_build_apps/junit/utils.py,sha256=j0PYhFTZjXtTwkENdeL4bFJcX24ktf1CsOOVXz65yNo,1297
|
|
15
|
+
idf_build_apps/manifest/__init__.py,sha256=Q2-cb3ngNjnl6_zWhUfzZZB10f_-Rv2JYNck3Lk7UkQ,133
|
|
16
|
+
idf_build_apps/manifest/if_parser.py,sha256=AAEyYPgcBWL2ToCmcvhx3fl8ebYdC5-LMvvDkYtWn8o,6546
|
|
17
|
+
idf_build_apps/manifest/manifest.py,sha256=6F6Nt93eaoBOHlGHZtv2hHT8Zw0HmJf0d_MESlgye6M,14478
|
|
18
|
+
idf_build_apps/manifest/soc_header.py,sha256=PzJ37xFspt5f0AXWvAFNA_avHZA9fMXHBrwDYLi3qEI,4344
|
|
19
|
+
idf_build_apps/vendors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
idf_build_apps/vendors/pydantic_sources.py,sha256=2IN6opo6qjCwaqhERFbgA4PtEwqKaTtEkccy5_fYAT0,4130
|
|
21
|
+
idf_build_apps/yaml/__init__.py,sha256=W-3z5no07RQ6eYKGyOAPA8Z2CLiMPob8DD91I4URjrA,162
|
|
22
|
+
idf_build_apps/yaml/parser.py,sha256=b3LvogO6do-eJPRsYzT-8xk8AT2MnXpLCzQutJqyC7M,2128
|
|
23
|
+
idf_build_apps-2.5.1.dist-info/entry_points.txt,sha256=3pVUirUEsb6jsDRikkQWNUt4hqLK2ci1HvW_Vf8b6uE,59
|
|
24
|
+
idf_build_apps-2.5.1.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
25
|
+
idf_build_apps-2.5.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
|
26
|
+
idf_build_apps-2.5.1.dist-info/METADATA,sha256=lvliMyMStV-5w_77UputZgbH9ZL2aDagexjxU6OUi-I,4610
|
|
27
|
+
idf_build_apps-2.5.1.dist-info/RECORD,,
|
idf_build_apps/config.py
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import typing as t
|
|
6
|
-
from pathlib import (
|
|
7
|
-
Path,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
from .utils import (
|
|
11
|
-
to_absolute_path,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class InvalidTomlError(SystemExit):
|
|
16
|
-
def __init__(self, filepath: t.Union[str, Path], msg: str) -> None:
|
|
17
|
-
super().__init__(f'Failed parsing toml file "{filepath}" with error: {msg}')
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
PYPROJECT_TOML_FN = 'pyproject.toml'
|
|
21
|
-
IDF_BUILD_APPS_TOML_FN = '.idf_build_apps.toml'
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def load_toml(filepath: t.Union[str, Path]) -> dict:
|
|
25
|
-
try:
|
|
26
|
-
import tomllib # type: ignore # python 3.11
|
|
27
|
-
|
|
28
|
-
try:
|
|
29
|
-
with open(str(filepath), 'rb') as fr:
|
|
30
|
-
return tomllib.load(fr)
|
|
31
|
-
except Exception as e:
|
|
32
|
-
raise InvalidTomlError(filepath, str(e))
|
|
33
|
-
except ImportError:
|
|
34
|
-
import toml
|
|
35
|
-
|
|
36
|
-
try:
|
|
37
|
-
return toml.load(str(filepath))
|
|
38
|
-
except Exception as e:
|
|
39
|
-
raise InvalidTomlError(filepath, str(e))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _get_config_from_file(filepath: str) -> t.Tuple[t.Optional[dict], str]:
|
|
43
|
-
config = None
|
|
44
|
-
if os.path.isfile(filepath):
|
|
45
|
-
if os.path.basename(filepath) == PYPROJECT_TOML_FN:
|
|
46
|
-
tool = load_toml(filepath).get('tool', None)
|
|
47
|
-
if tool:
|
|
48
|
-
config = tool.get('idf-build-apps', None)
|
|
49
|
-
elif config is None:
|
|
50
|
-
config = load_toml(filepath)
|
|
51
|
-
|
|
52
|
-
return config, filepath
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _get_config_from_path(dirpath: str) -> t.Tuple[t.Optional[dict], str]:
|
|
56
|
-
config = None
|
|
57
|
-
filepath = dirpath
|
|
58
|
-
if os.path.isfile(os.path.join(dirpath, PYPROJECT_TOML_FN)):
|
|
59
|
-
config, filepath = _get_config_from_file(os.path.join(dirpath, PYPROJECT_TOML_FN))
|
|
60
|
-
|
|
61
|
-
if config is None and os.path.isfile(os.path.join(dirpath, IDF_BUILD_APPS_TOML_FN)):
|
|
62
|
-
config, filepath = _get_config_from_file(os.path.join(dirpath, IDF_BUILD_APPS_TOML_FN))
|
|
63
|
-
|
|
64
|
-
return config, filepath
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def get_valid_config(starts_from: str = os.getcwd(), custom_path: t.Optional[str] = None) -> t.Optional[dict]:
|
|
68
|
-
"""
|
|
69
|
-
Get the valid config from the current directory or its parent directories.
|
|
70
|
-
|
|
71
|
-
If the custom path is provided, it will be used to get the config file.
|
|
72
|
-
|
|
73
|
-
Otherwise, the search will start from the current working directory and go up to the root directory.
|
|
74
|
-
|
|
75
|
-
The search will stop in the following cases:
|
|
76
|
-
|
|
77
|
-
- A valid config file is found.
|
|
78
|
-
- A `.git` directory is found.
|
|
79
|
-
- The root directory is reached.
|
|
80
|
-
|
|
81
|
-
:param starts_from: starting directory to search for the config file. Default is the current working directory.
|
|
82
|
-
:param custom_path: custom path to the config file.
|
|
83
|
-
:return: the valid config dict if found, otherwise None.
|
|
84
|
-
"""
|
|
85
|
-
root_dir = to_absolute_path('/')
|
|
86
|
-
cur_dir = to_absolute_path(starts_from)
|
|
87
|
-
|
|
88
|
-
config = None
|
|
89
|
-
if custom_path and os.path.isfile(custom_path):
|
|
90
|
-
config, filepath = _get_config_from_file(to_absolute_path(custom_path))
|
|
91
|
-
if config is not None:
|
|
92
|
-
# use print here since the verbose settings may be set in the config file
|
|
93
|
-
print(f'Using custom config file: {filepath}')
|
|
94
|
-
return config
|
|
95
|
-
|
|
96
|
-
while cur_dir != root_dir and config is None:
|
|
97
|
-
config, filepath = _get_config_from_path(cur_dir)
|
|
98
|
-
if config is not None:
|
|
99
|
-
# use print here since the verbose settings may be set in the config file
|
|
100
|
-
print(f'Using config file: {filepath}')
|
|
101
|
-
return config
|
|
102
|
-
|
|
103
|
-
if os.path.exists(os.path.join(cur_dir, '.git')):
|
|
104
|
-
break
|
|
105
|
-
|
|
106
|
-
cur_dir = os.path.abspath(os.path.join(cur_dir, '..'))
|
|
107
|
-
|
|
108
|
-
return None
|