diff --git a/tools/export_utils/shell_types.py b/tools/export_utils/shell_types.py index cb1f541263..df860d8486 100644 --- a/tools/export_utils/shell_types.py +++ b/tools/export_utils/shell_types.py @@ -1,5 +1,6 @@ # SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 +import base64 import getpass import os import re @@ -10,9 +11,9 @@ from datetime import datetime from datetime import timedelta from pathlib import Path from subprocess import run -from tempfile import gettempdir from tempfile import NamedTemporaryFile from tempfile import TemporaryDirectory +from tempfile import gettempdir from typing import Dict from typing import List from typing import TextIO @@ -26,14 +27,22 @@ from utils import conf from utils import run_cmd -class Shell(): - def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str,str]): +class Shell: + def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str, str]): self.shell = shell self.deactivate_cmd = deactivate_cmd self.new_esp_idf_env = new_esp_idf_env try: - self.tmp_dir_path = Path(gettempdir()) / ('esp_idf_activate_' + getpass.getuser()) + username = getpass.getuser() + username_safe = ( + # If username contains special characters, base64-encode it + base64.urlsafe_b64encode(username.encode('utf-8')).decode('ascii').rstrip('=') + # Find characters that are not ASCII alphanumeric, dot, or dash + if re.search(r'[^\w.-]', username, flags=re.ASCII) + else username + ) + self.tmp_dir_path = Path(gettempdir()) / f'esp_idf_activate_{username_safe}' except Exception as e: self.tmp_dir_path = Path(gettempdir()) / 'esp_idf_activate' warn(f'Failed to get username with error: {e}. Using default temporary directory {self.tmp_dir_path}.') @@ -79,7 +88,7 @@ class Shell(): class UnixShell(Shell): - def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str,str]): + def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str, str]): super().__init__(shell, deactivate_cmd, new_esp_idf_env) with NamedTemporaryFile(dir=self.tmp_dir_path, delete=False, prefix='activate_') as fd: @@ -100,8 +109,12 @@ class UnixShell(Shell): stdout = self.autocompletion() # type: ignore if stdout is not None: fd.write(f'{stdout}\n') - fd.write((f'echo "\nDone! You can now compile ESP-IDF projects.\n' - 'Go to the project directory and run:\n\n idf.py build"\n')) + fd.write( + ( + 'echo "\nDone! You can now compile ESP-IDF projects.\n' + 'Go to the project directory and run:\n\n idf.py build"\n' + ) + ) def export(self) -> None: with open(self.script_file_path, 'w', encoding='utf-8') as fd: @@ -195,7 +208,7 @@ class ZshShell(UnixShell): class FishShell(UnixShell): - def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str,str]): + def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str, str]): super().__init__(shell, deactivate_cmd, new_esp_idf_env) self.new_esp_idf_env['IDF_TOOLS_INSTALL_CMD'] = os.path.join(conf.IDF_PATH, 'install.fish') self.new_esp_idf_env['IDF_TOOLS_EXPORT_CMD'] = os.path.join(conf.IDF_PATH, 'export.fish') @@ -219,7 +232,7 @@ class FishShell(UnixShell): class PowerShell(Shell): - def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str,str]): + def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str, str]): super().__init__(shell, deactivate_cmd, new_esp_idf_env) with NamedTemporaryFile(dir=self.tmp_dir_path, delete=False, prefix='activate_', suffix='.ps1') as fd: @@ -230,14 +243,16 @@ class PowerShell(Shell): self.new_esp_idf_env['IDF_TOOLS_EXPORT_CMD'] = os.path.join(conf.IDF_PATH, 'export.ps1') def get_functions(self) -> str: - return '\n'.join([ - r'function idf.py { &python "$Env:IDF_PATH\tools\idf.py" $args }', - r'function global:esptool.py { &python -m esptool $args }', - r'function global:espefuse.py { &python -m espefuse $args }', - r'function global:espsecure.py { &python -m espsecure $args }', - r'function global:otatool.py { &python "$Env:IDF_PATH\components\app_update\otatool.py" $args }', - r'function global:parttool.py { &python "$Env:IDF_PATH\components\partition_table\parttool.py" $args }', - ]) + return '\n'.join( + [ + r'function idf.py { &python "$Env:IDF_PATH\tools\idf.py" $args }', + r'function global:esptool.py { &python -m esptool $args }', + r'function global:espefuse.py { &python -m espefuse $args }', + r'function global:espsecure.py { &python -m espsecure $args }', + r'function global:otatool.py { &python "$Env:IDF_PATH\components\app_update\otatool.py" $args }', + r'function global:parttool.py { &python "$Env:IDF_PATH\components\partition_table\parttool.py" $args }', + ] + ) def export(self) -> None: self.init_file() @@ -254,8 +269,12 @@ class PowerShell(Shell): fd.write(f'$Env:{var}="{value}"\n') functions = self.get_functions() fd.write(f'{functions}\n') - fd.write((f'echo "\nDone! You can now compile ESP-IDF projects.\n' - 'Go to the project directory and run:\n\n idf.py build\n"')) + fd.write( + ( + 'echo "\nDone! You can now compile ESP-IDF projects.\n' + 'Go to the project directory and run:\n\n idf.py build\n"' + ) + ) def spawn(self) -> None: self.init_file() @@ -266,7 +285,7 @@ class PowerShell(Shell): class WinCmd(Shell): - def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str,str]): + def __init__(self, shell: str, deactivate_cmd: str, new_esp_idf_env: Dict[str, str]): super().__init__(shell, deactivate_cmd, new_esp_idf_env) with NamedTemporaryFile(dir=self.tmp_dir_path, delete=False, prefix='activate_', suffix='.bat') as fd: @@ -279,14 +298,16 @@ class WinCmd(Shell): self.new_esp_idf_env['IDF_TOOLS_PY_PATH'] = conf.IDF_TOOLS_PY def get_functions(self) -> str: - return '\n'.join([ - r'DOSKEY idf.py=python.exe "%IDF_PATH%\tools\idf.py" $*', - r'DOSKEY esptool.py=python.exe -m esptool $*', - r'DOSKEY espefuse.py=python.exe -m espefuse $*', - r'DOSKEY espsecure.py=python.exe -m espsecure $*', - r'DOSKEY otatool.py=python.exe "%IDF_PATH%\components\app_update\otatool.py" $*', - r'DOSKEY parttool.py=python.exe "%IDF_PATH%\components\partition_table\parttool.py" $*', - ]) + return '\n'.join( + [ + r'DOSKEY idf.py=python.exe "%IDF_PATH%\tools\idf.py" $*', + r'DOSKEY esptool.py=python.exe -m esptool $*', + r'DOSKEY espefuse.py=python.exe -m espefuse $*', + r'DOSKEY espsecure.py=python.exe -m espsecure $*', + r'DOSKEY otatool.py=python.exe "%IDF_PATH%\components\app_update\otatool.py" $*', + r'DOSKEY parttool.py=python.exe "%IDF_PATH%\components\partition_table\parttool.py" $*', + ] + ) def export(self) -> None: self.init_file() @@ -300,14 +321,18 @@ class WinCmd(Shell): fd.write(f'set {var}={value}\n') functions = self.get_functions() fd.write(f'{functions}\n') - fd.write('\n'.join([ - 'echo.', - 'echo Done! You can now compile ESP-IDF projects.', - 'echo Go to the project directory and run:', - 'echo.', - 'echo idf.py build', - 'echo.', - ])) + fd.write( + '\n'.join( + [ + 'echo.', + 'echo Done! You can now compile ESP-IDF projects.', + 'echo Go to the project directory and run:', + 'echo.', + 'echo idf.py build', + 'echo.', + ] + ) + ) def spawn(self) -> None: self.init_file() @@ -331,7 +356,7 @@ SHELL_CLASSES = { 'powershell.exe': PowerShell, 'powershell': PowerShell, 'cmd.exe': WinCmd, - 'cmd': WinCmd + 'cmd': WinCmd, } SUPPORTED_SHELLS = ' '.join(SHELL_CLASSES.keys())