Skip to content

Commit 1cecdab

Browse files
committed
init commit just moved from idf-build-apps
0 parents  commit 1cecdab

File tree

6 files changed

+683
-0
lines changed

6 files changed

+683
-0
lines changed

esp_bool_parser/__init__.py

Whitespace-only changes.

esp_bool_parser/constants.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import enum
5+
import importlib
6+
import logging
7+
import os
8+
import re
9+
import sys
10+
import typing as t
11+
12+
from .utils import (
13+
to_version,
14+
)
15+
16+
LOGGER = logging.getLogger(__name__)
17+
18+
_idf_env = os.getenv('IDF_PATH') or ''
19+
if not _idf_env:
20+
LOGGER.warning('IDF_PATH environment variable is not set. Entering test mode...')
21+
LOGGER.warning('- Setting IDF_PATH to current directory...')
22+
IDF_PATH = os.path.abspath(_idf_env)
23+
IDF_PY = os.path.join(IDF_PATH, 'tools', 'idf.py')
24+
IDF_SIZE_PY = os.path.join(IDF_PATH, 'tools', 'idf_size.py')
25+
PROJECT_DESCRIPTION_JSON = 'project_description.json'
26+
DEFAULT_SDKCONFIG = 'sdkconfig.defaults'
27+
28+
29+
_idf_py_actions = os.path.join(IDF_PATH, 'tools', 'idf_py_actions')
30+
sys.path.append(_idf_py_actions)
31+
try:
32+
_idf_py_constant_py = importlib.import_module('constants')
33+
except ModuleNotFoundError:
34+
LOGGER.warning(
35+
'- Set supported/preview targets to empty list... (ESP-IDF constants.py module not found under %s)',
36+
_idf_py_actions,
37+
)
38+
_idf_py_constant_py = object() # type: ignore
39+
SUPPORTED_TARGETS = getattr(_idf_py_constant_py, 'SUPPORTED_TARGETS', [])
40+
PREVIEW_TARGETS = getattr(_idf_py_constant_py, 'PREVIEW_TARGETS', [])
41+
ALL_TARGETS = SUPPORTED_TARGETS + PREVIEW_TARGETS
42+
43+
44+
def _idf_version_from_cmake() -> t.Tuple[int, int, int]:
45+
version_path = os.path.join(IDF_PATH, 'tools', 'cmake', 'version.cmake')
46+
if not os.path.isfile(version_path):
47+
LOGGER.warning('- Setting ESP-IDF version to 1.0.0... (ESP-IDF version.cmake not exists at %s)', version_path)
48+
return 1, 0, 0
49+
50+
regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)')
51+
ver = {}
52+
try:
53+
with open(version_path) as f:
54+
for line in f:
55+
m = regex.match(line)
56+
57+
if m:
58+
ver[m.group(1)] = m.group(2)
59+
60+
return int(ver['MAJOR']), int(ver['MINOR']), int(ver['PATCH'])
61+
except (KeyError, OSError):
62+
raise ValueError(f'Cannot find ESP-IDF version in {version_path}')
63+
64+
65+
IDF_VERSION_MAJOR, IDF_VERSION_MINOR, IDF_VERSION_PATCH = _idf_version_from_cmake()
66+
67+
IDF_VERSION = to_version(f'{IDF_VERSION_MAJOR}.{IDF_VERSION_MINOR}.{IDF_VERSION_PATCH}')
68+
69+
70+
class BuildStatus(str, enum.Enum):
71+
UNKNOWN = 'unknown'
72+
DISABLED = 'disabled'
73+
SKIPPED = 'skipped'
74+
SHOULD_BE_BUILT = 'should be built'
75+
FAILED = 'build failed'
76+
SUCCESS = 'build success'
77+
78+
79+
class BuildStage(str, enum.Enum):
80+
DRY_RUN = 'Dry Run'
81+
PRE_BUILD = 'Pre Build'
82+
BUILD = 'Build'
83+
POST_BUILD = 'Post Build'
84+
85+
@classmethod
86+
def max_length(cls) -> int:
87+
return max(len(v.value) for v in cls.__members__.values())
88+
89+
90+
completion_instructions = """
91+
With the `--activate` option, detect your shell type and add the appropriate commands to your shell's config file
92+
so that it runs on startup. You will likely have to restart.
93+
or re-login for the autocompletion to start working.
94+
95+
You can also specify your shell using the `--shell` option.
96+
97+
If you do not want automatic modification of your shell configuration file
98+
You can manually add the commands provided below to activate autocompletion.
99+
or run them in your current terminal session for one-time activation.
100+
101+
Once again, you will likely have to restart
102+
or re-login for the autocompletion to start working.
103+
104+
bash:
105+
eval "$(register-python-argcomplete idf-build-apps)"
106+
107+
zsh:
108+
To activate completions in zsh, first make sure compinit is marked for
109+
autoload and run autoload:
110+
111+
autoload -U compinit
112+
compinit
113+
114+
Afterwards you can enable completions for idf-build-apps:
115+
116+
eval "$(register-python-argcomplete idf-build-apps)"
117+
118+
fish:
119+
# Not required to be in the config file, only run once
120+
register-python-argcomplete --shell fish idf-build-apps >~/.config/fish/completions/idf-build-apps.fish
121+
"""
122+
IDF_BUILD_APPS_TOML_FN = '.idf_build_apps.toml'

esp_bool_parser/if_parser.py

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
import operator
4+
import os
5+
from ast import (
6+
literal_eval,
7+
)
8+
from typing import (
9+
Any,
10+
)
11+
12+
from packaging.version import (
13+
Version,
14+
)
15+
from pyparsing import (
16+
Keyword,
17+
Literal,
18+
MatchFirst,
19+
ParseResults,
20+
QuotedString,
21+
Suppress,
22+
Word,
23+
alphas,
24+
delimitedList,
25+
hexnums,
26+
infixNotation,
27+
nums,
28+
opAssoc,
29+
)
30+
31+
from .constants import (
32+
IDF_VERSION,
33+
IDF_VERSION_MAJOR,
34+
IDF_VERSION_MINOR,
35+
IDF_VERSION_PATCH,
36+
)
37+
from utils import (
38+
InvalidIfClause,
39+
InvalidInput,
40+
to_version,
41+
)
42+
from soc_header import (
43+
SOC_HEADERS,
44+
)
45+
46+
47+
class Stmt:
48+
"""
49+
Statement
50+
"""
51+
52+
def get_value(self, target: str, config_name: str) -> Any:
53+
"""
54+
Lazy calculated. All subclasses of `Stmt` should implement this function.
55+
56+
:param target: ESP-IDF target
57+
:type target: str
58+
:param config_name: config name
59+
:type target: str
60+
:return: the value of the statement
61+
"""
62+
raise NotImplementedError('Please implement this function in sub classes')
63+
64+
65+
class ChipAttr(Stmt):
66+
"""
67+
Attributes defined in SOC Header Files and other keywords as followed:
68+
69+
- IDF_TARGET: target
70+
- INCLUDE_DEFAULT: take the default build targets into account or not
71+
- IDF_VERSION_MAJOR: major version of ESP-IDF
72+
- IDF_VERSION_MINOR: minor version of ESP-IDF
73+
- IDF_VERSION_PATCH: patch version of ESP-IDF
74+
- CONFIG_NAME: config name defined in the config rules
75+
"""
76+
77+
def __init__(self, t: ParseResults):
78+
self.attr: str = t[0]
79+
80+
def get_value(self, target: str, config_name: str) -> Any:
81+
from .manifest import FolderRule # lazy-load
82+
83+
if self.attr == 'IDF_TARGET':
84+
return target
85+
86+
if self.attr == 'INCLUDE_DEFAULT':
87+
return 1 if target in FolderRule.DEFAULT_BUILD_TARGETS else 0
88+
89+
if self.attr == 'IDF_VERSION':
90+
return IDF_VERSION
91+
92+
if self.attr == 'IDF_VERSION_MAJOR':
93+
return IDF_VERSION_MAJOR
94+
95+
if self.attr == 'IDF_VERSION_MINOR':
96+
return IDF_VERSION_MINOR
97+
98+
if self.attr == 'IDF_VERSION_PATCH':
99+
return IDF_VERSION_PATCH
100+
101+
if self.attr == 'CONFIG_NAME':
102+
return config_name
103+
104+
if self.attr in SOC_HEADERS[target]:
105+
return SOC_HEADERS[target][self.attr]
106+
107+
# for non-keyword cap words, check if it is defined in the environment variables
108+
if self.attr in os.environ:
109+
return os.environ[self.attr]
110+
111+
return 0 # default return 0 as false
112+
113+
114+
class Integer(Stmt):
115+
def __init__(self, t: ParseResults):
116+
self.expr: str = t[0]
117+
118+
def get_value(self, target: str, config_name: str) -> Any: # noqa: ARG002
119+
return literal_eval(self.expr)
120+
121+
122+
class String(Stmt):
123+
def __init__(self, t: ParseResults):
124+
self.expr: str = t[0]
125+
126+
def get_value(self, target: str, config_name: str) -> Any: # noqa: ARG002
127+
return literal_eval(f'"{self.expr}"') # double quotes is swallowed by QuotedString
128+
129+
130+
class List_(Stmt):
131+
def __init__(self, t: ParseResults):
132+
self.expr = t
133+
134+
def get_value(self, target: str, config_name: str) -> Any:
135+
return [item.get_value(target, config_name) for item in self.expr]
136+
137+
138+
class BoolStmt(Stmt):
139+
_OP_DICT = {
140+
'==': operator.eq,
141+
'!=': operator.ne,
142+
'>': operator.gt,
143+
'>=': operator.ge,
144+
'<': operator.lt,
145+
'<=': operator.le,
146+
'not in': lambda x, y: x not in y,
147+
'in': lambda x, y: x in y,
148+
}
149+
150+
def __init__(self, t: ParseResults):
151+
self.left: Stmt = t[0]
152+
self.comparison: str = t[1]
153+
self.right: Stmt = t[2]
154+
155+
def get_value(self, target: str, config_name: str) -> Any:
156+
_l = self.left.get_value(target, config_name)
157+
_r = self.right.get_value(target, config_name)
158+
159+
if self.comparison not in ['in', 'not in']:
160+
# will use version comparison if any of the operands is a Version
161+
if any(isinstance(x, Version) for x in [_l, _r]):
162+
_l = to_version(_l)
163+
_r = to_version(_r)
164+
else:
165+
# use str for "in" and "not in" operator
166+
if isinstance(_l, Version):
167+
_l = str(_l)
168+
if isinstance(_r, Version):
169+
_r = str(_r)
170+
171+
if self.comparison in self._OP_DICT:
172+
return self._OP_DICT[self.comparison](_l, _r)
173+
174+
raise InvalidInput(f'Unsupported comparison operator: "{self.comparison}"')
175+
176+
177+
class BoolExpr(Stmt):
178+
pass
179+
180+
181+
def _and(_l, _r):
182+
return _l and _r
183+
184+
185+
def _or(_l, _r):
186+
return _l or _r
187+
188+
189+
class BoolOrAnd(BoolExpr):
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+
)
196+
self.left: BoolStmt = t[0][0]
197+
self.right: BoolStmt = t[0][2]
198+
199+
if t[0][1] == 'and':
200+
self.operation = _and
201+
if t[0][1] == 'or':
202+
self.operation = _or
203+
204+
def get_value(self, target: str, config_name: str) -> Any:
205+
return self.operation(self.left.get_value(target, config_name), self.right.get_value(target, config_name))
206+
207+
208+
CAP_WORD = Word(alphas.upper(), nums + alphas.upper() + '_').setParseAction(ChipAttr)
209+
210+
DECIMAL_NUMBER = Word(nums)
211+
HEX_NUMBER = Literal('0x') + Word(hexnums)
212+
INTEGER = (HEX_NUMBER | DECIMAL_NUMBER).setParseAction(Integer)
213+
214+
STRING = QuotedString('"').setParseAction(String)
215+
216+
LIST = Suppress('[') + delimitedList(INTEGER | STRING).setParseAction(List_) + Suppress(']')
217+
218+
BOOL_OPERAND = CAP_WORD | INTEGER | STRING | LIST
219+
220+
EQ = Keyword('==').setParseAction(lambda t: t[0])
221+
NE = Keyword('!=').setParseAction(lambda t: t[0])
222+
LE = Keyword('<=').setParseAction(lambda t: t[0])
223+
LT = Keyword('<').setParseAction(lambda t: t[0])
224+
GE = Keyword('>=').setParseAction(lambda t: t[0])
225+
GT = Keyword('>').setParseAction(lambda t: t[0])
226+
NOT_IN = Keyword('not in').setParseAction(lambda t: t[0])
227+
IN = Keyword('in').setParseAction(lambda t: t[0])
228+
229+
BOOL_STMT = BOOL_OPERAND + (EQ | NE | LE | LT | GE | GT | NOT_IN | IN) + BOOL_OPERAND
230+
BOOL_STMT.setParseAction(BoolStmt)
231+
232+
AND = Keyword('and')
233+
OR = Keyword('or')
234+
235+
BOOL_EXPR = infixNotation(
236+
BOOL_STMT,
237+
[
238+
(MatchFirst((AND, OR)), 2, opAssoc.LEFT, BoolOrAnd),
239+
],
240+
)

0 commit comments

Comments
 (0)