idf-build-apps 2.0.0rc0__tar.gz → 2.0.0rc1__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.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/check-pre-commit.yml +4 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/test-build-idf-apps.yml +3 -3
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/CHANGELOG.md +26 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/PKG-INFO +1 -1
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/index.rst +1 -0
- idf_build_apps-2.0.0rc1/docs/migration/1.x_to_2.x.md +135 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/__init__.py +4 -3
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/app.py +81 -69
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/constants.py +3 -3
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/main.py +42 -3
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/manifest/manifest.py +11 -9
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/session_args.py +17 -9
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/utils.py +43 -14
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/pyproject.toml +1 -1
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/setup.py +1 -1
- idf_build_apps-2.0.0rc1/tests/test_app.py +95 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/tests/test_manifest.py +29 -2
- idf_build_apps-2.0.0rc0/tests/test_app.py +0 -34
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.editorconfig +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.git-blame-ignore-revs +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.gitattributes +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/dependabot.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/issue_comment.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/new_issues.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/new_prs.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/publish-pypi.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/test-build-docs.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.gitignore +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.pre-commit-config.yaml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.readthedocs.yml +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/LICENSE +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/README.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/CHANGELOG.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/Makefile +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/_apidoc_templates/module.rst_t +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/_apidoc_templates/package.rst_t +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/_apidoc_templates/toc.rst_t +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/_static/espressif-logo.svg +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/_static/theme_overrides.css +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/_templates/layout.html +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/cli.rst +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/conf.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/config_file.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/find_build.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/docs/manifest.md +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/__main__.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/build_apps_args.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/config.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/finder.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/junit/__init__.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/junit/report.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/junit/utils.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/log.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/manifest/__init__.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/manifest/if_parser.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/idf_build_apps/manifest/soc_header.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/license_header.txt +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/tests/conftest.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/tests/test_build.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/tests/test_finder.py +0 -0
- {idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/tests/test_utils.py +0 -0
{idf_build_apps-2.0.0rc0 → idf_build_apps-2.0.0rc1}/.github/workflows/test-build-idf-apps.yml
RENAMED
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
pip install flit
|
|
29
29
|
flit build
|
|
30
30
|
- name: Upload built python packages
|
|
31
|
-
uses: actions/upload-artifact@
|
|
31
|
+
uses: actions/upload-artifact@v4
|
|
32
32
|
with:
|
|
33
33
|
name: wheel
|
|
34
34
|
path: dist/idf_build_apps-*.whl
|
|
@@ -38,13 +38,13 @@ jobs:
|
|
|
38
38
|
needs: build-python-packages
|
|
39
39
|
strategy:
|
|
40
40
|
matrix:
|
|
41
|
-
idf-branch: [ release-v5.0, release-v5.1 ]
|
|
41
|
+
idf-branch: [ release-v5.0, release-v5.1, release-v5.2 ]
|
|
42
42
|
runs-on: ubuntu-latest
|
|
43
43
|
container:
|
|
44
44
|
image: espressif/idf:${{ matrix.idf-branch }}
|
|
45
45
|
steps:
|
|
46
46
|
- name: Download wheel
|
|
47
|
-
uses: actions/download-artifact@
|
|
47
|
+
uses: actions/download-artifact@v4
|
|
48
48
|
with:
|
|
49
49
|
name: wheel
|
|
50
50
|
- name: Build the Apps
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v2.0.0rc1 (2024-01-05)
|
|
6
|
+
|
|
7
|
+
### BREAKING CHANGE
|
|
8
|
+
|
|
9
|
+
- - Turn `app.build()` arguments to kwargs
|
|
10
|
+
|
|
11
|
+
### Feat
|
|
12
|
+
|
|
13
|
+
- support custom `_pre_build`, `_post_build` in App instances
|
|
14
|
+
- add `json_to_app` method with support custom classes
|
|
15
|
+
|
|
16
|
+
### Fix
|
|
17
|
+
|
|
18
|
+
- remove os-specific `os.mknod`
|
|
19
|
+
- sort App instance correctly
|
|
20
|
+
- modify yaml dict shared by yaml anchors
|
|
21
|
+
- make build_comment a `dump_only` field
|
|
22
|
+
- improve error message when env var IDF_PATH not set
|
|
23
|
+
- search sdkconfig path
|
|
24
|
+
|
|
5
25
|
## v2.0.0rc0 (2023-12-18)
|
|
6
26
|
|
|
7
27
|
### Refactor
|
|
@@ -106,6 +126,12 @@ All notable changes to this project will be documented in this file.
|
|
|
106
126
|
- migrate `App` class to pydantic model
|
|
107
127
|
- update dependencies and do code upgrade to python 3.7
|
|
108
128
|
|
|
129
|
+
## v1.1.4 (2023-12-29)
|
|
130
|
+
|
|
131
|
+
### Fix
|
|
132
|
+
|
|
133
|
+
- stop modifying yaml dict shared by yaml anchors
|
|
134
|
+
|
|
109
135
|
## v1.1.3 (2023-11-13)
|
|
110
136
|
|
|
111
137
|
### Fix
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Migration From 1.x to 2.x
|
|
2
|
+
|
|
3
|
+
There are a few breaking changes in 2.x. This document will help you migrate your code from 1.x to 2.x.
|
|
4
|
+
|
|
5
|
+
## Python Version Support
|
|
6
|
+
|
|
7
|
+
idf-build-apps 1.x supports Python 2.7 and Python 3.4 or newer. idf-build-apps 2.x only supports Python 3.7 or newer.
|
|
8
|
+
|
|
9
|
+
## Logging Related Changes
|
|
10
|
+
|
|
11
|
+
In 2.x, we're following the standard Python logging convention.
|
|
12
|
+
|
|
13
|
+
Before:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from idf_build_apps import LOGGER
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
After:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import logging
|
|
23
|
+
idf_build_apps_logger = logging.getLogger('idf_build_apps')
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Normal Arguments to Keyword-only Arguments
|
|
27
|
+
|
|
28
|
+
In 2.x, we move some arguments from normal arguments to keyword-only arguments. This is to make the API more consistent and easier to use.
|
|
29
|
+
|
|
30
|
+
To understand the difference between these terms better, here's a quick summary:
|
|
31
|
+
- "positonal-only argument" means the argument is a positional-only argument. (python 3.8+ only)
|
|
32
|
+
- "keyword-only argument" means the argument is a keyword-only argument.
|
|
33
|
+
- "normal argument" means the argument is not a positional-only argument, nor a keyword-only argument.
|
|
34
|
+
|
|
35
|
+
For example, in the following function:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
def foo(a, /, b, c, *, d=1, e=2, f=3):
|
|
39
|
+
pass
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- "a" is a positional-only argument.
|
|
43
|
+
- "b" and "c" are normal arguments.
|
|
44
|
+
- "d", "e", and "f" are keyword-only arguments.
|
|
45
|
+
|
|
46
|
+
The following calls are valid:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
foo(1, 2, 3, d=4, e=5, f=6)
|
|
50
|
+
foo(1, 2, c=3, d=4, e=5, f=6)
|
|
51
|
+
foo(1, b=2, c=3, d=4, e=5, f=6)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The following calls are invalid:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
foo(1, 2, 3, 4, 5, 6)
|
|
58
|
+
foo(1, b=2, 3, d=4, e=5, f=6)
|
|
59
|
+
foo(a=1, b=2, c=3, d=4, e=5, f=6)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `App.__init__()`
|
|
63
|
+
|
|
64
|
+
The `__init__` function of `App` class, and all its sub-classes, like `CMakeApp`, and `MakeApp`, now takes only `app_dir`, and `target` as normal arguments. All the rest of the arguments are keyword-only arguments.
|
|
65
|
+
|
|
66
|
+
Before:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
app = App('foo', 'esp32', 'sdkconfig.ci', 'default')
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
After:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
app = App('foo', 'esp32', sdkconfig_path='sdkconfig.ci', config_name='default')
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
or all in keyword-only arguments:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
app = App(app_dir='foo', target='esp32', sdkconfig_path='sdkconfig.ci', config_name='default')
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `App.build()`
|
|
85
|
+
|
|
86
|
+
The `build` function of `App` class, and all its sub-classes, like `CMakeApp`, and `MakeApp`, now takes all arguments as keyword-only arguments.
|
|
87
|
+
|
|
88
|
+
### `find_apps()`
|
|
89
|
+
|
|
90
|
+
The `find_apps` function now takes only `paths` and `target` as normal arguments. All the rest of the arguments are keyword-only arguments.
|
|
91
|
+
|
|
92
|
+
### `build_apps()`
|
|
93
|
+
|
|
94
|
+
The `build_apps` function now takes only `apps` as normal argument. All the rest of the arguments are keyword-only arguments.
|
|
95
|
+
|
|
96
|
+
## Function Signature Changes
|
|
97
|
+
|
|
98
|
+
In 2.x, we change the signature of some functions to make them more intuitive and self-explanatory.
|
|
99
|
+
|
|
100
|
+
### `find_apps()`
|
|
101
|
+
|
|
102
|
+
- `build_log_path` is renamed to `build_log_filename`. The file will be generated under `build_dir` if specified.
|
|
103
|
+
- `size_json_path` is renamed to `size_json_filename`. The file will be generated under `build_dir` if specified.
|
|
104
|
+
|
|
105
|
+
## CLI Changes
|
|
106
|
+
|
|
107
|
+
In 2.x, we change the separator for some options to better differentiate them from `None` and empty list.
|
|
108
|
+
|
|
109
|
+
- `--modified-components`
|
|
110
|
+
- `--modified-files`
|
|
111
|
+
- `--ignore-app-dependencies-filepatterns`
|
|
112
|
+
|
|
113
|
+
Before:
|
|
114
|
+
|
|
115
|
+
```shell
|
|
116
|
+
idf-build-apps build -p foo -t esp32 --modified-components foo bar --modified-files foo bar --ignore-app-dependencies-filepatterns foo bar
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
After:
|
|
120
|
+
|
|
121
|
+
```shell
|
|
122
|
+
idf-build-apps build -p foo -t esp32 --modified-components 'foo;bar' --modified-files 'foo;bar' --ignore-app-dependencies-filepatterns 'foo;bar'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
passing `''` to specify it as `None`
|
|
126
|
+
|
|
127
|
+
```shell
|
|
128
|
+
idf-build-apps build -p foo -t esp32 --modified-components ''
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
or passing `';'` to specify it as an empty list
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
idf-build-apps build -p foo -t esp32 --modified-components ';'
|
|
135
|
+
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2022-
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
@@ -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.0.
|
|
11
|
+
__version__ = '2.0.0rc1'
|
|
12
12
|
|
|
13
13
|
from .session_args import (
|
|
14
14
|
SessionArgs,
|
|
@@ -28,6 +28,7 @@ from .log import (
|
|
|
28
28
|
from .main import (
|
|
29
29
|
build_apps,
|
|
30
30
|
find_apps,
|
|
31
|
+
json_to_app,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
__all__ = [
|
|
@@ -37,6 +38,6 @@ __all__ = [
|
|
|
37
38
|
'MakeApp',
|
|
38
39
|
'find_apps',
|
|
39
40
|
'build_apps',
|
|
41
|
+
'json_to_app',
|
|
40
42
|
'setup_logging',
|
|
41
|
-
'SESSION_ARGS',
|
|
42
43
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2022-
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import functools
|
|
@@ -8,11 +8,7 @@ import os
|
|
|
8
8
|
import re
|
|
9
9
|
import shutil
|
|
10
10
|
import sys
|
|
11
|
-
import tempfile
|
|
12
11
|
import typing as t
|
|
13
|
-
from copy import (
|
|
14
|
-
deepcopy,
|
|
15
|
-
)
|
|
16
12
|
from datetime import (
|
|
17
13
|
datetime,
|
|
18
14
|
)
|
|
@@ -28,10 +24,9 @@ from pydantic import (
|
|
|
28
24
|
computed_field,
|
|
29
25
|
)
|
|
30
26
|
|
|
31
|
-
from
|
|
27
|
+
from . import (
|
|
32
28
|
SESSION_ARGS,
|
|
33
29
|
)
|
|
34
|
-
|
|
35
30
|
from .build_apps_args import (
|
|
36
31
|
BuildAppsArgs,
|
|
37
32
|
)
|
|
@@ -132,12 +127,16 @@ class App(BaseModel):
|
|
|
132
127
|
|
|
133
128
|
# build status related
|
|
134
129
|
build_status: BuildStatus = BuildStatus.UNKNOWN
|
|
130
|
+
build_comment: t.Optional[str] = None
|
|
135
131
|
|
|
136
|
-
_build_comment: t.Optional[str] = None
|
|
137
132
|
_build_stage: t.Optional[BuildStage] = None
|
|
138
133
|
_build_duration: float = 0
|
|
139
134
|
_build_timestamp: t.Optional[datetime] = None
|
|
140
135
|
|
|
136
|
+
__EQ_IGNORE_FIELDS__ = [
|
|
137
|
+
'build_comment',
|
|
138
|
+
]
|
|
139
|
+
|
|
141
140
|
def __init__(
|
|
142
141
|
self,
|
|
143
142
|
app_dir: str,
|
|
@@ -164,6 +163,7 @@ class App(BaseModel):
|
|
|
164
163
|
|
|
165
164
|
self._build_log_filename = build_log_filename
|
|
166
165
|
self._size_json_filename = size_json_filename
|
|
166
|
+
self._is_build_log_path_temp = not bool(build_log_filename)
|
|
167
167
|
|
|
168
168
|
# pass all parameters to initialize hook method
|
|
169
169
|
kwargs.update(
|
|
@@ -297,25 +297,18 @@ class App(BaseModel):
|
|
|
297
297
|
|
|
298
298
|
return os.path.join(self.work_dir, self.build_dir)
|
|
299
299
|
|
|
300
|
-
@property
|
|
301
|
-
def build_comment(self) -> str:
|
|
302
|
-
return self._build_comment or ''
|
|
303
|
-
|
|
304
|
-
@build_comment.setter
|
|
305
|
-
def build_comment(self, value: str) -> None:
|
|
306
|
-
self._build_comment = value
|
|
307
|
-
|
|
308
300
|
@computed_field # type: ignore
|
|
309
301
|
@property
|
|
310
302
|
def build_log_filename(self) -> t.Optional[str]:
|
|
311
303
|
return self._expand(self._build_log_filename)
|
|
312
304
|
|
|
313
305
|
@property
|
|
314
|
-
def build_log_path(self) ->
|
|
306
|
+
def build_log_path(self) -> str:
|
|
315
307
|
if self.build_log_filename:
|
|
316
308
|
return os.path.join(self.build_path, self.build_log_filename)
|
|
317
309
|
|
|
318
|
-
|
|
310
|
+
# use a temp file if build log path is not specified
|
|
311
|
+
return os.path.join(self.build_path, f'.temp.build.{hash(self)}.log')
|
|
319
312
|
|
|
320
313
|
@computed_field # type: ignore
|
|
321
314
|
@property
|
|
@@ -340,17 +333,21 @@ class App(BaseModel):
|
|
|
340
333
|
real_sdkconfig_files: t.List[str] = []
|
|
341
334
|
sdkconfig_files_defined_target: t.Optional[str] = None
|
|
342
335
|
|
|
336
|
+
# put the expanded variable files in a temporary directory
|
|
337
|
+
# will remove if the content is the same as the original one
|
|
343
338
|
expanded_dir = os.path.join(self.work_dir, 'expanded_sdkconfig_files', os.path.basename(self.build_dir))
|
|
344
339
|
if not os.path.isdir(expanded_dir):
|
|
345
340
|
os.makedirs(expanded_dir)
|
|
346
341
|
|
|
347
342
|
for f in self.sdkconfig_defaults_candidates + ([self.sdkconfig_path] if self.sdkconfig_path else []):
|
|
348
|
-
if
|
|
349
|
-
f = os.path.join(self.work_dir, f)
|
|
350
|
-
|
|
343
|
+
# use filepath if abs/rel already point to itself
|
|
351
344
|
if not os.path.isfile(f):
|
|
352
|
-
|
|
353
|
-
|
|
345
|
+
# find it in the app_dir
|
|
346
|
+
self._logger.debug('sdkconfig file %s not found, checking under app_dir...', f)
|
|
347
|
+
f = os.path.join(self.app_dir, f)
|
|
348
|
+
if not os.path.isfile(f):
|
|
349
|
+
self._logger.debug('sdkconfig file %s not found, skipping...', f)
|
|
350
|
+
continue
|
|
354
351
|
|
|
355
352
|
expanded_fp = os.path.join(expanded_dir, os.path.basename(f))
|
|
356
353
|
with open(f) as fr:
|
|
@@ -469,14 +466,7 @@ class App(BaseModel):
|
|
|
469
466
|
|
|
470
467
|
return wrapper
|
|
471
468
|
|
|
472
|
-
|
|
473
|
-
def build(
|
|
474
|
-
self,
|
|
475
|
-
manifest_rootpath: t.Optional[str] = None,
|
|
476
|
-
modified_components: t.Union[t.List[str], str, None] = None,
|
|
477
|
-
modified_files: t.Union[t.List[str], str, None] = None,
|
|
478
|
-
check_app_dependencies: bool = False,
|
|
479
|
-
) -> None:
|
|
469
|
+
def _pre_build(self) -> None:
|
|
480
470
|
if self.dry_run:
|
|
481
471
|
self._build_stage = BuildStage.DRY_RUN
|
|
482
472
|
else:
|
|
@@ -518,26 +508,32 @@ class App(BaseModel):
|
|
|
518
508
|
if not self.dry_run:
|
|
519
509
|
os.unlink(sdkconfig_file)
|
|
520
510
|
|
|
521
|
-
if self.build_log_path:
|
|
522
|
-
self._logger.
|
|
511
|
+
if os.path.isfile(self.build_log_path):
|
|
512
|
+
self._logger.debug('Removed existing build log file: %s', self.build_log_path)
|
|
513
|
+
if not self.dry_run:
|
|
514
|
+
os.unlink(self.build_log_path)
|
|
515
|
+
elif not self.dry_run:
|
|
516
|
+
os.makedirs(os.path.dirname(self.build_log_path), exist_ok=True)
|
|
517
|
+
self._logger.info('Writing build log to %s', self.build_log_path)
|
|
523
518
|
|
|
524
519
|
if self.dry_run:
|
|
525
520
|
self.build_status = BuildStatus.SKIPPED
|
|
526
521
|
self.build_comment = 'dry run'
|
|
527
522
|
return
|
|
528
523
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
524
|
+
@record_build_duration # type: ignore
|
|
525
|
+
def build(
|
|
526
|
+
self,
|
|
527
|
+
*,
|
|
528
|
+
manifest_rootpath: t.Optional[str] = None,
|
|
529
|
+
modified_components: t.Union[t.List[str], str, None] = None,
|
|
530
|
+
modified_files: t.Union[t.List[str], str, None] = None,
|
|
531
|
+
check_app_dependencies: bool = False,
|
|
532
|
+
) -> None:
|
|
533
|
+
self._pre_build()
|
|
536
534
|
|
|
537
|
-
self._build_stage = BuildStage.BUILD
|
|
538
535
|
try:
|
|
539
536
|
self._build(
|
|
540
|
-
logfile=logfile,
|
|
541
537
|
manifest_rootpath=manifest_rootpath,
|
|
542
538
|
modified_components=to_list(modified_components),
|
|
543
539
|
modified_files=to_list(modified_files),
|
|
@@ -546,12 +542,17 @@ class App(BaseModel):
|
|
|
546
542
|
except BuildError as e:
|
|
547
543
|
self.build_status = BuildStatus.FAILED
|
|
548
544
|
self.build_comment = str(e)
|
|
549
|
-
finally:
|
|
550
|
-
logfile.close()
|
|
551
545
|
|
|
546
|
+
self._post_build()
|
|
547
|
+
|
|
548
|
+
def _post_build(self) -> None:
|
|
552
549
|
self._build_stage = BuildStage.POST_BUILD
|
|
550
|
+
|
|
551
|
+
if not os.path.isfile(self.build_log_path):
|
|
552
|
+
return
|
|
553
|
+
|
|
553
554
|
has_unignored_warning = False
|
|
554
|
-
with open(
|
|
555
|
+
with open(self.build_log_path) as fr:
|
|
555
556
|
lines = [line.rstrip() for line in fr.readlines() if line.rstrip()]
|
|
556
557
|
for line in lines:
|
|
557
558
|
is_error_or_warning, ignored = self.is_error_or_warning(line)
|
|
@@ -567,15 +568,14 @@ class App(BaseModel):
|
|
|
567
568
|
self._logger.error(
|
|
568
569
|
'Last %s lines from the build log "%s":',
|
|
569
570
|
self.LOG_DEBUG_LINES,
|
|
570
|
-
|
|
571
|
+
self.build_log_path,
|
|
571
572
|
)
|
|
572
573
|
for line in lines[-self.LOG_DEBUG_LINES :]:
|
|
573
574
|
self._logger.error('%s', line)
|
|
574
575
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
self._logger.debug('Removed temporary build log file: %s', logfile.name)
|
|
576
|
+
if self._is_build_log_path_temp and self.build_status == BuildStatus.SUCCESS:
|
|
577
|
+
os.unlink(self.build_log_path)
|
|
578
|
+
self._logger.debug('Removed success build temporary log file: %s', self.build_log_path)
|
|
579
579
|
|
|
580
580
|
# Generate Size Files
|
|
581
581
|
if self.build_status == BuildStatus.SUCCESS:
|
|
@@ -586,8 +586,7 @@ class App(BaseModel):
|
|
|
586
586
|
exclude_list = []
|
|
587
587
|
if self.size_json_path:
|
|
588
588
|
exclude_list.append(os.path.basename(self.size_json_path))
|
|
589
|
-
|
|
590
|
-
exclude_list.append(os.path.basename(self.build_log_path))
|
|
589
|
+
exclude_list.append(os.path.basename(self.build_log_path))
|
|
591
590
|
|
|
592
591
|
rmdir(
|
|
593
592
|
self.build_path,
|
|
@@ -604,13 +603,13 @@ class App(BaseModel):
|
|
|
604
603
|
|
|
605
604
|
def _build(
|
|
606
605
|
self,
|
|
607
|
-
|
|
606
|
+
*,
|
|
608
607
|
manifest_rootpath: t.Optional[str] = None,
|
|
609
608
|
modified_components: t.Optional[t.List[str]] = None,
|
|
610
609
|
modified_files: t.Optional[t.List[str]] = None,
|
|
611
610
|
check_app_dependencies: bool = False,
|
|
612
611
|
) -> None:
|
|
613
|
-
|
|
612
|
+
self._build_stage = BuildStage.BUILD
|
|
614
613
|
|
|
615
614
|
def _write_size_json(self) -> None:
|
|
616
615
|
if not self.size_json_path:
|
|
@@ -779,12 +778,19 @@ class MakeApp(App):
|
|
|
779
778
|
|
|
780
779
|
def _build(
|
|
781
780
|
self,
|
|
782
|
-
|
|
781
|
+
*,
|
|
783
782
|
manifest_rootpath: t.Optional[str] = None,
|
|
784
783
|
modified_components: t.Optional[t.List[str]] = None,
|
|
785
784
|
modified_files: t.Optional[t.List[str]] = None,
|
|
786
785
|
check_app_dependencies: bool = False,
|
|
787
786
|
) -> None:
|
|
787
|
+
super()._build(
|
|
788
|
+
manifest_rootpath=manifest_rootpath,
|
|
789
|
+
modified_components=modified_components,
|
|
790
|
+
modified_files=modified_files,
|
|
791
|
+
check_app_dependencies=check_app_dependencies,
|
|
792
|
+
)
|
|
793
|
+
|
|
788
794
|
# additional env variables
|
|
789
795
|
additional_env_dict = {
|
|
790
796
|
'IDF_TARGET': self.target,
|
|
@@ -801,8 +807,8 @@ class MakeApp(App):
|
|
|
801
807
|
for cmd in commands:
|
|
802
808
|
subprocess_run(
|
|
803
809
|
cmd,
|
|
804
|
-
log_terminal=
|
|
805
|
-
log_fs=
|
|
810
|
+
log_terminal=self._is_build_log_path_temp,
|
|
811
|
+
log_fs=self.build_log_path,
|
|
806
812
|
check=True,
|
|
807
813
|
additional_env_dict=additional_env_dict,
|
|
808
814
|
cwd=self.work_dir,
|
|
@@ -846,12 +852,19 @@ class CMakeApp(App):
|
|
|
846
852
|
|
|
847
853
|
def _build(
|
|
848
854
|
self,
|
|
849
|
-
|
|
855
|
+
*,
|
|
850
856
|
manifest_rootpath: t.Optional[str] = None,
|
|
851
857
|
modified_components: t.Optional[t.List[str]] = None,
|
|
852
858
|
modified_files: t.Optional[t.List[str]] = None,
|
|
853
859
|
check_app_dependencies: bool = False,
|
|
854
860
|
) -> None:
|
|
861
|
+
super()._build(
|
|
862
|
+
manifest_rootpath=manifest_rootpath,
|
|
863
|
+
modified_components=modified_components,
|
|
864
|
+
modified_files=modified_files,
|
|
865
|
+
check_app_dependencies=check_app_dependencies,
|
|
866
|
+
)
|
|
867
|
+
|
|
855
868
|
if not self._checked_should_build:
|
|
856
869
|
self._check_should_build(
|
|
857
870
|
manifest_rootpath=manifest_rootpath,
|
|
@@ -883,8 +896,8 @@ class CMakeApp(App):
|
|
|
883
896
|
if modified_components is not None and check_app_dependencies and self.build_status == BuildStatus.UNKNOWN:
|
|
884
897
|
subprocess_run(
|
|
885
898
|
common_args + ['reconfigure'],
|
|
886
|
-
log_terminal=
|
|
887
|
-
log_fs=
|
|
899
|
+
log_terminal=self._is_build_log_path_temp,
|
|
900
|
+
log_fs=self.build_log_path,
|
|
888
901
|
check=True,
|
|
889
902
|
additional_env_dict=additional_env_dict,
|
|
890
903
|
)
|
|
@@ -905,23 +918,22 @@ class CMakeApp(App):
|
|
|
905
918
|
return
|
|
906
919
|
|
|
907
920
|
# idf.py build
|
|
908
|
-
build_args = deepcopy(common_args)
|
|
909
921
|
if self.cmake_vars:
|
|
910
922
|
for key, val in self.cmake_vars.items():
|
|
911
|
-
|
|
923
|
+
common_args.append(f'-D{key}={val}')
|
|
912
924
|
if 'TEST_EXCLUDE_COMPONENTS' in self.cmake_vars and 'TEST_COMPONENTS' not in self.cmake_vars:
|
|
913
|
-
|
|
925
|
+
common_args.append('-DTESTS_ALL=1')
|
|
914
926
|
if 'CONFIG_APP_BUILD_BOOTLOADER' in self.cmake_vars:
|
|
915
927
|
# In case if secure_boot is enabled then for bootloader build need to add `bootloader` cmd
|
|
916
|
-
|
|
917
|
-
|
|
928
|
+
common_args.append('bootloader')
|
|
929
|
+
common_args.append('build')
|
|
918
930
|
if self.verbose:
|
|
919
|
-
|
|
931
|
+
common_args.append('-v')
|
|
920
932
|
|
|
921
933
|
subprocess_run(
|
|
922
|
-
|
|
923
|
-
log_terminal=
|
|
924
|
-
log_fs=
|
|
934
|
+
common_args,
|
|
935
|
+
log_terminal=self._is_build_log_path_temp,
|
|
936
|
+
log_fs=self.build_log_path,
|
|
925
937
|
check=True,
|
|
926
938
|
additional_env_dict=additional_env_dict,
|
|
927
939
|
)
|
|
@@ -24,9 +24,9 @@ if _BUILDING_DOCS:
|
|
|
24
24
|
if _BUILDING_DOCS:
|
|
25
25
|
_idf_env = tempfile.gettempdir()
|
|
26
26
|
else:
|
|
27
|
-
_idf_env = os.getenv('IDF_PATH'
|
|
28
|
-
if not
|
|
29
|
-
|
|
27
|
+
_idf_env = os.getenv('IDF_PATH') or ''
|
|
28
|
+
if not _idf_env:
|
|
29
|
+
raise SystemExit('environment variable IDF_PATH must be set')
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
IDF_PATH = os.path.abspath(_idf_env)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2022-
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
@@ -11,11 +11,17 @@ import sys
|
|
|
11
11
|
import textwrap
|
|
12
12
|
import typing as t
|
|
13
13
|
|
|
14
|
+
from pydantic import (
|
|
15
|
+
Field,
|
|
16
|
+
create_model,
|
|
17
|
+
)
|
|
18
|
+
|
|
14
19
|
from . import (
|
|
15
20
|
SESSION_ARGS,
|
|
16
21
|
)
|
|
17
22
|
from .app import (
|
|
18
23
|
App,
|
|
24
|
+
AppDeserializer,
|
|
19
25
|
CMakeApp,
|
|
20
26
|
MakeApp,
|
|
21
27
|
)
|
|
@@ -293,7 +299,6 @@ def build_apps(
|
|
|
293
299
|
if f and os.path.isfile(f):
|
|
294
300
|
os.remove(f)
|
|
295
301
|
LOGGER.debug('Remove existing collect file %s', f)
|
|
296
|
-
os.mknod(f)
|
|
297
302
|
|
|
298
303
|
exit_code = 0
|
|
299
304
|
for i, app in enumerate(apps):
|
|
@@ -742,7 +747,7 @@ def main():
|
|
|
742
747
|
os.makedirs(os.path.dirname(os.path.realpath(args.output)), exist_ok=True)
|
|
743
748
|
with open(args.output, 'w') as fw:
|
|
744
749
|
for app in apps:
|
|
745
|
-
fw.write(app.
|
|
750
|
+
fw.write(app.to_json() + '\n')
|
|
746
751
|
else:
|
|
747
752
|
for app in apps:
|
|
748
753
|
print(app)
|
|
@@ -791,3 +796,37 @@ def main():
|
|
|
791
796
|
print(f' {app}')
|
|
792
797
|
|
|
793
798
|
sys.exit(res)
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def json_to_app(json_str: str, extra_classes: t.Optional[t.List[t.Type[App]]] = None) -> App:
|
|
802
|
+
"""
|
|
803
|
+
Deserialize json string to App object
|
|
804
|
+
|
|
805
|
+
.. note::
|
|
806
|
+
|
|
807
|
+
You can pass extra_cls to support custom App class. A custom App class must be a subclass of App, and have a
|
|
808
|
+
different value of `build_system`. For example, a custom CMake app
|
|
809
|
+
|
|
810
|
+
>>> class CustomApp(CMakeApp):
|
|
811
|
+
>>> build_system: Literal['custom_cmake'] = 'custom_cmake'
|
|
812
|
+
|
|
813
|
+
Then you can pass the CustomApp class to the `extra_cls` argument
|
|
814
|
+
|
|
815
|
+
>>> json_str = CustomApp('.', 'esp32').to_json()
|
|
816
|
+
>>> json_to_app(json_str, extra_classes=[CustomApp])
|
|
817
|
+
|
|
818
|
+
:param json_str: json string
|
|
819
|
+
:param extra_classes: extra App class
|
|
820
|
+
:return: App object
|
|
821
|
+
"""
|
|
822
|
+
types = [App, CMakeApp, MakeApp]
|
|
823
|
+
if extra_classes:
|
|
824
|
+
types.extend(extra_classes)
|
|
825
|
+
|
|
826
|
+
custom_deserializer = create_model(
|
|
827
|
+
'_CustomDeserializer',
|
|
828
|
+
app=(t.Union[tuple(types)], Field(discriminator='build_system')),
|
|
829
|
+
__base__=AppDeserializer,
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
return custom_deserializer.from_json(json_str)
|
|
@@ -58,15 +58,17 @@ class FolderRule:
|
|
|
58
58
|
) -> None:
|
|
59
59
|
self.folder = os.path.abspath(folder)
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
self.
|
|
61
|
+
def _clause_to_if_clause(clause: t.Dict[str, t.Any]) -> IfClause:
|
|
62
|
+
_kwargs = {'stmt': clause['if']}
|
|
63
|
+
if 'temporary' in clause:
|
|
64
|
+
_kwargs['temporary'] = clause['temporary']
|
|
65
|
+
if 'reason' in clause:
|
|
66
|
+
_kwargs['reason'] = clause['reason']
|
|
67
|
+
return IfClause(**_kwargs)
|
|
68
|
+
|
|
69
|
+
self.enable = [_clause_to_if_clause(clause) for clause in enable] if enable else []
|
|
70
|
+
self.disable = [_clause_to_if_clause(clause) for clause in disable] if disable else []
|
|
71
|
+
self.disable_test = [_clause_to_if_clause(clause) for clause in disable_test] if disable_test else []
|
|
70
72
|
self.depends_components = depends_components or []
|
|
71
73
|
self.depends_filepatterns = depends_filepatterns or []
|
|
72
74
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
+
import logging
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
6
7
|
import typing as t
|
|
7
8
|
|
|
9
|
+
LOGGER = logging.getLogger(__name__)
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
class SessionArgs:
|
|
10
13
|
workdir: str = os.getcwd()
|
|
@@ -35,27 +38,32 @@ class SessionArgs:
|
|
|
35
38
|
self.override_sdkconfig_file_path = override_sdkconfig_merged_file
|
|
36
39
|
|
|
37
40
|
def _get_override_sdkconfig_files_items(self, override_sdkconfig_files: t.Tuple[str]) -> t.Dict:
|
|
38
|
-
|
|
41
|
+
d = {}
|
|
39
42
|
for f in override_sdkconfig_files:
|
|
40
|
-
if
|
|
41
|
-
f = os.path.join(self.workdir, f)
|
|
43
|
+
# use filepath if abs/rel already point to itself
|
|
42
44
|
if not os.path.isfile(f):
|
|
43
|
-
|
|
45
|
+
# find it in the workdir
|
|
46
|
+
LOGGER.debug('override sdkconfig file %s not found, checking under app_dir...', f)
|
|
47
|
+
f = os.path.join(self.workdir, f)
|
|
48
|
+
if not os.path.isfile(f):
|
|
49
|
+
LOGGER.debug('override sdkconfig file %s not found, skipping...', f)
|
|
50
|
+
continue
|
|
51
|
+
|
|
44
52
|
with open(f) as fr:
|
|
45
53
|
for line in fr:
|
|
46
54
|
m = re.compile(r"^([^=]+)=\"?([^\"\n]*)\"?\n*$").match(line)
|
|
47
55
|
if not m:
|
|
48
56
|
continue
|
|
49
|
-
|
|
50
|
-
return
|
|
57
|
+
d[m.group(1)] = m.group(2)
|
|
58
|
+
return d
|
|
51
59
|
|
|
52
60
|
def _get_override_sdkconfig_items(self, override_sdkconfig_items: t.Tuple[str]) -> t.Dict:
|
|
53
|
-
|
|
61
|
+
d = {}
|
|
54
62
|
for line in override_sdkconfig_items:
|
|
55
63
|
m = re.compile(r"^([^=]+)=\"?([^\"\n]*)\"?\n*$").match(line)
|
|
56
64
|
if m:
|
|
57
|
-
|
|
58
|
-
return
|
|
65
|
+
d[m.group(1)] = m.group(2)
|
|
66
|
+
return d
|
|
59
67
|
|
|
60
68
|
def _create_override_sdkconfig_merged_file(self, override_sdkconfig_merged_items) -> t.Optional[str]:
|
|
61
69
|
if not override_sdkconfig_merged_items:
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2022-
|
|
1
|
+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import fnmatch
|
|
5
|
+
import functools
|
|
5
6
|
import glob
|
|
6
7
|
import logging
|
|
7
8
|
import os
|
|
@@ -134,7 +135,7 @@ def find_first_match(pattern: str, path: str) -> t.Optional[str]:
|
|
|
134
135
|
def subprocess_run(
|
|
135
136
|
cmd: t.List[str],
|
|
136
137
|
log_terminal: bool = True,
|
|
137
|
-
log_fs: t.
|
|
138
|
+
log_fs: t.Union[t.IO[str], str, None] = None,
|
|
138
139
|
check: bool = False,
|
|
139
140
|
additional_env_dict: t.Optional[t.Dict[str, str]] = None,
|
|
140
141
|
**kwargs,
|
|
@@ -157,16 +158,26 @@ def subprocess_run(
|
|
|
157
158
|
subprocess_env.update(additional_env_dict)
|
|
158
159
|
|
|
159
160
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=subprocess_env, **kwargs)
|
|
160
|
-
if p.stdout:
|
|
161
|
-
for line in p.stdout:
|
|
162
|
-
if isinstance(line, bytes):
|
|
163
|
-
line = line.decode('utf-8')
|
|
164
161
|
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
def _log_stdout(fs: t.Optional[t.IO[str]] = None):
|
|
163
|
+
if p.stdout:
|
|
164
|
+
for line in p.stdout:
|
|
165
|
+
if isinstance(line, bytes):
|
|
166
|
+
line = line.decode('utf-8')
|
|
167
|
+
|
|
168
|
+
if log_terminal:
|
|
169
|
+
sys.stdout.write(line)
|
|
170
|
+
|
|
171
|
+
if fs:
|
|
172
|
+
fs.write(line)
|
|
167
173
|
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
if p.stdout:
|
|
175
|
+
if log_fs:
|
|
176
|
+
if isinstance(log_fs, str):
|
|
177
|
+
with open(log_fs, 'a') as fa:
|
|
178
|
+
_log_stdout(fa)
|
|
179
|
+
else:
|
|
180
|
+
_log_stdout(log_fs)
|
|
170
181
|
|
|
171
182
|
returncode = p.wait()
|
|
172
183
|
if check and returncode != 0:
|
|
@@ -305,25 +316,43 @@ def files_matches_patterns(
|
|
|
305
316
|
return False
|
|
306
317
|
|
|
307
318
|
|
|
319
|
+
@functools.total_ordering
|
|
308
320
|
class BaseModel(_BaseModel):
|
|
309
321
|
"""
|
|
310
322
|
BaseModel that is hashable
|
|
311
323
|
"""
|
|
312
324
|
|
|
325
|
+
__EQ_IGNORE_FIELDS__: t.List[str] = []
|
|
326
|
+
|
|
313
327
|
def __lt__(self, other: t.Any) -> bool:
|
|
314
328
|
if isinstance(other, self.__class__):
|
|
315
329
|
for k in self.model_dump():
|
|
316
|
-
if
|
|
317
|
-
return getattr(self, k) < getattr(other, k)
|
|
318
|
-
else:
|
|
330
|
+
if k in self.__EQ_IGNORE_FIELDS__:
|
|
319
331
|
continue
|
|
320
332
|
|
|
333
|
+
self_attr = getattr(self, k, '') or ''
|
|
334
|
+
other_attr = getattr(other, k, '') or ''
|
|
335
|
+
|
|
336
|
+
if self_attr != other_attr:
|
|
337
|
+
return self_attr < other_attr
|
|
338
|
+
|
|
339
|
+
continue
|
|
340
|
+
|
|
341
|
+
return False
|
|
342
|
+
|
|
321
343
|
return NotImplemented
|
|
322
344
|
|
|
323
345
|
def __eq__(self, other: t.Any) -> bool:
|
|
324
346
|
if isinstance(other, self.__class__):
|
|
325
347
|
# we only care the public attributes
|
|
326
|
-
|
|
348
|
+
self_model_dump = self.model_dump()
|
|
349
|
+
other_model_dump = other.model_dump()
|
|
350
|
+
|
|
351
|
+
for _field in self.__EQ_IGNORE_FIELDS__:
|
|
352
|
+
self_model_dump.pop(_field, None)
|
|
353
|
+
other_model_dump.pop(_field, None)
|
|
354
|
+
|
|
355
|
+
return self_model_dump == other_model_dump
|
|
327
356
|
|
|
328
357
|
return NotImplemented
|
|
329
358
|
|
|
@@ -27,7 +27,7 @@ entry_points = \
|
|
|
27
27
|
{'console_scripts': ['idf-build-apps = idf_build_apps:main.main']}
|
|
28
28
|
|
|
29
29
|
setup(name='idf-build-apps',
|
|
30
|
-
version='2.0.
|
|
30
|
+
version='2.0.0rc1',
|
|
31
31
|
description='Tools for building ESP-IDF related apps.',
|
|
32
32
|
author=None,
|
|
33
33
|
author_email='Fu Hanxi <fuhanxi@espressif.com>',
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from pydantic import (
|
|
8
|
+
ValidationError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from idf_build_apps import (
|
|
12
|
+
AppDeserializer,
|
|
13
|
+
CMakeApp,
|
|
14
|
+
MakeApp,
|
|
15
|
+
)
|
|
16
|
+
from idf_build_apps.main import (
|
|
17
|
+
json_to_app,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
if sys.version_info < (3, 8):
|
|
21
|
+
from typing_extensions import (
|
|
22
|
+
Literal,
|
|
23
|
+
)
|
|
24
|
+
else:
|
|
25
|
+
from typing import (
|
|
26
|
+
Literal,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_serialization():
|
|
31
|
+
a = CMakeApp('foo', 'bar')
|
|
32
|
+
a_s = a.to_json()
|
|
33
|
+
|
|
34
|
+
b = CMakeApp.model_validate_json(a_s)
|
|
35
|
+
assert a == b
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_deserialization(tmp_path):
|
|
39
|
+
a = CMakeApp('foo', 'bar', size_json_filename='size.json')
|
|
40
|
+
b = MakeApp('foo', 'bar', build_log_filename='build.log')
|
|
41
|
+
|
|
42
|
+
assert a != b
|
|
43
|
+
|
|
44
|
+
with open(tmp_path / 'test.txt', 'w') as fw:
|
|
45
|
+
fw.write(a.to_json() + '\n')
|
|
46
|
+
fw.write(b.to_json() + '\n')
|
|
47
|
+
|
|
48
|
+
with open(tmp_path / 'test.txt') as fr:
|
|
49
|
+
a_s = AppDeserializer.from_json(fr.readline())
|
|
50
|
+
b_s = AppDeserializer.from_json(fr.readline())
|
|
51
|
+
|
|
52
|
+
assert a == a_s
|
|
53
|
+
assert b == b_s
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_app_sorting():
|
|
57
|
+
a = CMakeApp('foo', 'esp32')
|
|
58
|
+
b = MakeApp('foo', 'esp32')
|
|
59
|
+
|
|
60
|
+
c = CMakeApp('foo', 'esp32', size_json_filename='size.json')
|
|
61
|
+
d = CMakeApp('foo', 'esp32s2')
|
|
62
|
+
e = CMakeApp('foo', 'esp32s2', build_comment='build_comment')
|
|
63
|
+
|
|
64
|
+
with pytest.raises(TypeError, match="'<' not supported between instances of 'CMakeApp' and 'MakeApp'"):
|
|
65
|
+
assert a < b
|
|
66
|
+
|
|
67
|
+
assert a < c < d
|
|
68
|
+
assert d > c > a
|
|
69
|
+
|
|
70
|
+
# __EQ_IGNORE_FIELDS__
|
|
71
|
+
assert d == e
|
|
72
|
+
assert not d < e
|
|
73
|
+
assert not d > e
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_app_deserializer():
|
|
77
|
+
a = CMakeApp('foo', 'esp32')
|
|
78
|
+
b = MakeApp('foo', 'esp32')
|
|
79
|
+
|
|
80
|
+
class CustomApp(CMakeApp):
|
|
81
|
+
build_system: Literal['custom'] = 'custom' # type: ignore
|
|
82
|
+
|
|
83
|
+
c = CustomApp('foo', 'esp32')
|
|
84
|
+
|
|
85
|
+
assert json_to_app(a.to_json()) == a
|
|
86
|
+
assert json_to_app(b.to_json()) == b
|
|
87
|
+
|
|
88
|
+
with pytest.raises(
|
|
89
|
+
ValidationError,
|
|
90
|
+
match="Input tag 'custom' found using 'build_system' does not match "
|
|
91
|
+
"any of the expected tags: 'unknown', 'cmake', 'make'",
|
|
92
|
+
):
|
|
93
|
+
assert json_to_app(c.to_json()) == c
|
|
94
|
+
|
|
95
|
+
assert json_to_app(c.to_json(), extra_classes=[CustomApp]) == c
|
|
@@ -21,7 +21,7 @@ from idf_build_apps.utils import (
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def test_manifest(tmpdir, recwarn):
|
|
24
|
+
def test_manifest(tmpdir, recwarn, monkeypatch):
|
|
25
25
|
yaml_file = tmpdir / 'test.yml'
|
|
26
26
|
yaml_file.write_text(
|
|
27
27
|
"""
|
|
@@ -54,11 +54,38 @@ test2:
|
|
|
54
54
|
assert manifest.enable_build_targets('test2') == ['linux']
|
|
55
55
|
assert manifest.enable_test_targets('test2') == ['linux']
|
|
56
56
|
|
|
57
|
-
Manifest
|
|
57
|
+
monkeypatch.setattr(idf_build_apps.manifest.manifest.Manifest, 'CHECK_MANIFEST_RULES', True)
|
|
58
58
|
with pytest.raises(InvalidManifest, match=msg_fmt.format(os.path.join(tmpdir, 'test1'), yaml_file)):
|
|
59
59
|
Manifest.from_file(yaml_file)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def test_manifest_with_anchor(tmpdir, monkeypatch):
|
|
63
|
+
yaml_file = tmpdir / 'test.yml'
|
|
64
|
+
yaml_file.write_text(
|
|
65
|
+
"""
|
|
66
|
+
.base: &base
|
|
67
|
+
depends_components:
|
|
68
|
+
- a
|
|
69
|
+
|
|
70
|
+
foo: &foo
|
|
71
|
+
<<: *base
|
|
72
|
+
disable:
|
|
73
|
+
- if: IDF_TARGET == "esp32"
|
|
74
|
+
|
|
75
|
+
bar:
|
|
76
|
+
<<: *foo
|
|
77
|
+
""",
|
|
78
|
+
encoding='utf8',
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
monkeypatch.setattr(idf_build_apps.manifest.manifest.FolderRule, 'DEFAULT_BUILD_TARGETS', ['esp32'])
|
|
82
|
+
|
|
83
|
+
with pytest.warns(UserWarning, match='Folder ".+" does not exist. Please check your manifest file'):
|
|
84
|
+
manifest = Manifest.from_file(yaml_file)
|
|
85
|
+
|
|
86
|
+
assert manifest.enable_build_targets('bar') == []
|
|
87
|
+
|
|
88
|
+
|
|
62
89
|
class TestIfParser:
|
|
63
90
|
def test_idf_version(self, monkeypatch):
|
|
64
91
|
monkeypatch.setattr(idf_build_apps.manifest.if_parser, 'IDF_VERSION', Version('5.9.0'))
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
from idf_build_apps import (
|
|
5
|
-
AppDeserializer,
|
|
6
|
-
CMakeApp,
|
|
7
|
-
MakeApp,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def test_serialization():
|
|
12
|
-
a = CMakeApp('foo', 'bar')
|
|
13
|
-
a_s = a.to_json()
|
|
14
|
-
|
|
15
|
-
b = CMakeApp.model_validate_json(a_s)
|
|
16
|
-
assert a == b
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_deserialization(tmp_path):
|
|
20
|
-
a = CMakeApp('foo', 'bar', size_json_filename='size.json')
|
|
21
|
-
b = MakeApp('foo', 'bar', build_log_filename='build.log')
|
|
22
|
-
|
|
23
|
-
assert a != b
|
|
24
|
-
|
|
25
|
-
with open(tmp_path / 'test.txt', 'w') as fw:
|
|
26
|
-
fw.write(a.to_json() + '\n')
|
|
27
|
-
fw.write(b.to_json() + '\n')
|
|
28
|
-
|
|
29
|
-
with open(tmp_path / 'test.txt') as fr:
|
|
30
|
-
a_s = AppDeserializer.from_json(fr.readline())
|
|
31
|
-
b_s = AppDeserializer.from_json(fr.readline())
|
|
32
|
-
|
|
33
|
-
assert a == a_s
|
|
34
|
-
assert b == b_s
|
|
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
|
|
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
|