idf-build-apps 2.5.0rc2__tar.gz → 2.5.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.
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/test-build-idf-apps.yml +6 -1
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/CHANGELOG.md +16 -17
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/PKG-INFO +1 -1
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/__init__.py +1 -1
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/args.py +72 -18
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/finder.py +1 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/main.py +14 -2
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/if_parser.py +20 -11
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/manifest.py +14 -3
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/pyproject.toml +1 -1
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/setup.py +1 -1
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/conftest.py +15 -1
- idf_build_apps-2.5.2/tests/test_args.py +296 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_cmd.py +7 -8
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_finder.py +1 -1
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_manifest.py +9 -2
- idf_build_apps-2.5.0rc2/tests/test_args.py +0 -125
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.editorconfig +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.git-blame-ignore-revs +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.gitattributes +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/dependabot.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/check-pre-commit.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/issue_comment.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/new_issues.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/new_prs.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/publish-pypi.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/test-build-docs.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.gitignore +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.pre-commit-config.yaml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.readthedocs.yml +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/LICENSE +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/README.md +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_apidoc_templates/module.rst_t +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_apidoc_templates/package.rst_t +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_apidoc_templates/toc.rst_t +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_static/espressif-logo.svg +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_static/theme_overrides.css +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_templates/layout.html +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/conf_common.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/Makefile +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/conf.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/build.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/config_rules.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/dependency_driven_build.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/find.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/guides/1.x_to_2.x.md +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/index.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/others/CHANGELOG.md +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/others/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/references/cli.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/references/config_file.md +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/references/manifest.rst +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/__main__.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/app.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/autocompletions.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/constants.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/junit/__init__.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/junit/report.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/junit/utils.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/log.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/__init__.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/soc_header.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/session_args.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/utils.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/vendors/__init__.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/vendors/pydantic_sources.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/yaml/__init__.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/yaml/parser.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/license_header.txt +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_app.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_build.py +0 -0
- {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_utils.py +0 -0
|
@@ -76,7 +76,12 @@ jobs:
|
|
|
76
76
|
python -m idf_build_apps build -vv -t esp32 \
|
|
77
77
|
-p $IDF_PATH/examples/get-started/hello_world \
|
|
78
78
|
--size-file size_info.json
|
|
79
|
-
pytest --cov idf_build_apps --cov-report term-missing --junit-xml
|
|
79
|
+
pytest --cov idf_build_apps --cov-report term-missing:skip-covered --junit-xml pytest.xml | tee pytest-coverage.txt
|
|
80
|
+
- name: Pytest coverage comment
|
|
81
|
+
uses: MishaKav/pytest-coverage-comment@main
|
|
82
|
+
with:
|
|
83
|
+
pytest-coverage-path: pytest-coverage.txt
|
|
84
|
+
junitxml-path: pytest.xml
|
|
80
85
|
|
|
81
86
|
build-apps-on-idf-8266:
|
|
82
87
|
runs-on: ubuntu-latest
|
|
@@ -2,43 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## v2.5.
|
|
5
|
+
## v2.5.2 (2024-09-27)
|
|
6
6
|
|
|
7
7
|
### Fix
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- pickle dump default protocal different in python 3.7
|
|
12
|
-
- keep backward compatibility in build_apps function
|
|
9
|
+
- unset CLI argument wrongly overwrite config file settings with default value
|
|
10
|
+
- allow unknown fields
|
|
13
11
|
|
|
14
|
-
## v2.5.
|
|
12
|
+
## v2.5.1 (2024-09-26)
|
|
15
13
|
|
|
16
14
|
### Fix
|
|
17
15
|
|
|
18
|
-
-
|
|
16
|
+
- stop using lambda functions since they cannot be pickled
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- rename BuildArguments.ignore_warning_strings to ignore_warning_strs
|
|
23
|
-
|
|
24
|
-
## v2.5.0rc0 (2024-09-10)
|
|
18
|
+
## v2.5.0 (2024-09-26)
|
|
25
19
|
|
|
26
20
|
### Feat
|
|
27
21
|
|
|
28
|
-
-
|
|
22
|
+
- raise exception when chaining `or`/`and` in manifest file if statements
|
|
23
|
+
- support `idf-build-apps find` with checking modified manifest files
|
|
29
24
|
- support `idf-build-apps dump-manifest-sha`
|
|
30
25
|
|
|
31
26
|
### Fix
|
|
32
27
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
28
|
+
- stop calling `sys.exit` when return code is 0
|
|
29
|
+
- load config file before cli arguments and func arguments
|
|
30
|
+
- pickle dump default protocol different in python 3.7
|
|
31
|
+
- loose env var requirements. `IDF_PATH` not required
|
|
32
|
+
- stop print build log as error when build failed due to `--warning-as-error`
|
|
35
33
|
- requires typing-extensions below 3.11
|
|
36
|
-
-
|
|
34
|
+
- stop wrongly created/deleted temporary build log file
|
|
37
35
|
|
|
38
36
|
### Refactor
|
|
39
37
|
|
|
40
38
|
- declare argument once. used in both function, cli, and docs
|
|
41
|
-
- move Manifest.ROOTPATH to
|
|
39
|
+
- move Manifest.ROOTPATH to arguments
|
|
40
|
+
- expand @p placeholders in `BuildArguments`
|
|
42
41
|
|
|
43
42
|
## v2.4.3 (2024-08-07)
|
|
44
43
|
|
|
@@ -11,6 +11,7 @@ import sys
|
|
|
11
11
|
import typing as t
|
|
12
12
|
from copy import deepcopy
|
|
13
13
|
from dataclasses import dataclass
|
|
14
|
+
from io import TextIOWrapper
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import Any
|
|
16
17
|
|
|
@@ -51,6 +52,8 @@ class FieldMetadata:
|
|
|
51
52
|
:param choices: choices for the argument, used in argparse
|
|
52
53
|
:param type: type for the argument, used in argparse
|
|
53
54
|
:param required: whether the argument is required, used in argparse
|
|
55
|
+
:param default: default value for the argument, used in argparse
|
|
56
|
+
:param hidden: whether the argument is hidden, used in argparse
|
|
54
57
|
"""
|
|
55
58
|
|
|
56
59
|
# validate method
|
|
@@ -66,6 +69,8 @@ class FieldMetadata:
|
|
|
66
69
|
required: bool = False
|
|
67
70
|
# usually default is not needed. only set it when different from the default value of the field
|
|
68
71
|
default: t.Any = None
|
|
72
|
+
# hidden field, use deprecated instead, or hide it in the argparse
|
|
73
|
+
hidden: bool = False
|
|
69
74
|
|
|
70
75
|
|
|
71
76
|
P = ParamSpec('P')
|
|
@@ -112,6 +117,7 @@ class BaseArguments(BaseSettings):
|
|
|
112
117
|
toml_file='.idf_build_apps.toml',
|
|
113
118
|
pyproject_toml_table_header=('tool', 'idf-build-apps'),
|
|
114
119
|
pyproject_toml_depth=sys.maxsize,
|
|
120
|
+
extra='ignore',
|
|
115
121
|
)
|
|
116
122
|
|
|
117
123
|
@classmethod
|
|
@@ -135,8 +141,9 @@ class BaseArguments(BaseSettings):
|
|
|
135
141
|
if info.field_name and info.field_name in cls.model_fields:
|
|
136
142
|
f = cls.model_fields[info.field_name]
|
|
137
143
|
meta = get_meta(f)
|
|
138
|
-
if meta and meta.validate_method
|
|
139
|
-
|
|
144
|
+
if meta and meta.validate_method:
|
|
145
|
+
if ValidateMethod.TO_LIST in meta.validate_method:
|
|
146
|
+
return to_list(v)
|
|
140
147
|
|
|
141
148
|
return v
|
|
142
149
|
|
|
@@ -195,7 +202,9 @@ class DependencyDrivenBuildArguments(GlobalArguments):
|
|
|
195
202
|
validate_method=[ValidateMethod.TO_LIST],
|
|
196
203
|
type=semicolon_separated_str_to_list,
|
|
197
204
|
),
|
|
198
|
-
description='semicolon-separated list of modified components'
|
|
205
|
+
description='semicolon-separated list of modified components. '
|
|
206
|
+
'If set to "", the value would be considered as None. '
|
|
207
|
+
'If set to ";", the value would be considered as an empty list.',
|
|
199
208
|
default=None,
|
|
200
209
|
)
|
|
201
210
|
modified_files: t.Optional[t.List[str]] = field(
|
|
@@ -203,7 +212,9 @@ class DependencyDrivenBuildArguments(GlobalArguments):
|
|
|
203
212
|
validate_method=[ValidateMethod.TO_LIST],
|
|
204
213
|
type=semicolon_separated_str_to_list,
|
|
205
214
|
),
|
|
206
|
-
description='semicolon-separated list of modified files'
|
|
215
|
+
description='semicolon-separated list of modified files. '
|
|
216
|
+
'If set to "", the value would be considered as None. '
|
|
217
|
+
'If set to ";", the value would be considered as an empty list.',
|
|
207
218
|
default=None,
|
|
208
219
|
)
|
|
209
220
|
deactivate_dependency_driven_build_by_components: t.Optional[t.List[str]] = field(
|
|
@@ -219,7 +230,10 @@ class DependencyDrivenBuildArguments(GlobalArguments):
|
|
|
219
230
|
shorthand='-dc',
|
|
220
231
|
),
|
|
221
232
|
description='semicolon-separated list of components. '
|
|
222
|
-
'dependency-driven build feature will be deactivated when any of these components are modified'
|
|
233
|
+
'dependency-driven build feature will be deactivated when any of these components are modified. '
|
|
234
|
+
'Must be specified together with --modified-components. '
|
|
235
|
+
'If set to "", the value would be considered as None. '
|
|
236
|
+
'If set to ";", the value would be considered as an empty list.',
|
|
223
237
|
validation_alias=AliasChoices(
|
|
224
238
|
'deactivate_dependency_driven_build_by_components', 'ignore_app_dependencies_components'
|
|
225
239
|
),
|
|
@@ -238,7 +252,10 @@ class DependencyDrivenBuildArguments(GlobalArguments):
|
|
|
238
252
|
shorthand='-df',
|
|
239
253
|
),
|
|
240
254
|
description='semicolon-separated list of file patterns. '
|
|
241
|
-
'dependency-driven build feature will be deactivated when any of matched files are modified'
|
|
255
|
+
'dependency-driven build feature will be deactivated when any of matched files are modified. '
|
|
256
|
+
'Must be specified together with --modified-files. '
|
|
257
|
+
'If set to "", the value would be considered as None. '
|
|
258
|
+
'If set to ";", the value would be considered as an empty list.',
|
|
242
259
|
validation_alias=AliasChoices(
|
|
243
260
|
'deactivate_dependency_driven_build_by_filepatterns', 'ignore_app_dependencies_filepatterns'
|
|
244
261
|
),
|
|
@@ -253,7 +270,7 @@ class DependencyDrivenBuildArguments(GlobalArguments):
|
|
|
253
270
|
)
|
|
254
271
|
compare_manifest_sha_filepath: t.Optional[str] = field(
|
|
255
272
|
None,
|
|
256
|
-
description='Path to the file containing the
|
|
273
|
+
description='Path to the file containing the hash of the manifest rules. '
|
|
257
274
|
'Compare the hash with the current manifest rules. '
|
|
258
275
|
'All matched apps will be built if the corresponding manifest rule is modified',
|
|
259
276
|
default=None,
|
|
@@ -443,7 +460,10 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
443
460
|
default=False,
|
|
444
461
|
)
|
|
445
462
|
default_build_targets: t.Optional[t.List[str]] = field(
|
|
446
|
-
|
|
463
|
+
FieldMetadata(
|
|
464
|
+
validate_method=[ValidateMethod.TO_LIST],
|
|
465
|
+
nargs='+',
|
|
466
|
+
),
|
|
447
467
|
description='space-separated list of the default enabled build targets for the apps. '
|
|
448
468
|
'When not specified, the default value is the targets listed by `idf.py --list-targets`',
|
|
449
469
|
default=None,
|
|
@@ -602,10 +622,16 @@ class BuildArguments(FindBuildArguments):
|
|
|
602
622
|
validation_alias=AliasChoices('ignore_warning_strs', 'ignore_warning_str'),
|
|
603
623
|
default=None,
|
|
604
624
|
)
|
|
605
|
-
ignore_warning_files: t.Optional[t.List[str]] = field(
|
|
625
|
+
ignore_warning_files: t.Optional[t.List[t.Union[str, TextIOWrapper]]] = field(
|
|
606
626
|
FieldMetadata(
|
|
607
|
-
|
|
627
|
+
validate_method=[ValidateMethod.TO_LIST],
|
|
628
|
+
deprecates={
|
|
629
|
+
'ignore_warning_file': {
|
|
630
|
+
'type': argparse.FileType('r'),
|
|
631
|
+
}
|
|
632
|
+
},
|
|
608
633
|
nargs='+',
|
|
634
|
+
type=argparse.FileType('r'),
|
|
609
635
|
),
|
|
610
636
|
description='Path to the files containing the patterns to ignore the warnings in the build output',
|
|
611
637
|
validation_alias=AliasChoices('ignore_warning_files', 'ignore_warning_file'),
|
|
@@ -621,7 +647,10 @@ class BuildArguments(FindBuildArguments):
|
|
|
621
647
|
|
|
622
648
|
# Attrs that support placeholders
|
|
623
649
|
collect_size_info_filename: t.Optional[str] = field(
|
|
624
|
-
|
|
650
|
+
FieldMetadata(
|
|
651
|
+
deprecates={'collect_size_info': {}},
|
|
652
|
+
hidden=True,
|
|
653
|
+
),
|
|
625
654
|
description='Record size json filepath of the built apps to the specified file. '
|
|
626
655
|
'Each line is a json string. Can expand placeholders @p',
|
|
627
656
|
validation_alias=AliasChoices('collect_size_info_filename', 'collect_size_info'),
|
|
@@ -629,7 +658,10 @@ class BuildArguments(FindBuildArguments):
|
|
|
629
658
|
exclude=True, # computed field is used
|
|
630
659
|
)
|
|
631
660
|
collect_app_info_filename: t.Optional[str] = field(
|
|
632
|
-
|
|
661
|
+
FieldMetadata(
|
|
662
|
+
deprecates={'collect_app_info': {}},
|
|
663
|
+
hidden=True,
|
|
664
|
+
),
|
|
633
665
|
description='Record serialized app model of the built apps to the specified file. '
|
|
634
666
|
'Each line is a json string. Can expand placeholders @p',
|
|
635
667
|
validation_alias=AliasChoices('collect_app_info_filename', 'collect_app_info'),
|
|
@@ -637,7 +669,10 @@ class BuildArguments(FindBuildArguments):
|
|
|
637
669
|
exclude=True, # computed field is used
|
|
638
670
|
)
|
|
639
671
|
junitxml_filename: t.Optional[str] = field(
|
|
640
|
-
|
|
672
|
+
FieldMetadata(
|
|
673
|
+
deprecates={'junitxml': {}},
|
|
674
|
+
hidden=True,
|
|
675
|
+
),
|
|
641
676
|
description='Path to the junitxml file to record the build results. Can expand placeholder @p',
|
|
642
677
|
validation_alias=AliasChoices('junitxml_filename', 'junitxml'),
|
|
643
678
|
default=None,
|
|
@@ -654,8 +689,14 @@ class BuildArguments(FindBuildArguments):
|
|
|
654
689
|
for s in self.ignore_warning_strs:
|
|
655
690
|
ignore_warnings_regexes.append(re.compile(s))
|
|
656
691
|
if self.ignore_warning_files:
|
|
657
|
-
for
|
|
658
|
-
|
|
692
|
+
for f in self.ignore_warning_files:
|
|
693
|
+
if isinstance(f, str):
|
|
694
|
+
with open(f) as fr:
|
|
695
|
+
for s in fr:
|
|
696
|
+
ignore_warnings_regexes.append(re.compile(s.strip()))
|
|
697
|
+
else:
|
|
698
|
+
for s in f:
|
|
699
|
+
ignore_warnings_regexes.append(re.compile(s.strip()))
|
|
659
700
|
App.IGNORE_WARNS_REGEXES = ignore_warnings_regexes
|
|
660
701
|
|
|
661
702
|
@computed_field # type: ignore
|
|
@@ -734,12 +775,20 @@ def add_args_to_parser(argument_cls: t.Type[BaseArguments], parser: argparse.Arg
|
|
|
734
775
|
if _shorthand:
|
|
735
776
|
_names.append(_shorthand)
|
|
736
777
|
|
|
778
|
+
if f_meta.hidden: # f is hidden, use deprecated field instead
|
|
779
|
+
help_msg = f.description
|
|
780
|
+
else:
|
|
781
|
+
help_msg = f'[Deprecated] Use {_snake_case_to_cli_arg_name(f_name)} instead'
|
|
782
|
+
|
|
737
783
|
parser.add_argument(
|
|
738
784
|
*_names,
|
|
739
785
|
**dep_f_kwargs,
|
|
740
|
-
help=
|
|
786
|
+
help=help_msg,
|
|
741
787
|
)
|
|
742
788
|
|
|
789
|
+
if f_meta and f_meta.hidden:
|
|
790
|
+
continue
|
|
791
|
+
|
|
743
792
|
names = [_snake_case_to_cli_arg_name(f_name)]
|
|
744
793
|
if f_meta and f_meta.shorthand:
|
|
745
794
|
names.append(f_meta.shorthand)
|
|
@@ -752,14 +801,19 @@ def add_args_to_parser(argument_cls: t.Type[BaseArguments], parser: argparse.Arg
|
|
|
752
801
|
kwargs['required'] = True
|
|
753
802
|
if f_meta.action:
|
|
754
803
|
kwargs['action'] = f_meta.action
|
|
804
|
+
# to make the CLI override config file work
|
|
805
|
+
if f_meta.action == 'store_true':
|
|
806
|
+
kwargs['default'] = None
|
|
807
|
+
|
|
755
808
|
if f_meta.nargs:
|
|
756
809
|
kwargs['nargs'] = f_meta.nargs
|
|
757
810
|
if f_meta.choices:
|
|
758
811
|
kwargs['choices'] = f_meta.choices
|
|
759
812
|
if f_meta.default:
|
|
760
813
|
kwargs['default'] = f_meta.default
|
|
761
|
-
|
|
762
|
-
|
|
814
|
+
|
|
815
|
+
# here in CLI arguments, don't set the default to field.default
|
|
816
|
+
# otherwise it will override the config file settings
|
|
763
817
|
|
|
764
818
|
parser.add_argument(
|
|
765
819
|
*names,
|
|
@@ -33,6 +33,7 @@ def _get_apps_from_path(
|
|
|
33
33
|
app_cls: t.Type[App] = CMakeApp,
|
|
34
34
|
args: FindArguments,
|
|
35
35
|
) -> t.List[App]:
|
|
36
|
+
# trigger test
|
|
36
37
|
def _validate_app(_app: App) -> bool:
|
|
37
38
|
if target not in _app.supported_targets:
|
|
38
39
|
LOGGER.debug('=> Ignored. %s only supports targets: %s', _app, ', '.join(_app.supported_targets))
|
|
@@ -70,6 +70,15 @@ def find_apps(
|
|
|
70
70
|
"""
|
|
71
71
|
apply_config_file(config_file)
|
|
72
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
|
+
|
|
73
82
|
if find_arguments is None:
|
|
74
83
|
find_arguments = FindArguments(
|
|
75
84
|
paths=to_list(paths), # type: ignore
|
|
@@ -125,7 +134,8 @@ def build_apps(
|
|
|
125
134
|
"""
|
|
126
135
|
apply_config_file(config_file)
|
|
127
136
|
|
|
128
|
-
|
|
137
|
+
# compatible with old usage
|
|
138
|
+
## `check_app_dependencies`
|
|
129
139
|
if 'check_app_dependencies' in kwargs:
|
|
130
140
|
LOGGER.warning(
|
|
131
141
|
'Passing "check_app_dependencies" directly is deprecated. '
|
|
@@ -138,6 +148,7 @@ def build_apps(
|
|
|
138
148
|
**kwargs,
|
|
139
149
|
)
|
|
140
150
|
|
|
151
|
+
apps = to_list(apps)
|
|
141
152
|
if apps is None:
|
|
142
153
|
apps = find_apps(
|
|
143
154
|
find_arguments=FindArguments(
|
|
@@ -431,7 +442,8 @@ def main():
|
|
|
431
442
|
for app in failed_apps:
|
|
432
443
|
print(f' {app}')
|
|
433
444
|
|
|
434
|
-
|
|
445
|
+
if ret_code != 0:
|
|
446
|
+
sys.exit(ret_code)
|
|
435
447
|
|
|
436
448
|
|
|
437
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
|
)
|
|
@@ -35,14 +35,21 @@ class IfClause:
|
|
|
35
35
|
def __init__(self, stmt: str, temporary: bool = False, reason: t.Optional[str] = None) -> None:
|
|
36
36
|
try:
|
|
37
37
|
self.stmt: BoolStmt = BOOL_EXPR.parseString(stmt)[0]
|
|
38
|
-
except ParseException:
|
|
39
|
-
raise InvalidIfClause(f'Invalid if
|
|
38
|
+
except (ParseException, InvalidIfClause) as ex:
|
|
39
|
+
raise InvalidIfClause(f'Invalid if clause: {stmt}. {ex}')
|
|
40
40
|
|
|
41
41
|
self.temporary = temporary
|
|
42
42
|
self.reason = reason
|
|
43
43
|
|
|
44
44
|
if self.temporary is True and not self.reason:
|
|
45
|
-
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
|
+
)
|
|
46
53
|
|
|
47
54
|
def get_value(self, target: str, config_name: str) -> t.Any:
|
|
48
55
|
return self.stmt.get_value(target, config_name)
|
|
@@ -249,6 +256,7 @@ class Manifest:
|
|
|
249
256
|
|
|
250
257
|
rules: t.List[FolderRule] = []
|
|
251
258
|
for path in paths:
|
|
259
|
+
LOGGER.debug('Loading manifest file %s', path)
|
|
252
260
|
_manifest = cls.from_file(path, root_path=root_path)
|
|
253
261
|
|
|
254
262
|
for rule in _manifest.rules:
|
|
@@ -344,11 +352,14 @@ class Manifest:
|
|
|
344
352
|
return x
|
|
345
353
|
|
|
346
354
|
for folder, sha_value in recorded__rel_folder__sha__dict.items():
|
|
355
|
+
# removed
|
|
347
356
|
if folder not in self__rel_folder__sha__dict:
|
|
348
357
|
diff_folders.add(_path(folder))
|
|
358
|
+
# modified
|
|
349
359
|
elif sha_value != self__rel_folder__sha__dict[folder]:
|
|
350
360
|
diff_folders.add(_path(folder))
|
|
351
361
|
|
|
362
|
+
# new
|
|
352
363
|
for folder in self__rel_folder__sha__dict:
|
|
353
364
|
if folder not in recorded__rel_folder__sha__dict:
|
|
354
365
|
diff_folders.add(_path(folder))
|
|
@@ -35,7 +35,7 @@ entry_points = \
|
|
|
35
35
|
{'console_scripts': ['idf-build-apps = idf_build_apps:main.main']}
|
|
36
36
|
|
|
37
37
|
setup(name='idf-build-apps',
|
|
38
|
-
version='2.5.
|
|
38
|
+
version='2.5.2',
|
|
39
39
|
description='Tools for building ESP-IDF related apps.',
|
|
40
40
|
author=None,
|
|
41
41
|
author_email='Fu Hanxi <fuhanxi@espressif.com>',
|
|
@@ -10,13 +10,14 @@ from idf_build_apps import (
|
|
|
10
10
|
setup_logging,
|
|
11
11
|
)
|
|
12
12
|
from idf_build_apps.args import apply_config_file
|
|
13
|
-
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN
|
|
13
|
+
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN, SUPPORTED_TARGETS
|
|
14
14
|
from idf_build_apps.manifest.manifest import FolderRule
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@pytest.fixture(autouse=True)
|
|
18
18
|
def clean_cls_attr(tmp_path):
|
|
19
19
|
App.MANIFEST = None
|
|
20
|
+
FolderRule.DEFAULT_BUILD_TARGETS = SUPPORTED_TARGETS
|
|
20
21
|
idf_build_apps.SESSION_ARGS.clean()
|
|
21
22
|
apply_config_file(IDF_BUILD_APPS_TOML_FN)
|
|
22
23
|
os.chdir(tmp_path)
|
|
@@ -66,3 +67,16 @@ def sha_of_enable_only_esp32():
|
|
|
66
67
|
)
|
|
67
68
|
|
|
68
69
|
return sha
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.fixture
|
|
73
|
+
def sha_of_enable_esp32_or_esp32s2():
|
|
74
|
+
sha = FolderRule('test1', enable=[{'if': 'IDF_TARGET == "esp32" or IDF_TARGET == "esp32s2"'}]).sha
|
|
75
|
+
|
|
76
|
+
# !!! ONLY CHANGE IT WHEN NECESSARY !!!
|
|
77
|
+
assert (
|
|
78
|
+
sha
|
|
79
|
+
== 'f3408e9bf1d6b9a9e14559e6567917986678a3414229b29f96493aec4dc1bc3e6d0ecc4f79adced0d5c26bc1cd80a4d15fe6aaefa5d1e7033a58290374f4fc7f' # noqa: E501
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return sha
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from xml.etree import ElementTree
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from conftest import (
|
|
7
|
+
create_project,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from idf_build_apps import App
|
|
11
|
+
from idf_build_apps.args import (
|
|
12
|
+
BuildArguments,
|
|
13
|
+
DependencyDrivenBuildArguments,
|
|
14
|
+
FindArguments,
|
|
15
|
+
FindBuildArguments,
|
|
16
|
+
)
|
|
17
|
+
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN
|
|
18
|
+
from idf_build_apps.main import main
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_init_attr_deprecated_by():
|
|
22
|
+
args = DependencyDrivenBuildArguments(
|
|
23
|
+
ignore_app_dependencies_filepatterns=['bar'],
|
|
24
|
+
modified_files=['barfile'],
|
|
25
|
+
)
|
|
26
|
+
assert args.deactivate_dependency_driven_build_by_filepatterns == ['bar']
|
|
27
|
+
|
|
28
|
+
args = DependencyDrivenBuildArguments(
|
|
29
|
+
deactivate_dependency_driven_build_by_components=['foo'],
|
|
30
|
+
modified_components=['foocomp'],
|
|
31
|
+
)
|
|
32
|
+
assert args.deactivate_dependency_driven_build_by_components == ['foo']
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.mark.parametrize(
|
|
36
|
+
'kwargs, expected',
|
|
37
|
+
[
|
|
38
|
+
({'config': 'foo'}, ['foo']),
|
|
39
|
+
({'config_rules_str': 'bar'}, ['bar']),
|
|
40
|
+
({'config_rules': ['baz']}, ['baz']),
|
|
41
|
+
],
|
|
42
|
+
)
|
|
43
|
+
def test_init_attr_override(kwargs, expected):
|
|
44
|
+
args = FindBuildArguments(
|
|
45
|
+
**kwargs,
|
|
46
|
+
)
|
|
47
|
+
assert args.config_rules == expected
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_apply_config_with_deprecated_names():
|
|
51
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
52
|
+
fw.write("""config = [
|
|
53
|
+
"foo"
|
|
54
|
+
]
|
|
55
|
+
no_color = true
|
|
56
|
+
""")
|
|
57
|
+
|
|
58
|
+
args = FindBuildArguments()
|
|
59
|
+
assert args.config_rules == ['foo']
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_empty_argument():
|
|
63
|
+
args = FindArguments()
|
|
64
|
+
assert args.config_rules is None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_build_args_expansion():
|
|
68
|
+
args = BuildArguments(
|
|
69
|
+
parallel_index=2, collect_app_info='@p.txt', junitxml='x_@p.txt', collect_size_info='@p_@p.txt'
|
|
70
|
+
)
|
|
71
|
+
assert args.collect_app_info == '2.txt'
|
|
72
|
+
assert args.junitxml == 'x_2.txt'
|
|
73
|
+
|
|
74
|
+
args.parallel_index = 3
|
|
75
|
+
assert args.collect_app_info == '3.txt'
|
|
76
|
+
assert args.junitxml == 'x_3.txt'
|
|
77
|
+
assert args.collect_size_info == '3_3.txt'
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_func_overwrite_config():
|
|
81
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
82
|
+
fw.write("""config = [
|
|
83
|
+
"foo"
|
|
84
|
+
]
|
|
85
|
+
modified_components = [
|
|
86
|
+
'comp1',
|
|
87
|
+
]
|
|
88
|
+
no_color = true
|
|
89
|
+
""")
|
|
90
|
+
|
|
91
|
+
args = FindArguments(
|
|
92
|
+
config=['bar'],
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert args.config_rules == ['bar']
|
|
96
|
+
assert args.modified_components == ['comp1']
|
|
97
|
+
assert args.modified_files is None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_func_overwrite_toml_overwrite_pyproject_toml():
|
|
101
|
+
with open('pyproject.toml', 'w') as fw:
|
|
102
|
+
fw.write("""[tool.idf-build-apps]
|
|
103
|
+
config = [
|
|
104
|
+
"foo"
|
|
105
|
+
]
|
|
106
|
+
modified_components = [
|
|
107
|
+
'comp1',
|
|
108
|
+
]
|
|
109
|
+
ignore_app_dependencies_components = [
|
|
110
|
+
'baz'
|
|
111
|
+
]
|
|
112
|
+
verbose=3
|
|
113
|
+
""")
|
|
114
|
+
|
|
115
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
116
|
+
fw.write("""config = [
|
|
117
|
+
"bar"
|
|
118
|
+
]
|
|
119
|
+
modified_files = [
|
|
120
|
+
'file1',
|
|
121
|
+
]
|
|
122
|
+
""")
|
|
123
|
+
|
|
124
|
+
args = FindArguments(
|
|
125
|
+
modified_components=['comp2'],
|
|
126
|
+
)
|
|
127
|
+
assert args.config_rules == ['bar']
|
|
128
|
+
assert args.modified_components == ['comp2']
|
|
129
|
+
assert args.modified_files == ['file1']
|
|
130
|
+
assert args.verbose == 3
|
|
131
|
+
assert args.deactivate_dependency_driven_build_by_components == ['baz']
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class TestIgnoreWarningFile:
|
|
135
|
+
def test_deprecated_cli(self, monkeypatch, capsys):
|
|
136
|
+
with open('foo.txt', 'w') as fw:
|
|
137
|
+
fw.write('warning:xxx')
|
|
138
|
+
|
|
139
|
+
with monkeypatch.context() as m:
|
|
140
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build', '--ignore-warning-file', 'foo.txt'])
|
|
141
|
+
main()
|
|
142
|
+
|
|
143
|
+
assert len(App.IGNORE_WARNS_REGEXES) == 1
|
|
144
|
+
assert App.IGNORE_WARNS_REGEXES[0].pattern == 'warning:xxx'
|
|
145
|
+
|
|
146
|
+
with open('bar.txt', 'w') as fw:
|
|
147
|
+
fw.write('warning:yyy')
|
|
148
|
+
|
|
149
|
+
with monkeypatch.context() as m:
|
|
150
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build', '--ignore-warning-file', 'foo.txt', 'bar.txt'])
|
|
151
|
+
with pytest.raises(SystemExit):
|
|
152
|
+
main()
|
|
153
|
+
|
|
154
|
+
assert 'unrecognized arguments: bar.txt' in capsys.readouterr().err
|
|
155
|
+
|
|
156
|
+
def test_new_cli(self, monkeypatch):
|
|
157
|
+
with open('foo.txt', 'w') as fw:
|
|
158
|
+
fw.write('warning:xxx')
|
|
159
|
+
with open('bar.txt', 'w') as fw:
|
|
160
|
+
fw.write('warning:yyy')
|
|
161
|
+
|
|
162
|
+
with monkeypatch.context() as m:
|
|
163
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build', '--ignore-warning-files', 'foo.txt', 'bar.txt'])
|
|
164
|
+
main()
|
|
165
|
+
|
|
166
|
+
assert len(App.IGNORE_WARNS_REGEXES) == 2
|
|
167
|
+
assert App.IGNORE_WARNS_REGEXES[0].pattern == 'warning:xxx'
|
|
168
|
+
assert App.IGNORE_WARNS_REGEXES[1].pattern == 'warning:yyy'
|
|
169
|
+
|
|
170
|
+
def test_func_with_str(self):
|
|
171
|
+
with open('foo.txt', 'w') as fw:
|
|
172
|
+
fw.write('warning:xxx')
|
|
173
|
+
with open('bar.txt', 'w') as fw:
|
|
174
|
+
fw.write('warning:yyy')
|
|
175
|
+
|
|
176
|
+
BuildArguments(
|
|
177
|
+
ignore_warning_files=['foo.txt', 'bar.txt'],
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
assert len(App.IGNORE_WARNS_REGEXES) == 2
|
|
181
|
+
assert App.IGNORE_WARNS_REGEXES[0].pattern == 'warning:xxx'
|
|
182
|
+
assert App.IGNORE_WARNS_REGEXES[1].pattern == 'warning:yyy'
|
|
183
|
+
|
|
184
|
+
def test_func_with_fs(self):
|
|
185
|
+
with open('foo.txt', 'w') as fw:
|
|
186
|
+
fw.write('warning:xxx')
|
|
187
|
+
with open('bar.txt', 'w') as fw:
|
|
188
|
+
fw.write('warning:yyy')
|
|
189
|
+
|
|
190
|
+
BuildArguments(
|
|
191
|
+
ignore_warning_file=[open('foo.txt'), open('bar.txt')],
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
assert len(App.IGNORE_WARNS_REGEXES) == 2
|
|
195
|
+
assert App.IGNORE_WARNS_REGEXES[0].pattern == 'warning:xxx'
|
|
196
|
+
assert App.IGNORE_WARNS_REGEXES[1].pattern == 'warning:yyy'
|
|
197
|
+
|
|
198
|
+
def test_ignore_extra_fields(self):
|
|
199
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
200
|
+
fw.write("""dry_run = true""")
|
|
201
|
+
|
|
202
|
+
args = FindArguments()
|
|
203
|
+
assert not hasattr(args, 'dry_run')
|
|
204
|
+
|
|
205
|
+
def test_config_file(self, tmp_path, monkeypatch):
|
|
206
|
+
create_project('foo', tmp_path)
|
|
207
|
+
|
|
208
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
209
|
+
fw.write("""paths = ["foo"]
|
|
210
|
+
target = "esp32"
|
|
211
|
+
build_dir = "build_@t"
|
|
212
|
+
junitxml = "test.xml"
|
|
213
|
+
keep_going = true
|
|
214
|
+
""")
|
|
215
|
+
|
|
216
|
+
# test basic config
|
|
217
|
+
with monkeypatch.context() as m:
|
|
218
|
+
m.setenv('PATH', 'foo') # let build fail
|
|
219
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build'])
|
|
220
|
+
with pytest.raises(SystemExit):
|
|
221
|
+
main()
|
|
222
|
+
|
|
223
|
+
with open('test.xml') as f:
|
|
224
|
+
xml = ElementTree.fromstring(f.read())
|
|
225
|
+
test_suite = xml.findall('testsuite')[0]
|
|
226
|
+
assert test_suite.attrib['failures'] == '1'
|
|
227
|
+
assert test_suite.attrib['errors'] == '0'
|
|
228
|
+
assert test_suite.attrib['skipped'] == '0'
|
|
229
|
+
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'
|
|
230
|
+
|
|
231
|
+
# test cli overrides config
|
|
232
|
+
with monkeypatch.context() as m:
|
|
233
|
+
m.setenv('PATH', 'foo') # let build fail
|
|
234
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build', '--build-dir', 'build_hi_@t'])
|
|
235
|
+
with pytest.raises(SystemExit):
|
|
236
|
+
main()
|
|
237
|
+
|
|
238
|
+
with open('test.xml') as f:
|
|
239
|
+
xml = ElementTree.fromstring(f.read())
|
|
240
|
+
test_suite = xml.findall('testsuite')[0]
|
|
241
|
+
assert test_suite.attrib['failures'] == '1'
|
|
242
|
+
assert test_suite.attrib['errors'] == '0'
|
|
243
|
+
assert test_suite.attrib['skipped'] == '0'
|
|
244
|
+
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_hi_esp32'
|
|
245
|
+
|
|
246
|
+
# test cli action_true
|
|
247
|
+
with monkeypatch.context() as m:
|
|
248
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build', '--dry-run'])
|
|
249
|
+
main()
|
|
250
|
+
|
|
251
|
+
with open('test.xml') as f:
|
|
252
|
+
xml = ElementTree.fromstring(f.read())
|
|
253
|
+
test_suite = xml.findall('testsuite')[0]
|
|
254
|
+
assert test_suite.attrib['failures'] == '0'
|
|
255
|
+
assert test_suite.attrib['errors'] == '0'
|
|
256
|
+
assert test_suite.attrib['skipped'] == '1'
|
|
257
|
+
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'
|
|
258
|
+
|
|
259
|
+
# test config store_true set to true
|
|
260
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'a') as fw:
|
|
261
|
+
fw.write('\ndry_run = true\n')
|
|
262
|
+
|
|
263
|
+
with monkeypatch.context() as m:
|
|
264
|
+
m.setattr('sys.argv', ['idf-build-apps', 'build'])
|
|
265
|
+
main()
|
|
266
|
+
|
|
267
|
+
with open('test.xml') as f:
|
|
268
|
+
xml = ElementTree.fromstring(f.read())
|
|
269
|
+
test_suite = xml.findall('testsuite')[0]
|
|
270
|
+
assert test_suite.attrib['failures'] == '0'
|
|
271
|
+
assert test_suite.attrib['errors'] == '0'
|
|
272
|
+
assert test_suite.attrib['skipped'] == '1'
|
|
273
|
+
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'
|
|
274
|
+
|
|
275
|
+
# test config store_true set to false, but CLI set to true
|
|
276
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
277
|
+
fw.write("""paths = ["foo"]
|
|
278
|
+
build_dir = "build_@t"
|
|
279
|
+
junitxml = "test.xml"
|
|
280
|
+
dry_run = false
|
|
281
|
+
""")
|
|
282
|
+
|
|
283
|
+
with monkeypatch.context() as m:
|
|
284
|
+
m.setattr(
|
|
285
|
+
'sys.argv', ['idf-build-apps', 'build', '--default-build-targets', 'esp32', 'esp32s2', '--dry-run']
|
|
286
|
+
)
|
|
287
|
+
main()
|
|
288
|
+
|
|
289
|
+
with open('test.xml') as f:
|
|
290
|
+
xml = ElementTree.fromstring(f.read())
|
|
291
|
+
test_suite = xml.findall('testsuite')[0]
|
|
292
|
+
assert test_suite.attrib['failures'] == '0'
|
|
293
|
+
assert test_suite.attrib['errors'] == '0'
|
|
294
|
+
assert test_suite.attrib['skipped'] == '2'
|
|
295
|
+
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'
|
|
296
|
+
assert test_suite.findall('testcase')[1].attrib['name'] == 'foo/build_esp32s2'
|
|
@@ -10,13 +10,6 @@ from idf_build_apps.main import main
|
|
|
10
10
|
from idf_build_apps.utils import InvalidCommand
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class FakeArgs:
|
|
14
|
-
"""like what argparse.Namespace does"""
|
|
15
|
-
|
|
16
|
-
def __init__(self, **kwargs):
|
|
17
|
-
self.__dict__.update(kwargs)
|
|
18
|
-
|
|
19
|
-
|
|
20
13
|
@pytest.mark.parametrize(
|
|
21
14
|
'args, expected_error',
|
|
22
15
|
[
|
|
@@ -26,7 +19,9 @@ class FakeArgs:
|
|
|
26
19
|
(['--manifest-files', 'test.yml', '--output', 'test.sha1'], None),
|
|
27
20
|
],
|
|
28
21
|
)
|
|
29
|
-
def test_manifest_dump_sha_values(
|
|
22
|
+
def test_manifest_dump_sha_values(
|
|
23
|
+
args, expected_error, sha_of_enable_only_esp32, sha_of_enable_esp32_or_esp32s2, capsys, monkeypatch
|
|
24
|
+
):
|
|
30
25
|
Path('test.yml').write_text(
|
|
31
26
|
"""
|
|
32
27
|
foo:
|
|
@@ -38,6 +33,9 @@ bar:
|
|
|
38
33
|
baz:
|
|
39
34
|
enable:
|
|
40
35
|
- if: IDF_TARGET == "esp32"
|
|
36
|
+
foobar:
|
|
37
|
+
enable:
|
|
38
|
+
- if: IDF_TARGET == "esp32" or IDF_TARGET == "esp32s2"
|
|
41
39
|
""",
|
|
42
40
|
encoding='utf8',
|
|
43
41
|
)
|
|
@@ -67,4 +65,5 @@ baz:
|
|
|
67
65
|
f'bar:{sha_of_enable_only_esp32}\n'
|
|
68
66
|
f'baz:{sha_of_enable_only_esp32}\n'
|
|
69
67
|
f'foo:{sha_of_enable_only_esp32}\n'
|
|
68
|
+
f'foobar:{sha_of_enable_esp32_or_esp32s2}\n'
|
|
70
69
|
)
|
|
@@ -102,7 +102,7 @@ get-started:
|
|
|
102
102
|
"""
|
|
103
103
|
examples/get-started:
|
|
104
104
|
enable:
|
|
105
|
-
- if: IDF_VERSION_MAJOR > 0 and IDF_VERSION_MINOR < 999
|
|
105
|
+
- if: IDF_VERSION_MAJOR > 0 and IDF_VERSION_MINOR < 999
|
|
106
106
|
""",
|
|
107
107
|
encoding='utf8',
|
|
108
108
|
)
|
|
@@ -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 os
|
|
5
4
|
|
|
6
5
|
import pytest
|
|
@@ -522,9 +521,17 @@ class TestIfParser:
|
|
|
522
521
|
|
|
523
522
|
def test_invalid_if_statement(self):
|
|
524
523
|
statement = '1'
|
|
525
|
-
with pytest.raises(InvalidIfClause, match='Invalid if
|
|
524
|
+
with pytest.raises(InvalidIfClause, match='Invalid if clause: 1'):
|
|
526
525
|
IfClause(statement)
|
|
527
526
|
|
|
528
527
|
def test_temporary_must_with_reason(self):
|
|
529
528
|
with pytest.raises(InvalidIfClause, match='"reason" must be set when "temporary: true"'):
|
|
530
529
|
IfClause(stmt='IDF_TARGET == "esp32"', temporary=True)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def test_consecutive_or_and_stml_manifest():
|
|
533
|
+
with pytest.raises(InvalidIfClause, match='Chaining "and"/"or" is not allowed'):
|
|
534
|
+
IfClause(stmt='IDF_TARGET == "esp32" or IDF_TARGET == "esp32c3" or IDF_TARGET == "esp32s3"')
|
|
535
|
+
|
|
536
|
+
with pytest.raises(InvalidIfClause, match='Chaining "and"/"or" is not allowed'):
|
|
537
|
+
IfClause(stmt='IDF_TARGET == "esp32" or IDF_TARGET == "esp32c3" and IDF_TARGET == "esp32s3"')
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
from idf_build_apps.args import (
|
|
7
|
-
BuildArguments,
|
|
8
|
-
DependencyDrivenBuildArguments,
|
|
9
|
-
FindArguments,
|
|
10
|
-
FindBuildArguments,
|
|
11
|
-
)
|
|
12
|
-
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_init_attr_deprecated_by():
|
|
16
|
-
args = DependencyDrivenBuildArguments(
|
|
17
|
-
ignore_app_dependencies_filepatterns=['bar'],
|
|
18
|
-
modified_files=['barfile'],
|
|
19
|
-
)
|
|
20
|
-
assert args.deactivate_dependency_driven_build_by_filepatterns == ['bar']
|
|
21
|
-
|
|
22
|
-
args = DependencyDrivenBuildArguments(
|
|
23
|
-
deactivate_dependency_driven_build_by_components=['foo'],
|
|
24
|
-
modified_components=['foocomp'],
|
|
25
|
-
)
|
|
26
|
-
assert args.deactivate_dependency_driven_build_by_components == ['foo']
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.mark.parametrize(
|
|
30
|
-
'kwargs, expected',
|
|
31
|
-
[
|
|
32
|
-
({'config': 'foo'}, ['foo']),
|
|
33
|
-
({'config_rules_str': 'bar'}, ['bar']),
|
|
34
|
-
({'config_rules': ['baz']}, ['baz']),
|
|
35
|
-
],
|
|
36
|
-
)
|
|
37
|
-
def test_init_attr_override(kwargs, expected):
|
|
38
|
-
args = FindBuildArguments(
|
|
39
|
-
**kwargs,
|
|
40
|
-
)
|
|
41
|
-
assert args.config_rules == expected
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def test_apply_config_with_deprecated_names():
|
|
45
|
-
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
46
|
-
fw.write("""config = [
|
|
47
|
-
"foo"
|
|
48
|
-
]
|
|
49
|
-
no_color = true
|
|
50
|
-
""")
|
|
51
|
-
|
|
52
|
-
args = FindBuildArguments()
|
|
53
|
-
assert args.config_rules == ['foo']
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_empty_argument():
|
|
57
|
-
args = FindArguments()
|
|
58
|
-
assert args.config_rules is None
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_build_args_expansion():
|
|
62
|
-
args = BuildArguments(
|
|
63
|
-
parallel_index=2, collect_app_info='@p.txt', junitxml='x_@p.txt', collect_size_info='@p_@p.txt'
|
|
64
|
-
)
|
|
65
|
-
assert args.collect_app_info == '2.txt'
|
|
66
|
-
assert args.junitxml == 'x_2.txt'
|
|
67
|
-
|
|
68
|
-
args.parallel_index = 3
|
|
69
|
-
assert args.collect_app_info == '3.txt'
|
|
70
|
-
assert args.junitxml == 'x_3.txt'
|
|
71
|
-
assert args.collect_size_info == '3_3.txt'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def test_func_overwrite_config():
|
|
75
|
-
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
76
|
-
fw.write("""config = [
|
|
77
|
-
"foo"
|
|
78
|
-
]
|
|
79
|
-
modified_components = [
|
|
80
|
-
'comp1',
|
|
81
|
-
]
|
|
82
|
-
no_color = true
|
|
83
|
-
""")
|
|
84
|
-
|
|
85
|
-
args = FindArguments(
|
|
86
|
-
config=['bar'],
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
assert args.config_rules == ['bar']
|
|
90
|
-
assert args.modified_components == ['comp1']
|
|
91
|
-
assert args.modified_files is None
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_func_overwrite_toml_overwrite_pyproject_toml():
|
|
95
|
-
with open('pyproject.toml', 'w') as fw:
|
|
96
|
-
fw.write("""[tool.idf-build-apps]
|
|
97
|
-
config = [
|
|
98
|
-
"foo"
|
|
99
|
-
]
|
|
100
|
-
modified_components = [
|
|
101
|
-
'comp1',
|
|
102
|
-
]
|
|
103
|
-
ignore_app_dependencies_components = [
|
|
104
|
-
'baz'
|
|
105
|
-
]
|
|
106
|
-
verbose=3
|
|
107
|
-
""")
|
|
108
|
-
|
|
109
|
-
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
110
|
-
fw.write("""config = [
|
|
111
|
-
"bar"
|
|
112
|
-
]
|
|
113
|
-
modified_files = [
|
|
114
|
-
'file1',
|
|
115
|
-
]
|
|
116
|
-
""")
|
|
117
|
-
|
|
118
|
-
args = FindArguments(
|
|
119
|
-
modified_components=['comp2'],
|
|
120
|
-
)
|
|
121
|
-
assert args.config_rules == ['bar']
|
|
122
|
-
assert args.modified_components == ['comp2']
|
|
123
|
-
assert args.modified_files == ['file1']
|
|
124
|
-
assert args.verbose == 3
|
|
125
|
-
assert args.deactivate_dependency_driven_build_by_components == ['baz']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/dependency_driven_build.rst
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|