mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
change(tools): idf.py & tools.py ruff formatting
This commit is contained in:
@@ -246,6 +246,8 @@ def init_cli(verbose_output: list | None = None) -> Any:
|
|||||||
self.callback(self.name, context, global_args, **action_args)
|
self.callback(self.name, context, global_args, **action_args)
|
||||||
|
|
||||||
class Action(click.Command):
|
class Action(click.Command):
|
||||||
|
callback: Callable
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
import asyncio
|
import asyncio
|
||||||
import importlib
|
import importlib
|
||||||
@@ -8,21 +8,17 @@ import re
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from asyncio.subprocess import Process
|
from asyncio.subprocess import Process
|
||||||
|
from collections.abc import Generator
|
||||||
from pkgutil import iter_modules
|
from pkgutil import iter_modules
|
||||||
|
from re import Match
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
|
||||||
from typing import Generator
|
|
||||||
from typing import List
|
|
||||||
from typing import Match
|
|
||||||
from typing import Optional
|
|
||||||
from typing import TextIO
|
from typing import TextIO
|
||||||
from typing import Tuple
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import yaml
|
import yaml
|
||||||
from esp_idf_monitor import get_ansi_converter
|
from esp_idf_monitor import get_ansi_converter
|
||||||
|
|
||||||
from idf_py_actions.errors import NoSerialPortFoundError
|
from idf_py_actions.errors import NoSerialPortFoundError
|
||||||
|
|
||||||
from .constants import GENERATORS
|
from .constants import GENERATORS
|
||||||
@@ -43,7 +39,7 @@ SHELL_COMPLETE_RUN = SHELL_COMPLETE_VAR in os.environ
|
|||||||
# https://docs.python.org/3/reference/compound_stmts.html#function-definitions
|
# https://docs.python.org/3/reference/compound_stmts.html#function-definitions
|
||||||
# Default parameter values are evaluated from left to right
|
# Default parameter values are evaluated from left to right
|
||||||
# when the function definition is executed
|
# when the function definition is executed
|
||||||
def get_build_context(ctx: Dict={}) -> Dict:
|
def get_build_context(ctx: dict = {}) -> dict:
|
||||||
"""
|
"""
|
||||||
The build context is set in the ensure_build_directory function. It can be used
|
The build context is set in the ensure_build_directory function. It can be used
|
||||||
in modules or other code, which don't have direct access to such information.
|
in modules or other code, which don't have direct access to such information.
|
||||||
@@ -64,13 +60,13 @@ def _set_build_context(args: 'PropertyDict') -> None:
|
|||||||
|
|
||||||
proj_desc_fn = f'{args.build_dir}/project_description.json'
|
proj_desc_fn = f'{args.build_dir}/project_description.json'
|
||||||
try:
|
try:
|
||||||
with open(proj_desc_fn, 'r', encoding='utf-8') as f:
|
with open(proj_desc_fn, encoding='utf-8') as f:
|
||||||
ctx['proj_desc'] = json.load(f)
|
ctx['proj_desc'] = json.load(f)
|
||||||
except (OSError, ValueError) as e:
|
except (OSError, ValueError) as e:
|
||||||
raise FatalError(f'Cannot load {proj_desc_fn}: {e}')
|
raise FatalError(f'Cannot load {proj_desc_fn}: {e}')
|
||||||
|
|
||||||
|
|
||||||
def executable_exists(args: List) -> bool:
|
def executable_exists(args: list) -> bool:
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(args)
|
subprocess.check_output(args)
|
||||||
return True
|
return True
|
||||||
@@ -79,7 +75,7 @@ def executable_exists(args: List) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _idf_version_from_cmake() -> Optional[str]:
|
def _idf_version_from_cmake() -> str | None:
|
||||||
"""Acquires version of ESP-IDF from version.cmake"""
|
"""Acquires version of ESP-IDF from version.cmake"""
|
||||||
version_path = os.path.join(os.environ['IDF_PATH'], 'tools/cmake/version.cmake')
|
version_path = os.path.join(os.environ['IDF_PATH'], 'tools/cmake/version.cmake')
|
||||||
regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)')
|
regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)')
|
||||||
@@ -92,28 +88,38 @@ def _idf_version_from_cmake() -> Optional[str]:
|
|||||||
if m:
|
if m:
|
||||||
ver[m.group(1)] = m.group(2)
|
ver[m.group(1)] = m.group(2)
|
||||||
|
|
||||||
return 'v%s.%s.%s' % (ver['MAJOR'], ver['MINOR'], ver['PATCH'])
|
return f'v{ver["MAJOR"]}.{ver["MINOR"]}.{ver["PATCH"]}'
|
||||||
except (KeyError, OSError):
|
except (KeyError, OSError):
|
||||||
sys.stderr.write('WARNING: Cannot find ESP-IDF version in version.cmake\n')
|
sys.stderr.write('WARNING: Cannot find ESP-IDF version in version.cmake\n')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_target(path: str, sdkconfig_filename: str='sdkconfig') -> Optional[str]:
|
def get_target(path: str, sdkconfig_filename: str = 'sdkconfig') -> str | None:
|
||||||
path = os.path.join(path, sdkconfig_filename)
|
path = os.path.join(path, sdkconfig_filename)
|
||||||
return get_sdkconfig_value(path, 'CONFIG_IDF_TARGET')
|
return get_sdkconfig_value(path, 'CONFIG_IDF_TARGET')
|
||||||
|
|
||||||
|
|
||||||
def idf_version() -> Optional[str]:
|
def idf_version() -> str | None:
|
||||||
"""Print version of ESP-IDF"""
|
"""Print version of ESP-IDF"""
|
||||||
|
|
||||||
# Try to get version from git:
|
# Try to get version from git:
|
||||||
try:
|
try:
|
||||||
version: Optional[str] = subprocess.check_output([
|
version: str | None = (
|
||||||
'git',
|
subprocess.check_output(
|
||||||
'--git-dir=%s' % os.path.join(os.environ['IDF_PATH'], '.git'),
|
[
|
||||||
'--work-tree=%s' % os.environ['IDF_PATH'],
|
'git',
|
||||||
'describe', '--tags', '--dirty', '--match', 'v*.*',
|
f'--git-dir={os.path.join(os.environ["IDF_PATH"], ".git")}',
|
||||||
]).decode('utf-8', 'ignore').strip()
|
f'--work-tree={os.environ["IDF_PATH"]}',
|
||||||
|
'describe',
|
||||||
|
'--tags',
|
||||||
|
'--dirty',
|
||||||
|
'--match',
|
||||||
|
'v*.*',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.decode('utf-8', 'ignore')
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
# if failed, then try to parse cmake.version file
|
# if failed, then try to parse cmake.version file
|
||||||
sys.stderr.write('WARNING: Git version unavailable, reading from source\n')
|
sys.stderr.write('WARNING: Git version unavailable, reading from source\n')
|
||||||
@@ -128,19 +134,18 @@ def get_default_serial_port() -> Any:
|
|||||||
try:
|
try:
|
||||||
import esptool
|
import esptool
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
|
||||||
ports = list(sorted(p.device for p in serial.tools.list_ports.comports()))
|
ports = list(sorted(p.device for p in serial.tools.list_ports.comports()))
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
ports = [
|
ports = [port for port in ports if not port.endswith(('Bluetooth-Incoming-Port', 'wlan-debug'))]
|
||||||
port
|
|
||||||
for port in ports
|
|
||||||
if not port.endswith(('Bluetooth-Incoming-Port', 'wlan-debug'))
|
|
||||||
]
|
|
||||||
# high baud rate could cause the failure of creation of the connection
|
# high baud rate could cause the failure of creation of the connection
|
||||||
esp = esptool.get_default_connected_device(serial_list=ports, port=None, connect_attempts=4,
|
esp = esptool.get_default_connected_device(
|
||||||
initial_baud=115200)
|
serial_list=ports, port=None, connect_attempts=4, initial_baud=115200
|
||||||
|
)
|
||||||
if esp is None:
|
if esp is None:
|
||||||
raise NoSerialPortFoundError(
|
raise NoSerialPortFoundError(
|
||||||
"No serial ports found. Connect a device, or use '-p PORT' option to set a specific port.")
|
"No serial ports found. Connect a device, or use '-p PORT' option to set a specific port."
|
||||||
|
)
|
||||||
|
|
||||||
serial_port = esp.serial_port
|
serial_port = esp.serial_port
|
||||||
esp._port.close()
|
esp._port.close()
|
||||||
@@ -150,29 +155,29 @@ def get_default_serial_port() -> Any:
|
|||||||
except NoSerialPortFoundError:
|
except NoSerialPortFoundError:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise FatalError('An exception occurred during detection of the serial port: {}'.format(e))
|
raise FatalError(f'An exception occurred during detection of the serial port: {e}')
|
||||||
|
|
||||||
|
|
||||||
# function prints warning when autocompletion is not being performed
|
# function prints warning when autocompletion is not being performed
|
||||||
# set argument stream to sys.stderr for errors and exceptions
|
# set argument stream to sys.stderr for errors and exceptions
|
||||||
def print_warning(message: str, stream: Optional[TextIO]=None) -> None:
|
def print_warning(message: str, stream: TextIO | None = None) -> None:
|
||||||
if not SHELL_COMPLETE_RUN:
|
if not SHELL_COMPLETE_RUN:
|
||||||
print(message, file=stream or sys.stderr)
|
print(message, file=stream or sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def color_print(message: str, color: str, newline: Optional[str]='\n') -> None:
|
def color_print(message: str, color: str, newline: str | None = '\n') -> None:
|
||||||
""" Print a message to stderr with colored highlighting """
|
"""Print a message to stderr with colored highlighting"""
|
||||||
ansi_normal = '\033[0m'
|
ansi_normal = '\033[0m'
|
||||||
sys.stderr.write('%s%s%s%s' % (color, message, ansi_normal, newline))
|
sys.stderr.write(f'{color}{message}{ansi_normal}{newline}')
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
def yellow_print(message: str, newline: Optional[str]='\n') -> None:
|
def yellow_print(message: str, newline: str | None = '\n') -> None:
|
||||||
ansi_yellow = '\033[0;33m'
|
ansi_yellow = '\033[0;33m'
|
||||||
color_print(message, ansi_yellow, newline)
|
color_print(message, ansi_yellow, newline)
|
||||||
|
|
||||||
|
|
||||||
def red_print(message: str, newline: Optional[str]='\n') -> None:
|
def red_print(message: str, newline: str | None = '\n') -> None:
|
||||||
ansi_red = '\033[1;31m'
|
ansi_red = '\033[1;31m'
|
||||||
color_print(message, ansi_red, newline)
|
color_print(message, ansi_red, newline)
|
||||||
|
|
||||||
@@ -181,15 +186,12 @@ def debug_print_idf_version() -> None:
|
|||||||
print_warning(f'ESP-IDF {idf_version() or "version unknown"}')
|
print_warning(f'ESP-IDF {idf_version() or "version unknown"}')
|
||||||
|
|
||||||
|
|
||||||
def load_hints() -> Dict:
|
def load_hints() -> dict:
|
||||||
"""Helper function to load hints yml file"""
|
"""Helper function to load hints yml file"""
|
||||||
hints: Dict = {
|
hints: dict = {'yml': [], 'modules': []}
|
||||||
'yml': [],
|
|
||||||
'modules': []
|
|
||||||
}
|
|
||||||
|
|
||||||
current_module_dir = os.path.dirname(__file__)
|
current_module_dir = os.path.dirname(__file__)
|
||||||
with open(os.path.join(current_module_dir, 'hints.yml'), 'r', encoding='utf-8') as file:
|
with open(os.path.join(current_module_dir, 'hints.yml'), encoding='utf-8') as file:
|
||||||
hints['yml'] = yaml.safe_load(file)
|
hints['yml'] = yaml.safe_load(file)
|
||||||
|
|
||||||
hint_modules_dir = os.path.join(current_module_dir, 'hint_modules')
|
hint_modules_dir = os.path.join(current_module_dir, 'hint_modules')
|
||||||
@@ -206,13 +208,13 @@ def load_hints() -> Dict:
|
|||||||
red_print(f'Failed to import "{name}" from "{hint_modules_dir}" as a module')
|
red_print(f'Failed to import "{name}" from "{hint_modules_dir}" as a module')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
red_print('Module "{}" does not have function generate_hint.'.format(name))
|
red_print(f'Module "{name}" does not have function generate_hint.')
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
return hints
|
return hints
|
||||||
|
|
||||||
|
|
||||||
def generate_hints_buffer(output: str, hints: Dict) -> Generator:
|
def generate_hints_buffer(output: str, hints: dict) -> Generator:
|
||||||
"""Helper function to process hints within a string buffer"""
|
"""Helper function to process hints within a string buffer"""
|
||||||
# Call modules for possible hints with unchanged output. Note that
|
# Call modules for possible hints with unchanged output. Note that
|
||||||
# hints in hints.yml expect new line trimmed, but modules should
|
# hints in hints.yml expect new line trimmed, but modules should
|
||||||
@@ -227,7 +229,7 @@ def generate_hints_buffer(output: str, hints: Dict) -> Generator:
|
|||||||
for hint in hints['yml']:
|
for hint in hints['yml']:
|
||||||
variables_list = hint.get('variables')
|
variables_list = hint.get('variables')
|
||||||
hint_list, hint_vars, re_vars = [], [], []
|
hint_list, hint_vars, re_vars = [], [], []
|
||||||
match: Optional[Match[str]] = None
|
match: Match[str] | None = None
|
||||||
try:
|
try:
|
||||||
if variables_list:
|
if variables_list:
|
||||||
for variables in variables_list:
|
for variables in variables_list:
|
||||||
@@ -238,12 +240,12 @@ def generate_hints_buffer(output: str, hints: Dict) -> Generator:
|
|||||||
try:
|
try:
|
||||||
hint_list.append(hint['hint'].format(*hint_vars))
|
hint_list.append(hint['hint'].format(*hint_vars))
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
red_print(f'Argument {e} missing in {hint}. Check hints.yml file.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
match = re.compile(hint['re']).search(output)
|
match = re.compile(hint['re']).search(output)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
red_print(f'Argument {e} missing in {hint}. Check hints.yml file.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except re.error as e:
|
except re.error as e:
|
||||||
red_print('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e))
|
red_print('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e))
|
||||||
@@ -256,14 +258,14 @@ def generate_hints_buffer(output: str, hints: Dict) -> Generator:
|
|||||||
try:
|
try:
|
||||||
yield ' '.join(['HINT:', hint['hint'].format(extra_info)])
|
yield ' '.join(['HINT:', hint['hint'].format(extra_info)])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeyError("Argument 'hint' missing in {}. Check hints.yml file.".format(hint))
|
raise KeyError(f"Argument 'hint' missing in {hint}. Check hints.yml file.")
|
||||||
|
|
||||||
|
|
||||||
def generate_hints(*filenames: str) -> Generator:
|
def generate_hints(*filenames: str) -> Generator:
|
||||||
"""Getting output files and printing hints on how to resolve errors based on the output."""
|
"""Getting output files and printing hints on how to resolve errors based on the output."""
|
||||||
hints = load_hints()
|
hints = load_hints()
|
||||||
for file_name in filenames:
|
for file_name in filenames:
|
||||||
with open(file_name, 'r', encoding='utf-8') as file:
|
with open(file_name, encoding='utf-8') as file:
|
||||||
yield from generate_hints_buffer(file.read(), hints)
|
yield from generate_hints_buffer(file.read(), hints)
|
||||||
|
|
||||||
|
|
||||||
@@ -279,14 +281,24 @@ def fit_text_in_terminal(out: str) -> str:
|
|||||||
if len(out) >= terminal_width:
|
if len(out) >= terminal_width:
|
||||||
elide_size = (terminal_width - space_for_dots) // 2
|
elide_size = (terminal_width - space_for_dots) // 2
|
||||||
# cut out the middle part of the output if it does not fit in the terminal
|
# cut out the middle part of the output if it does not fit in the terminal
|
||||||
return '...'.join([out[:elide_size], out[len(out) - elide_size:]])
|
return '...'.join([out[:elide_size], out[len(out) - elide_size :]])
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
class RunTool:
|
class RunTool:
|
||||||
def __init__(self, tool_name: str, args: List, cwd: str, env: Optional[Dict]=None, custom_error_handler: Optional[FunctionType]=None,
|
def __init__(
|
||||||
build_dir: Optional[str]=None, hints: bool=True, force_progression: bool=False, interactive: bool=False, convert_output: bool=False
|
self,
|
||||||
) -> None:
|
tool_name: str,
|
||||||
|
args: list,
|
||||||
|
cwd: str,
|
||||||
|
env: dict | None = None,
|
||||||
|
custom_error_handler: FunctionType | None = None,
|
||||||
|
build_dir: str | None = None,
|
||||||
|
hints: bool = True,
|
||||||
|
force_progression: bool = False,
|
||||||
|
interactive: bool = False,
|
||||||
|
convert_output: bool = False,
|
||||||
|
) -> None:
|
||||||
self.tool_name = tool_name
|
self.tool_name = tool_name
|
||||||
self.args = args
|
self.args = args
|
||||||
self.cwd = cwd
|
self.cwd = cwd
|
||||||
@@ -301,20 +313,23 @@ class RunTool:
|
|||||||
|
|
||||||
def __call__(self) -> None:
|
def __call__(self) -> None:
|
||||||
def quote_arg(arg: str) -> str:
|
def quote_arg(arg: str) -> str:
|
||||||
""" Quote the `arg` with whitespace in them because it can cause problems when we call it from a subprocess."""
|
"""
|
||||||
if re.match(r"^(?![\'\"]).*\s.*", arg):
|
Quote the `arg` with whitespace in them because
|
||||||
|
it can cause problems when we call it from a subprocess.
|
||||||
|
"""
|
||||||
|
if re.match(r'^(?![\'\"]).*\s.*', arg):
|
||||||
return ''.join(["'", arg, "'"])
|
return ''.join(["'", arg, "'"])
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
self.args = [str(arg) for arg in self.args]
|
self.args = [str(arg) for arg in self.args]
|
||||||
display_args = ' '.join(quote_arg(arg) for arg in self.args)
|
display_args = ' '.join(quote_arg(arg) for arg in self.args)
|
||||||
print('Running %s in directory %s' % (self.tool_name, quote_arg(self.cwd)))
|
print(f'Running {self.tool_name} in directory {quote_arg(self.cwd)}')
|
||||||
print('Executing "%s"...' % str(display_args))
|
print(f'Executing "{str(display_args)}"...')
|
||||||
|
|
||||||
env_copy = dict(os.environ)
|
env_copy = dict(os.environ)
|
||||||
env_copy.update(self.env or {})
|
env_copy.update(self.env or {})
|
||||||
|
|
||||||
process: Union[Process, subprocess.CompletedProcess[bytes]]
|
process: Process | subprocess.CompletedProcess[bytes]
|
||||||
if self.hints:
|
if self.hints:
|
||||||
process, stderr_output_file, stdout_output_file = asyncio.run(self.run_command(self.args, env_copy))
|
process, stderr_output_file, stdout_output_file = asyncio.run(self.run_command(self.args, env_copy))
|
||||||
else:
|
else:
|
||||||
@@ -332,14 +347,16 @@ class RunTool:
|
|||||||
if not self.interactive:
|
if not self.interactive:
|
||||||
for hint in generate_hints(stderr_output_file, stdout_output_file):
|
for hint in generate_hints(stderr_output_file, stdout_output_file):
|
||||||
yellow_print(hint)
|
yellow_print(hint)
|
||||||
raise FatalError('{} failed with exit code {}, output of the command is in the {} and {}'.format(self.tool_name, process.returncode,
|
raise FatalError(
|
||||||
stderr_output_file, stdout_output_file))
|
f'{self.tool_name} failed with exit code {process.returncode}, '
|
||||||
|
f'output of the command is in the {stderr_output_file} and {stdout_output_file}'
|
||||||
|
)
|
||||||
|
|
||||||
raise FatalError('{} failed with exit code {}'.format(self.tool_name, process.returncode))
|
raise FatalError(f'{self.tool_name} failed with exit code {process.returncode}')
|
||||||
|
|
||||||
async def run_command(self, cmd: List, env_copy: Dict) -> Tuple[Process, Optional[str], Optional[str]]:
|
async def run_command(self, cmd: list, env_copy: dict) -> tuple[Process, str | None, str | None]:
|
||||||
""" Run the `cmd` command with capturing stderr and stdout from that function and return returncode
|
"""Run the `cmd` command with capturing stderr and stdout from that function and return returncode
|
||||||
and of the command, the id of the process, paths to captured output """
|
and of the command, the id of the process, paths to captured output"""
|
||||||
log_dir_name = 'log'
|
log_dir_name = 'log'
|
||||||
try:
|
try:
|
||||||
os.mkdir(os.path.join(self.build_dir, log_dir_name))
|
os.mkdir(os.path.join(self.build_dir, log_dir_name))
|
||||||
@@ -348,13 +365,24 @@ class RunTool:
|
|||||||
# Note: we explicitly pass in os.environ here, as we may have set IDF_PATH there during startup
|
# Note: we explicitly pass in os.environ here, as we may have set IDF_PATH there during startup
|
||||||
# limit was added for avoiding error in idf.py confserver
|
# limit was added for avoiding error in idf.py confserver
|
||||||
try:
|
try:
|
||||||
p = await asyncio.create_subprocess_exec(*cmd, env=env_copy, limit=1024 * 256, cwd=self.cwd, stdout=asyncio.subprocess.PIPE,
|
p = await asyncio.create_subprocess_exec(
|
||||||
stderr=asyncio.subprocess.PIPE)
|
*cmd,
|
||||||
|
env=env_copy,
|
||||||
|
limit=1024 * 256,
|
||||||
|
cwd=self.cwd,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
message = f'ERROR: {sys.executable} doesn\'t support asyncio. The issue can be worked around by re-running idf.py with the "--no-hints" argument.'
|
message = (
|
||||||
|
f"ERROR: {sys.executable} doesn't support asyncio. "
|
||||||
|
"Workaround: re-run idf.py with the '--no-hints' argument."
|
||||||
|
)
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
message += ' To fix the issue use the Windows Installer for setting up your python environment, ' \
|
message += (
|
||||||
|
' To fix the issue use the Windows Installer for setting up your python environment, '
|
||||||
'available from: https://dl.espressif.com/dl/esp-idf/'
|
'available from: https://dl.espressif.com/dl/esp-idf/'
|
||||||
|
)
|
||||||
sys.exit(message)
|
sys.exit(message)
|
||||||
|
|
||||||
stderr_output_file = os.path.join(self.build_dir, log_dir_name, f'idf_py_stderr_output_{p.pid}')
|
stderr_output_file = os.path.join(self.build_dir, log_dir_name, f'idf_py_stderr_output_{p.pid}')
|
||||||
@@ -363,7 +391,8 @@ class RunTool:
|
|||||||
try:
|
try:
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
self.read_and_write_stream(p.stderr, stderr_output_file, sys.stderr),
|
self.read_and_write_stream(p.stderr, stderr_output_file, sys.stderr),
|
||||||
self.read_and_write_stream(p.stdout, stdout_output_file, sys.stdout))
|
self.read_and_write_stream(p.stdout, stdout_output_file, sys.stdout),
|
||||||
|
)
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
# The process we are trying to read from was terminated. Print the
|
# The process we are trying to read from was terminated. Print the
|
||||||
# message here and let the asyncio to finish, because
|
# message here and let the asyncio to finish, because
|
||||||
@@ -376,9 +405,11 @@ class RunTool:
|
|||||||
await p.wait() # added for avoiding None returncode
|
await p.wait() # added for avoiding None returncode
|
||||||
return p, stderr_output_file, stdout_output_file
|
return p, stderr_output_file, stdout_output_file
|
||||||
|
|
||||||
async def read_and_write_stream(self, input_stream: asyncio.StreamReader, output_filename: str,
|
async def read_and_write_stream(
|
||||||
output_stream: TextIO) -> None:
|
self, input_stream: asyncio.StreamReader, output_filename: str, output_stream: TextIO
|
||||||
|
) -> None:
|
||||||
"""read the output of the `input_stream` and then write it into `output_filename` and `output_stream`"""
|
"""read the output of the `input_stream` and then write it into `output_filename` and `output_stream`"""
|
||||||
|
|
||||||
def delete_ansi_escape(text: str) -> str:
|
def delete_ansi_escape(text: str) -> str:
|
||||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||||
return ansi_escape.sub('', text)
|
return ansi_escape.sub('', text)
|
||||||
@@ -394,7 +425,7 @@ class RunTool:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def read_stream() -> Optional[str]:
|
async def read_stream() -> str | None:
|
||||||
try:
|
try:
|
||||||
output_b = await input_stream.readline()
|
output_b = await input_stream.readline()
|
||||||
return output_b.decode(errors='ignore')
|
return output_b.decode(errors='ignore')
|
||||||
@@ -404,7 +435,7 @@ class RunTool:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def read_interactive_stream() -> Optional[str]:
|
async def read_interactive_stream() -> str | None:
|
||||||
buffer = b''
|
buffer = b''
|
||||||
while True:
|
while True:
|
||||||
output_b = await input_stream.read(1)
|
output_b = await input_stream.read(1)
|
||||||
@@ -470,9 +501,11 @@ class RunTool:
|
|||||||
for hint in generate_hints_buffer(last_line, hints):
|
for hint in generate_hints_buffer(last_line, hints):
|
||||||
yellow_print(hint)
|
yellow_print(hint)
|
||||||
last_line = ''
|
last_line = ''
|
||||||
except (RuntimeError, EnvironmentError) as e:
|
except (OSError, RuntimeError) as e:
|
||||||
yellow_print('WARNING: The exception {} was raised and we can\'t capture all your {} and '
|
yellow_print(
|
||||||
'hints on how to resolve errors can be not accurate.'.format(e, output_stream.name.strip('<>')))
|
"WARNING: The exception {} was raised and we can't capture all your {} and "
|
||||||
|
'hints on how to resolve errors can be not accurate.'.format(e, output_stream.name.strip('<>'))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_tool(*args: Any, **kwargs: Any) -> None:
|
def run_tool(*args: Any, **kwargs: Any) -> None:
|
||||||
@@ -480,8 +513,14 @@ def run_tool(*args: Any, **kwargs: Any) -> None:
|
|||||||
return RunTool(*args, **kwargs)()
|
return RunTool(*args, **kwargs)()
|
||||||
|
|
||||||
|
|
||||||
def run_target(target_name: str, args: 'PropertyDict', env: Optional[Dict]=None,
|
def run_target(
|
||||||
custom_error_handler: Optional[FunctionType]=None, force_progression: bool=False, interactive: bool=False) -> None:
|
target_name: str,
|
||||||
|
args: 'PropertyDict',
|
||||||
|
env: dict | None = None,
|
||||||
|
custom_error_handler: FunctionType | None = None,
|
||||||
|
force_progression: bool = False,
|
||||||
|
interactive: bool = False,
|
||||||
|
) -> None:
|
||||||
"""Run target in build directory."""
|
"""Run target in build directory."""
|
||||||
if env is None:
|
if env is None:
|
||||||
env = {}
|
env = {}
|
||||||
@@ -498,11 +537,19 @@ def run_target(target_name: str, args: 'PropertyDict', env: Optional[Dict]=None,
|
|||||||
if 'CLICOLOR_FORCE' not in env:
|
if 'CLICOLOR_FORCE' not in env:
|
||||||
env['CLICOLOR_FORCE'] = '1'
|
env['CLICOLOR_FORCE'] = '1'
|
||||||
|
|
||||||
RunTool(generator_cmd[0], generator_cmd + [target_name], args.build_dir, env, custom_error_handler, hints=not args.no_hints,
|
RunTool(
|
||||||
force_progression=force_progression, interactive=interactive)()
|
generator_cmd[0],
|
||||||
|
generator_cmd + [target_name],
|
||||||
|
args.build_dir,
|
||||||
|
env,
|
||||||
|
custom_error_handler,
|
||||||
|
hints=not args.no_hints,
|
||||||
|
force_progression=force_progression,
|
||||||
|
interactive=interactive,
|
||||||
|
)()
|
||||||
|
|
||||||
|
|
||||||
def _strip_quotes(value: str, regexp: re.Pattern=re.compile(r"^\"(.*)\"$|^'(.*)'$|^(.*)$")) -> Optional[str]:
|
def _strip_quotes(value: str, regexp: re.Pattern = re.compile(r"^\"(.*)\"$|^'(.*)'$|^(.*)$")) -> str | None:
|
||||||
"""
|
"""
|
||||||
Strip quotes like CMake does during parsing cache entries
|
Strip quotes like CMake does during parsing cache entries
|
||||||
"""
|
"""
|
||||||
@@ -510,7 +557,7 @@ def _strip_quotes(value: str, regexp: re.Pattern=re.compile(r"^\"(.*)\"$|^'(.*)'
|
|||||||
return [x for x in matching_values.groups() if x is not None][0].rstrip() if matching_values is not None else None
|
return [x for x in matching_values.groups() if x is not None][0].rstrip() if matching_values is not None else None
|
||||||
|
|
||||||
|
|
||||||
def _parse_cmakecache(path: str) -> Dict:
|
def _parse_cmakecache(path: str) -> dict:
|
||||||
"""
|
"""
|
||||||
Parse the CMakeCache file at 'path'.
|
Parse the CMakeCache file at 'path'.
|
||||||
|
|
||||||
@@ -529,13 +576,13 @@ def _parse_cmakecache(path: str) -> Dict:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _parse_cmdl_cmakecache(entries: List) -> Dict[str, str]:
|
def _parse_cmdl_cmakecache(entries: list) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Parse list of CMake cache entries passed in via the -D option.
|
Parse list of CMake cache entries passed in via the -D option.
|
||||||
|
|
||||||
Returns a dict of name:value.
|
Returns a dict of name:value.
|
||||||
"""
|
"""
|
||||||
result: Dict = {}
|
result: dict = {}
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
key, value = entry.split('=', 1)
|
key, value = entry.split('=', 1)
|
||||||
value = _strip_quotes(value)
|
value = _strip_quotes(value)
|
||||||
@@ -544,7 +591,7 @@ def _parse_cmdl_cmakecache(entries: List) -> Dict[str, str]:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _new_cmakecache_entries(cache: Dict, cache_cmdl: Dict) -> bool:
|
def _new_cmakecache_entries(cache: dict, cache_cmdl: dict) -> bool:
|
||||||
for entry in cache_cmdl:
|
for entry in cache_cmdl:
|
||||||
if entry not in cache:
|
if entry not in cache:
|
||||||
return True
|
return True
|
||||||
@@ -557,14 +604,15 @@ def _detect_cmake_generator(prog_name: str) -> Any:
|
|||||||
"""
|
"""
|
||||||
Find the default cmake generator, if none was specified. Raises an exception if no valid generator is found.
|
Find the default cmake generator, if none was specified. Raises an exception if no valid generator is found.
|
||||||
"""
|
"""
|
||||||
for (generator_name, generator) in GENERATORS.items():
|
for generator_name, generator in GENERATORS.items():
|
||||||
if executable_exists(generator['version']):
|
if executable_exists(generator['version']):
|
||||||
return generator_name
|
return generator_name
|
||||||
raise FatalError("To use %s, either the 'ninja' or 'GNU make' build tool must be available in the PATH" % prog_name)
|
raise FatalError(f"To use {prog_name}, either the 'ninja' or 'GNU make' build tool must be available in the PATH")
|
||||||
|
|
||||||
|
|
||||||
def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmake: bool=False,
|
def ensure_build_directory(
|
||||||
env: Optional[Dict]=None) -> None:
|
args: 'PropertyDict', prog_name: str, always_run_cmake: bool = False, env: dict | None = None
|
||||||
|
) -> None:
|
||||||
"""Check the build directory exists and that cmake has been run there.
|
"""Check the build directory exists and that cmake has been run there.
|
||||||
|
|
||||||
If this isn't the case, create the build directory (if necessary) and
|
If this isn't the case, create the build directory (if necessary) and
|
||||||
@@ -583,11 +631,11 @@ def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmak
|
|||||||
# Verify the project directory
|
# Verify the project directory
|
||||||
if not os.path.isdir(project_dir):
|
if not os.path.isdir(project_dir):
|
||||||
if not os.path.exists(project_dir):
|
if not os.path.exists(project_dir):
|
||||||
raise FatalError('Project directory %s does not exist' % project_dir)
|
raise FatalError(f'Project directory {project_dir} does not exist')
|
||||||
else:
|
else:
|
||||||
raise FatalError('%s must be a project directory' % project_dir)
|
raise FatalError(f'{project_dir} must be a project directory')
|
||||||
if not os.path.exists(os.path.join(project_dir, 'CMakeLists.txt')):
|
if not os.path.exists(os.path.join(project_dir, 'CMakeLists.txt')):
|
||||||
raise FatalError('CMakeLists.txt not found in project directory %s' % project_dir)
|
raise FatalError(f'CMakeLists.txt not found in project directory {project_dir}')
|
||||||
|
|
||||||
# Verify/create the build directory
|
# Verify/create the build directory
|
||||||
build_dir = args.build_dir
|
build_dir = args.build_dir
|
||||||
@@ -598,7 +646,7 @@ def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmak
|
|||||||
cache_path = os.path.join(build_dir, 'CMakeCache.txt')
|
cache_path = os.path.join(build_dir, 'CMakeCache.txt')
|
||||||
cache = _parse_cmakecache(cache_path) if os.path.exists(cache_path) else {}
|
cache = _parse_cmakecache(cache_path) if os.path.exists(cache_path) else {}
|
||||||
|
|
||||||
args.define_cache_entry.append('CCACHE_ENABLE=%d' % args.ccache)
|
args.define_cache_entry.append(f'CCACHE_ENABLE={args.ccache:d}')
|
||||||
|
|
||||||
cache_cmdl = _parse_cmdl_cmakecache(args.define_cache_entry)
|
cache_cmdl = _parse_cmdl_cmakecache(args.define_cache_entry)
|
||||||
|
|
||||||
@@ -614,7 +662,7 @@ def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmak
|
|||||||
'-G',
|
'-G',
|
||||||
args.generator,
|
args.generator,
|
||||||
'-DPYTHON_DEPS_CHECKED=1',
|
'-DPYTHON_DEPS_CHECKED=1',
|
||||||
'-DPYTHON={}'.format(sys.executable),
|
f'-DPYTHON={sys.executable}',
|
||||||
'-DESP_PLATFORM=1',
|
'-DESP_PLATFORM=1',
|
||||||
]
|
]
|
||||||
if args.cmake_warn_uninitialized:
|
if args.cmake_warn_uninitialized:
|
||||||
@@ -641,17 +689,20 @@ def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmak
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
generator = _detect_cmake_generator(prog_name)
|
generator = _detect_cmake_generator(prog_name)
|
||||||
if args.generator is None:
|
if args.generator is None:
|
||||||
args.generator = (generator) # reuse the previously configured generator, if none was given
|
args.generator = generator # reuse the previously configured generator, if none was given
|
||||||
if generator != args.generator:
|
if generator != args.generator:
|
||||||
raise FatalError("Build is configured for generator '%s' not '%s'. Run '%s fullclean' to start again." %
|
raise FatalError(
|
||||||
(generator, args.generator, prog_name))
|
f"Build is configured for generator '{generator}' not '{args.generator}'. "
|
||||||
|
f"Run '{prog_name} fullclean' to start again."
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
home_dir = cache['CMAKE_HOME_DIRECTORY']
|
home_dir = cache['CMAKE_HOME_DIRECTORY']
|
||||||
if os.path.realpath(home_dir) != os.path.realpath(project_dir):
|
if os.path.realpath(home_dir) != os.path.realpath(project_dir):
|
||||||
raise FatalError(
|
raise FatalError(
|
||||||
"Build directory '%s' configured for project '%s' not '%s'. Run '%s fullclean' to start again." %
|
f"Build directory '{build_dir}' configured for project '{os.path.realpath(home_dir)}' "
|
||||||
(build_dir, os.path.realpath(home_dir), os.path.realpath(project_dir), prog_name))
|
f"not '{os.path.realpath(project_dir)}'. Run '{prog_name} fullclean' to start again."
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass # if cmake failed part way, CMAKE_HOME_DIRECTORY may not be set yet
|
pass # if cmake failed part way, CMAKE_HOME_DIRECTORY may not be set yet
|
||||||
|
|
||||||
@@ -659,8 +710,10 @@ def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmak
|
|||||||
python = cache['PYTHON']
|
python = cache['PYTHON']
|
||||||
if os.path.normcase(python) != os.path.normcase(sys.executable):
|
if os.path.normcase(python) != os.path.normcase(sys.executable):
|
||||||
raise FatalError(
|
raise FatalError(
|
||||||
"'{}' is currently active in the environment while the project was configured with '{}'. "
|
f"'{sys.executable}' is currently active in the environment while the project was "
|
||||||
"Run '{} fullclean' to start again.".format(sys.executable, python, prog_name))
|
f"configured with '{python}'. "
|
||||||
|
f"Run '{prog_name} fullclean' to start again."
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -668,8 +721,8 @@ def ensure_build_directory(args: 'PropertyDict', prog_name: str, always_run_cmak
|
|||||||
_set_build_context(args)
|
_set_build_context(args)
|
||||||
|
|
||||||
|
|
||||||
def merge_action_lists(*action_lists: Dict) -> Dict:
|
def merge_action_lists(*action_lists: dict) -> dict:
|
||||||
merged_actions: Dict = {
|
merged_actions: dict = {
|
||||||
'global_options': [],
|
'global_options': [],
|
||||||
'actions': {},
|
'actions': {},
|
||||||
'global_action_callbacks': [],
|
'global_action_callbacks': [],
|
||||||
@@ -681,7 +734,7 @@ def merge_action_lists(*action_lists: Dict) -> Dict:
|
|||||||
return merged_actions
|
return merged_actions
|
||||||
|
|
||||||
|
|
||||||
def get_sdkconfig_filename(args: 'PropertyDict', cache_cmdl: Optional[Dict]=None) -> str:
|
def get_sdkconfig_filename(args: 'PropertyDict', cache_cmdl: dict | None = None) -> str:
|
||||||
"""
|
"""
|
||||||
Get project's sdkconfig file name.
|
Get project's sdkconfig file name.
|
||||||
"""
|
"""
|
||||||
@@ -693,7 +746,7 @@ def get_sdkconfig_filename(args: 'PropertyDict', cache_cmdl: Optional[Dict]=None
|
|||||||
|
|
||||||
proj_desc_path = os.path.join(args.build_dir, 'project_description.json')
|
proj_desc_path = os.path.join(args.build_dir, 'project_description.json')
|
||||||
try:
|
try:
|
||||||
with open(proj_desc_path, 'r', encoding='utf-8') as f:
|
with open(proj_desc_path, encoding='utf-8') as f:
|
||||||
proj_desc = json.load(f)
|
proj_desc = json.load(f)
|
||||||
return str(proj_desc['config_file'])
|
return str(proj_desc['config_file'])
|
||||||
except (OSError, KeyError):
|
except (OSError, KeyError):
|
||||||
@@ -702,7 +755,7 @@ def get_sdkconfig_filename(args: 'PropertyDict', cache_cmdl: Optional[Dict]=None
|
|||||||
return os.path.join(args.project_dir, 'sdkconfig')
|
return os.path.join(args.project_dir, 'sdkconfig')
|
||||||
|
|
||||||
|
|
||||||
def get_sdkconfig_value(sdkconfig_file: str, key: str) -> Optional[str]:
|
def get_sdkconfig_value(sdkconfig_file: str, key: str) -> str | None:
|
||||||
"""
|
"""
|
||||||
Return the value of given key from sdkconfig_file.
|
Return the value of given key from sdkconfig_file.
|
||||||
If sdkconfig_file does not exist or the option is not present, returns None.
|
If sdkconfig_file does not exist or the option is not present, returns None.
|
||||||
@@ -713,8 +766,8 @@ def get_sdkconfig_value(sdkconfig_file: str, key: str) -> Optional[str]:
|
|||||||
# keep track of the last seen value for the given key
|
# keep track of the last seen value for the given key
|
||||||
value = None
|
value = None
|
||||||
# if the value is quoted, this excludes the quotes from the value
|
# if the value is quoted, this excludes the quotes from the value
|
||||||
pattern = re.compile(r"^{}=\"?([^\"]*)\"?$".format(key))
|
pattern = re.compile(rf'^{key}=\"?([^\"]*)\"?$')
|
||||||
with open(sdkconfig_file, 'r', encoding='utf-8') as f:
|
with open(sdkconfig_file, encoding='utf-8') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
match = re.match(pattern, line)
|
match = re.match(pattern, line)
|
||||||
if match:
|
if match:
|
||||||
@@ -722,15 +775,16 @@ def get_sdkconfig_value(sdkconfig_file: str, key: str) -> Optional[str]:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def is_target_supported(project_path: str, supported_targets: List) -> bool:
|
def is_target_supported(project_path: str, supported_targets: list) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns True if the active target is supported, or False otherwise.
|
Returns True if the active target is supported, or False otherwise.
|
||||||
"""
|
"""
|
||||||
return get_target(project_path) in supported_targets
|
return get_target(project_path) in supported_targets
|
||||||
|
|
||||||
|
|
||||||
def _check_idf_target(args: 'PropertyDict', prog_name: str, cache: Dict,
|
def _check_idf_target(
|
||||||
cache_cmdl: Dict, env: Optional[Dict]=None) -> None:
|
args: 'PropertyDict', prog_name: str, cache: dict, cache_cmdl: dict, env: dict | None = None
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Cross-check the three settings (sdkconfig, CMakeCache, environment) and if there is
|
Cross-check the three settings (sdkconfig, CMakeCache, environment) and if there is
|
||||||
mismatch, fail with instructions on how to fix this.
|
mismatch, fail with instructions on how to fix this.
|
||||||
@@ -750,34 +804,45 @@ def _check_idf_target(args: 'PropertyDict', prog_name: str, cache: Dict,
|
|||||||
if idf_target_from_env:
|
if idf_target_from_env:
|
||||||
# Let's check that IDF_TARGET values are consistent
|
# Let's check that IDF_TARGET values are consistent
|
||||||
if idf_target_from_sdkconfig and idf_target_from_sdkconfig != idf_target_from_env:
|
if idf_target_from_sdkconfig and idf_target_from_sdkconfig != idf_target_from_env:
|
||||||
raise FatalError("Project sdkconfig '{cfg}' was generated for target '{t_conf}', but environment variable IDF_TARGET "
|
raise FatalError(
|
||||||
"is set to '{t_env}'. Run '{prog} set-target {t_env}' to generate new sdkconfig file for target {t_env}."
|
f"Project sdkconfig '{sdkconfig}' was generated for target '{idf_target_from_sdkconfig}', "
|
||||||
.format(cfg=sdkconfig, t_conf=idf_target_from_sdkconfig, t_env=idf_target_from_env, prog=prog_name))
|
f"but environment variable IDF_TARGET is set to '{idf_target_from_env}'. "
|
||||||
|
f"Run '{prog_name} set-target {idf_target_from_env}' to generate new sdkconfig "
|
||||||
|
f'file for target {idf_target_from_env}.'
|
||||||
|
)
|
||||||
|
|
||||||
if idf_target_from_cache and idf_target_from_cache != idf_target_from_env:
|
if idf_target_from_cache and idf_target_from_cache != idf_target_from_env:
|
||||||
raise FatalError("Target settings are not consistent: '{t_env}' in the environment, '{t_cache}' in CMakeCache.txt. "
|
raise FatalError(
|
||||||
"Run '{prog} fullclean' to start again."
|
f"Target settings are not consistent: '{idf_target_from_env}' in the environment, "
|
||||||
.format(t_env=idf_target_from_env, t_cache=idf_target_from_cache, prog=prog_name))
|
f"'{idf_target_from_cache}' in CMakeCache.txt. "
|
||||||
|
f"Run '{prog_name} fullclean' to start again."
|
||||||
|
)
|
||||||
|
|
||||||
if idf_target_from_cache_cmdl and idf_target_from_cache_cmdl != idf_target_from_env:
|
if idf_target_from_cache_cmdl and idf_target_from_cache_cmdl != idf_target_from_env:
|
||||||
raise FatalError("Target '{t_cmdl}' specified on command line is not consistent with "
|
raise FatalError(
|
||||||
"target '{t_env}' in the environment."
|
f"Target '{idf_target_from_cache_cmdl}' specified on command line is not consistent with "
|
||||||
.format(t_cmdl=idf_target_from_cache_cmdl, t_env=idf_target_from_env))
|
f"target '{idf_target_from_env}' in the environment."
|
||||||
|
)
|
||||||
elif idf_target_from_cache_cmdl:
|
elif idf_target_from_cache_cmdl:
|
||||||
# Check if -DIDF_TARGET is consistent with target in CMakeCache.txt
|
# Check if -DIDF_TARGET is consistent with target in CMakeCache.txt
|
||||||
if idf_target_from_cache and idf_target_from_cache != idf_target_from_cache_cmdl:
|
if idf_target_from_cache and idf_target_from_cache != idf_target_from_cache_cmdl:
|
||||||
raise FatalError("Target '{t_cmdl}' specified on command line is not consistent with "
|
raise FatalError(
|
||||||
"target '{t_cache}' in CMakeCache.txt. Run '{prog} set-target {t_cmdl}' to re-generate "
|
f"Target '{idf_target_from_cache_cmdl}' specified on command line is not consistent with "
|
||||||
'CMakeCache.txt.'
|
f"target '{idf_target_from_cache}' in CMakeCache.txt. "
|
||||||
.format(t_cache=idf_target_from_cache, t_cmdl=idf_target_from_cache_cmdl, prog=prog_name))
|
f"Run '{prog_name} set-target {idf_target_from_cache_cmdl}' to re-generate "
|
||||||
|
'CMakeCache.txt.'
|
||||||
|
)
|
||||||
|
|
||||||
elif idf_target_from_cache:
|
elif idf_target_from_cache:
|
||||||
# This shouldn't happen, unless the user manually edits CMakeCache.txt or sdkconfig, but let's check anyway.
|
# This shouldn't happen, unless the user manually edits CMakeCache.txt or sdkconfig, but let's check anyway.
|
||||||
if idf_target_from_sdkconfig and idf_target_from_cache != idf_target_from_sdkconfig:
|
if idf_target_from_sdkconfig and idf_target_from_cache != idf_target_from_sdkconfig:
|
||||||
raise FatalError("Project sdkconfig '{cfg}' was generated for target '{t_conf}', but CMakeCache.txt contains '{t_cache}'. "
|
raise FatalError(
|
||||||
"To keep the setting in sdkconfig ({t_conf}) and re-generate CMakeCache.txt, run '{prog} fullclean'. "
|
f"Project sdkconfig '{sdkconfig}' was generated for target '{idf_target_from_sdkconfig}', but "
|
||||||
"To re-generate sdkconfig for '{t_cache}' target, run '{prog} set-target {t_cache}'."
|
f"CMakeCache.txt contains '{idf_target_from_cache}'. To keep the setting in sdkconfig "
|
||||||
.format(cfg=sdkconfig, t_conf=idf_target_from_sdkconfig, t_cache=idf_target_from_cache, prog=prog_name))
|
f"({idf_target_from_sdkconfig}) and re-generate CMakeCache.txt, run '{prog_name} fullclean'. To "
|
||||||
|
f"re-generate sdkconfig for '{idf_target_from_cache}' target, run '{prog_name} set-target "
|
||||||
|
f"{idf_target_from_cache}'."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TargetChoice(click.Choice):
|
class TargetChoice(click.Choice):
|
||||||
@@ -786,8 +851,9 @@ class TargetChoice(click.Choice):
|
|||||||
- ignores hyphens
|
- ignores hyphens
|
||||||
- not case sensitive
|
- not case sensitive
|
||||||
"""
|
"""
|
||||||
def __init__(self, choices: List) -> None:
|
|
||||||
super(TargetChoice, self).__init__(choices, case_sensitive=False)
|
def __init__(self, choices: list) -> None:
|
||||||
|
super().__init__(choices, case_sensitive=False)
|
||||||
|
|
||||||
def convert(self, value: Any, param: click.Parameter, ctx: click.Context) -> Any:
|
def convert(self, value: Any, param: click.Parameter, ctx: click.Context) -> Any:
|
||||||
def normalize(string: str) -> str:
|
def normalize(string: str) -> str:
|
||||||
@@ -797,7 +863,7 @@ class TargetChoice(click.Choice):
|
|||||||
ctx.token_normalize_func = normalize
|
ctx.token_normalize_func = normalize
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return super(TargetChoice, self).convert(value, param, ctx)
|
return super().convert(value, param, ctx)
|
||||||
finally:
|
finally:
|
||||||
ctx.token_normalize_func = saved_token_normalize_func
|
ctx.token_normalize_func = saved_token_normalize_func
|
||||||
|
|
||||||
@@ -807,7 +873,7 @@ class PropertyDict(dict):
|
|||||||
if name in self:
|
if name in self:
|
||||||
return self[name]
|
return self[name]
|
||||||
else:
|
else:
|
||||||
raise AttributeError("'PropertyDict' object has no attribute '%s'" % name)
|
raise AttributeError(f"'PropertyDict' object has no attribute '{name}'")
|
||||||
|
|
||||||
def __setattr__(self, name: str, value: Any) -> None:
|
def __setattr__(self, name: str, value: Any) -> None:
|
||||||
self[name] = value
|
self[name] = value
|
||||||
@@ -816,4 +882,4 @@ class PropertyDict(dict):
|
|||||||
if name in self:
|
if name in self:
|
||||||
del self[name]
|
del self[name]
|
||||||
else:
|
else:
|
||||||
raise AttributeError("'PropertyDict' object has no attribute '%s'" % name)
|
raise AttributeError(f"'PropertyDict' object has no attribute '{name}'")
|
||||||
|
Reference in New Issue
Block a user