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.
Files changed (73) hide show
  1. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/test-build-idf-apps.yml +6 -1
  2. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/CHANGELOG.md +16 -17
  3. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/PKG-INFO +1 -1
  4. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/__init__.py +1 -1
  5. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/args.py +72 -18
  6. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/finder.py +1 -0
  7. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/main.py +14 -2
  8. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/if_parser.py +20 -11
  9. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/manifest.py +14 -3
  10. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/pyproject.toml +1 -1
  11. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/setup.py +1 -1
  12. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/conftest.py +15 -1
  13. idf_build_apps-2.5.2/tests/test_args.py +296 -0
  14. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_cmd.py +7 -8
  15. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_finder.py +1 -1
  16. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_manifest.py +9 -2
  17. idf_build_apps-2.5.0rc2/tests/test_args.py +0 -125
  18. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.editorconfig +0 -0
  19. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.git-blame-ignore-revs +0 -0
  20. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.gitattributes +0 -0
  21. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/dependabot.yml +0 -0
  22. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/check-pre-commit.yml +0 -0
  23. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/issue_comment.yml +0 -0
  24. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/new_issues.yml +0 -0
  25. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/new_prs.yml +0 -0
  26. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/publish-pypi.yml +0 -0
  27. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.github/workflows/test-build-docs.yml +0 -0
  28. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.gitignore +0 -0
  29. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.pre-commit-config.yaml +0 -0
  30. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/.readthedocs.yml +0 -0
  31. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/CONTRIBUTING.md +0 -0
  32. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/LICENSE +0 -0
  33. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/README.md +0 -0
  34. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_apidoc_templates/module.rst_t +0 -0
  35. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_apidoc_templates/package.rst_t +0 -0
  36. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_apidoc_templates/toc.rst_t +0 -0
  37. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_static/espressif-logo.svg +0 -0
  38. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_static/theme_overrides.css +0 -0
  39. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/_templates/layout.html +0 -0
  40. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/conf_common.py +0 -0
  41. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/Makefile +0 -0
  42. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/conf.py +0 -0
  43. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/build.rst +0 -0
  44. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/config_rules.rst +0 -0
  45. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/dependency_driven_build.rst +0 -0
  46. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/explanations/find.rst +0 -0
  47. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/guides/1.x_to_2.x.md +0 -0
  48. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/index.rst +0 -0
  49. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/others/CHANGELOG.md +0 -0
  50. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/others/CONTRIBUTING.md +0 -0
  51. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/references/cli.rst +0 -0
  52. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/references/config_file.md +0 -0
  53. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/docs/en/references/manifest.rst +0 -0
  54. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/__main__.py +0 -0
  55. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/app.py +0 -0
  56. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/autocompletions.py +0 -0
  57. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/constants.py +0 -0
  58. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/junit/__init__.py +0 -0
  59. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/junit/report.py +0 -0
  60. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/junit/utils.py +0 -0
  61. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/log.py +0 -0
  62. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/__init__.py +0 -0
  63. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/manifest/soc_header.py +0 -0
  64. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/session_args.py +0 -0
  65. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/utils.py +0 -0
  66. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/vendors/__init__.py +0 -0
  67. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/vendors/pydantic_sources.py +0 -0
  68. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/yaml/__init__.py +0 -0
  69. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/idf_build_apps/yaml/parser.py +0 -0
  70. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/license_header.txt +0 -0
  71. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_app.py +0 -0
  72. {idf_build_apps-2.5.0rc2 → idf_build_apps-2.5.2}/tests/test_build.py +0 -0
  73. {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 report.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.0rc2 (2024-09-23)
5
+ ## v2.5.2 (2024-09-27)
6
6
 
7
7
  ### Fix
8
8
 
9
- - chdir to tmp_path at the beginning of each test case
10
- - load config before cli args and func args
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.0rc1 (2024-09-17)
12
+ ## v2.5.1 (2024-09-26)
15
13
 
16
14
  ### Fix
17
15
 
18
- - build_arguments expand @p placeholders
16
+ - stop using lambda functions since they cannot be pickled
19
17
 
20
- ### Refactor
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
- - support `idf-build-apps find` with checking modified manfiest files
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
- - loose env var requirements. IDF_PATH not required
34
- - stop print build log as error when build with warning-as-error
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
- - temp build log got wrongly created/deleted
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 params
39
+ - move Manifest.ROOTPATH to arguments
40
+ - expand @p placeholders in `BuildArguments`
42
41
 
43
42
  ## v2.4.3 (2024-08-07)
44
43
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: idf-build-apps
3
- Version: 2.5.0rc2
3
+ Version: 2.5.2
4
4
  Summary: Tools for building ESP-IDF related apps.
5
5
  Author-email: Fu Hanxi <fuhanxi@espressif.com>
6
6
  Requires-Python: >=3.7
@@ -8,7 +8,7 @@ Tools for building ESP-IDF related apps.
8
8
  # ruff: noqa: E402
9
9
  # avoid circular imports
10
10
 
11
- __version__ = '2.5.0rc2'
11
+ __version__ = '2.5.2'
12
12
 
13
13
  from .session_args import (
14
14
  SessionArgs,
@@ -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 and ValidateMethod.TO_LIST in meta.validate_method:
139
- return to_list(v)
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 sha256 hash of the manifest rules. '
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
- None,
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
- deprecates={'ignore_warning_file': {}},
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
- None,
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
- None,
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
- None,
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 s in self.ignore_warning_files:
658
- ignore_warnings_regexes.append(re.compile(s.strip()))
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=f'[Deprecated] Use {_snake_case_to_cli_arg_name(f_name)} instead',
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
- if 'default' not in kwargs:
762
- kwargs['default'] = f.default
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
- apps = to_list(apps)
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
- sys.exit(ret_code)
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
- class BoolAnd(BoolExpr):
181
- def __init__(self, t: ParseResults):
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
- def get_value(self, target: str, config_name: str) -> Any:
186
- return self.left.get_value(target, config_name) and self.right.get_value(target, config_name)
184
+
185
+ def _or(_l, _r):
186
+ return _l or _r
187
187
 
188
188
 
189
- class BoolOr(BoolExpr):
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) or self.right.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, BoolAnd),
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 statement: {stmt}')
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('"reason" must be set when "temporary: true"')
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))
@@ -60,7 +60,7 @@ idf-build-apps = "idf_build_apps:main.main"
60
60
 
61
61
  [tool.commitizen]
62
62
  name = "cz_conventional_commits"
63
- version = "2.5.0rc2"
63
+ version = "2.5.2"
64
64
  tag_format = "v$version"
65
65
  version_files = [
66
66
  "idf_build_apps/__init__.py",
@@ -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.0rc2',
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(args, expected_error, sha_of_enable_only_esp32, capsys, monkeypatch):
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 and IDF_VERSION_PATCH in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
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 statement: 1'):
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']