diff --git a/builder/frameworks/_embed_files.py b/builder/frameworks/_embed_files.py index 4e2ba2329..65953f25d 100644 --- a/builder/frameworks/_embed_files.py +++ b/builder/frameworks/_embed_files.py @@ -14,7 +14,8 @@ import shutil from os import SEEK_CUR, SEEK_END -from os.path import basename, isfile, join +from os.path import basename, isfile +from pathlib import Path from SCons.Script import Builder @@ -24,6 +25,14 @@ mcu = board.get("build.mcu", "esp32") is_xtensa = mcu in ("esp32", "esp32s2", "esp32s3") +cmake_dir = str(env.PioPlatform().get_package_dir("tool-cmake")) +cmake_cmd = f'"{Path(cmake_dir) / "bin" / "cmake"}"' + +idf_dir = str(env.PioPlatform().get_package_dir("framework-espidf")) +data_embed_script = ( + f'"{Path(idf_dir) / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake"}"' +) + # # Embedded files helpers # @@ -33,7 +42,7 @@ def extract_files(cppdefines, files_type): result = [] files = env.GetProjectOption("board_build.%s" % files_type, "").splitlines() if files: - result.extend([join("$PROJECT_DIR", f.strip()) for f in files if f]) + result.extend([str(Path("$PROJECT_DIR") / f.strip()) for f in files if f.strip()]) else: files_define = "COMPONENT_" + files_type.upper() for define in cppdefines: @@ -53,9 +62,10 @@ def extract_files(cppdefines, files_type): return [] for f in value.split(":"): + f = f.strip() if not f: continue - result.append(join("$PROJECT_DIR", f)) + result.append(str(Path("$PROJECT_DIR") / f)) for f in result: if not isfile(env.subst(f)): @@ -76,10 +86,14 @@ def prepare_file(source, target, env): shutil.copy(filepath, filepath + ".piobkp") with open(filepath, "rb+") as fp: - fp.seek(-1, SEEK_END) - if fp.read(1) != "\0": - fp.seek(0, SEEK_CUR) + fp.seek(0, SEEK_END) + size = fp.tell() + if size == 0: fp.write(b"\0") + else: + fp.seek(-1, SEEK_END) + if fp.read(1) != b"\0": + fp.write(b"\0") def revert_original_file(source, target, env): @@ -91,17 +105,17 @@ def revert_original_file(source, target, env): def embed_files(files, files_type): for f in files: filename = basename(f) + ".txt.o" - file_target = env.TxtToBin(join("$BUILD_DIR", filename), f) + file_target = env.TxtToBin(str(Path("$BUILD_DIR") / filename), f) env.Depends("$PIOMAINPROG", file_target) if files_type == "embed_txtfiles": env.AddPreAction(file_target, prepare_file) env.AddPostAction(file_target, revert_original_file) - env.AppendUnique(PIOBUILDFILES=[env.File(join("$BUILD_DIR", filename))]) + env.AppendUnique(PIOBUILDFILES=[env.File(str(Path("$BUILD_DIR") / filename))]) def transform_to_asm(target, source, env): - files = [join("$BUILD_DIR", s.name + ".S") for s in source] - return files, source + asm_targets = [str(Path("$BUILD_DIR") / (s.name + ".S")) for s in source] + return asm_targets, source env.Append( @@ -133,22 +147,12 @@ def transform_to_asm(target, source, env): action=env.VerboseAction( " ".join( [ - join( - env.PioPlatform().get_package_dir("tool-cmake") or "", - "bin", - "cmake", - ), + cmake_cmd, "-DDATA_FILE=$SOURCE", "-DSOURCE_FILE=$TARGET", "-DFILE_TYPE=$FILE_TYPE", "-P", - join( - env.PioPlatform().get_package_dir("framework-espidf") or "", - "tools", - "cmake", - "scripts", - "data_file_embed_asm.cmake", - ), + data_embed_script, ] ), "Generating assembly for $TARGET", @@ -171,7 +175,7 @@ def transform_to_asm(target, source, env): files = extract_files(flags, files_type) if "espidf" in env.subst("$PIOFRAMEWORK"): env.Requires( - join("$BUILD_DIR", "${PROGNAME}.elf"), + str(Path("$BUILD_DIR") / "${PROGNAME}.elf"), env.FileToAsm( files, FILE_TYPE="TEXT" if files_type == "embed_txtfiles" else "BINARY", diff --git a/builder/frameworks/arduino.py b/builder/frameworks/arduino.py index ee4a1478f..ab5c08114 100644 --- a/builder/frameworks/arduino.py +++ b/builder/frameworks/arduino.py @@ -35,8 +35,7 @@ from SCons.Script import DefaultEnvironment, SConscript from platformio import fs from platformio.package.manager.tool import ToolPackageManager - -IS_WINDOWS = sys.platform.startswith("win") +from platformio.compat import IS_WINDOWS # Constants for better performance UNICORE_FLAGS = { @@ -73,7 +72,7 @@ def get_platform_default_threshold(mcu): "esp32": 32000, # Standard ESP32 "esp32s2": 32000, # ESP32-S2 "esp32s3": 32766, # ESP32-S3 - "esp32c3": 30000, # ESP32-C3 + "esp32c3": 32000, # ESP32-C3 "esp32c2": 32000, # ESP32-C2 "esp32c6": 31600, # ESP32-C6 "esp32h2": 32000, # ESP32-H2 @@ -311,7 +310,7 @@ def framework_lib_dir(self): def sdk_dir(self): if self._sdk_dir is None: self._sdk_dir = fs.to_unix_path( - join(self.framework_lib_dir, self.mcu, "include") + str(Path(self.framework_lib_dir) / self.mcu / "include") ) return self._sdk_dir @@ -507,7 +506,7 @@ def safe_remove_sdkconfig_files(): envs = [section.replace("env:", "") for section in config.sections() if section.startswith("env:")] for env_name in envs: - file_path = join(project_dir, f"sdkconfig.{env_name}") + file_path = str(Path(project_dir) / f"sdkconfig.{env_name}") if exists(file_path): safe_delete_file(file_path) @@ -566,9 +565,7 @@ def safe_remove_sdkconfig_files(): SConscript("_embed_files.py", exports="env") -flag_any_custom_sdkconfig = exists(join( - platform.get_package_dir("framework-arduinoespressif32-libs"), - "sdkconfig")) +flag_any_custom_sdkconfig = exists(str(Path(FRAMEWORK_LIB_DIR) / "sdkconfig")) def has_unicore_flags(): @@ -599,7 +596,7 @@ def matching_custom_sdkconfig(): if not flag_any_custom_sdkconfig: return True, cust_sdk_is_present - last_sdkconfig_path = join(project_dir, "sdkconfig.defaults") + last_sdkconfig_path = str(Path(project_dir) / "sdkconfig.defaults") if not exists(last_sdkconfig_path): return False, cust_sdk_is_present @@ -901,12 +898,12 @@ def get_frameworks_in_current_env(): component_manager = ComponentManager(env) component_manager.handle_component_settings() silent_action = env.Action(component_manager.restore_pioarduino_build_py) - # hack to silence scons command output + # silence scons command output silent_action.strfunction = lambda target, source, env: '' env.AddPostAction("checkprogsize", silent_action) if IS_WINDOWS: env.AddBuildMiddleware(smart_include_length_shorten) - build_script_path = join(FRAMEWORK_DIR, "tools", "pioarduino-build.py") + build_script_path = str(Path(FRAMEWORK_DIR) / "tools" / "pioarduino-build.py") SConscript(build_script_path) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index d775816c1..90a0e5433 100644 --- a/builder/frameworks/component_manager.py +++ b/builder/frameworks/component_manager.py @@ -13,7 +13,7 @@ import re import yaml from yaml import SafeLoader -from os.path import join +from pathlib import Path from typing import Set, Optional, Dict, Any, List, Tuple @@ -49,8 +49,12 @@ def __init__(self, env): self.project_src_dir = env.subst("$PROJECT_SRC_DIR") # Get Arduino framework installation directory self.arduino_framework_dir = self.platform.get_package_dir("framework-arduinoespressif32") + # Get Arduino libraries installation directory + ald = self.platform.get_package_dir("framework-arduinoespressif32-libs") # Get MCU-specific Arduino libraries directory - self.arduino_libs_mcu = join(self.platform.get_package_dir("framework-arduinoespressif32-libs"), self.mcu) + self.arduino_libs_mcu = ( + str(Path(ald) / self.mcu) if ald else "" + ) class ComponentLogger: @@ -228,13 +232,14 @@ def _get_or_create_component_yml(self) -> str: Absolute path to the component YAML file """ # Try Arduino framework first - framework_yml = join(self.config.arduino_framework_dir, "idf_component.yml") - if os.path.exists(framework_yml): + afd = self.config.arduino_framework_dir + framework_yml = str(Path(afd) / "idf_component.yml") if afd else "" + if framework_yml and os.path.exists(framework_yml): self._create_backup(framework_yml) return framework_yml # Try project source directory - project_yml = join(self.config.project_src_dir, "idf_component.yml") + project_yml = str(Path(self.config.project_src_dir) / "idf_component.yml") if os.path.exists(project_yml): self._create_backup(project_yml) return project_yml @@ -416,9 +421,12 @@ def _backup_pioarduino_build_py(self) -> None: """ if "arduino" not in self.config.env.subst("$PIOFRAMEWORK"): return - - build_py_path = join(self.config.arduino_libs_mcu, "pioarduino-build.py") - backup_path = join(self.config.arduino_libs_mcu, f"pioarduino-build.py.{self.config.mcu}") + + if not self.config.arduino_libs_mcu: + return + + build_py_path = str(Path(self.config.arduino_libs_mcu) / "pioarduino-build.py") + backup_path = str(Path(self.config.arduino_libs_mcu) / f"pioarduino-build.py.{self.config.mcu}") if os.path.exists(build_py_path) and not os.path.exists(backup_path): shutil.copy2(build_py_path, backup_path) @@ -446,7 +454,7 @@ def _remove_include_directory(self, component: str) -> None: Args: component: Component name in filesystem format """ - include_path = join(self.config.arduino_libs_mcu, "include", component) + include_path = str(Path(self.config.arduino_libs_mcu) / "include" / component) if os.path.exists(include_path): shutil.rmtree(include_path) @@ -459,7 +467,7 @@ def _remove_cpppath_entries(self) -> None: for all components that were removed from the project. Uses multiple regex patterns to catch different include path formats. """ - build_py_path = join(self.config.arduino_libs_mcu, "pioarduino-build.py") + build_py_path = str(Path(self.config.arduino_libs_mcu) / "pioarduino-build.py") if not os.path.exists(build_py_path): return @@ -577,7 +585,9 @@ def _get_lib_ignore_entries(self) -> List[str]: 'spi_flash', # Flash memory access 'esp_timer', # Timer functions 'esp_event', # Event system - 'log' # Logging system + 'log', # Logging system + 'arduino_tinyusb', # Arduino TinyUSB library + 'tinyusb' # TinyUSB library ] filtered_entries = [] @@ -665,14 +675,17 @@ def _get_arduino_core_libraries(self) -> Dict[str, str]: libraries_mapping = {} # Path to Arduino Core Libraries - arduino_libs_dir = join(self.config.arduino_framework_dir, "libraries") + afd = self.config.arduino_framework_dir + if not afd: + return libraries_mapping + arduino_libs_dir = str(Path(afd).resolve() / "libraries") if not os.path.exists(arduino_libs_dir): return libraries_mapping try: for entry in os.listdir(arduino_libs_dir): - lib_path = join(arduino_libs_dir, entry) + lib_path = str(Path(arduino_libs_dir) / entry) if os.path.isdir(lib_path): lib_name = self._get_library_name_from_properties(lib_path) if lib_name: @@ -697,7 +710,7 @@ def _get_library_name_from_properties(self, lib_dir: str) -> Optional[str]: Returns: Official library name or None if not found or readable """ - prop_path = join(lib_dir, "library.properties") + prop_path = str(Path(lib_dir) / "library.properties") if not os.path.isfile(prop_path): return None @@ -808,7 +821,10 @@ def _map_library_to_include_path(self, lib_name: str, dir_name: str) -> str: 'esptouch': 'esp_smartconfig', 'ping': 'lwip', 'netif': 'lwip', - 'tcpip': 'lwip' + 'tcpip': 'lwip', + 'usb': 'arduino_tinyusb', + 'tinyusb': 'arduino_tinyusb', + 'arduino_tinyusb': 'arduino_tinyusb' } # Check extended mapping first @@ -837,7 +853,7 @@ def _convert_lib_name_to_include(self, lib_name: str) -> str: Converted include directory name for path removal """ # Load Arduino Core Libraries on first call - if not hasattr(self, '_arduino_libraries_cache'): + if self._arduino_libraries_cache is None: self._arduino_libraries_cache = self._get_arduino_core_libraries() lib_name_lower = lib_name.lower() @@ -886,7 +902,7 @@ def _remove_ignored_lib_includes(self) -> None: components when dependencies are detected. Uses multiple regex patterns to catch different include path formats. """ - build_py_path = join(self.config.arduino_libs_mcu, "pioarduino-build.py") + build_py_path = str(Path(self.config.arduino_libs_mcu) / "pioarduino-build.py") if not os.path.exists(build_py_path): self.logger.log_change("Build file not found") @@ -925,7 +941,10 @@ def _remove_ignored_lib_includes(self) -> None: rf'.*"[^"]*{re.escape(lib_name)}[^"]*include[^"]*"[^,\n]*,?\n', rf'.*join\([^)]*"include"[^)]*"{re.escape(lib_name)}"[^)]*\),?\n', rf'.*"{re.escape(lib_name)}/include"[^,\n]*,?\n', - rf'\s*"[^"]*/{re.escape(lib_name)}/[^"]*",?\n' + rf'\s*"[^"]*[\\/]{re.escape(lib_name)}[\\/][^"]*",?\n', + # pathlib-style: Path(...)/"include"/"" + rf'.*Path\([^)]*\)\s*/\s*"include"\s*/\s*"{re.escape(lib_name)}"[^,\n]*,?\n', + rf'.*Path\([^)]*{re.escape(lib_name)}[^)]*\)\s*/\s*"include"[^,\n]*,?\n' ] removed_count = 0 @@ -987,8 +1006,10 @@ def _backup_pioarduino_build_py(self) -> None: if "arduino" not in self.config.env.subst("$PIOFRAMEWORK"): return - build_py_path = join(self.config.arduino_libs_mcu, "pioarduino-build.py") - backup_path = join(self.config.arduino_libs_mcu, f"pioarduino-build.py.{self.config.mcu}") + if not self.config.arduino_libs_mcu: + return + build_py_path = str(Path(self.config.arduino_libs_mcu) / "pioarduino-build.py") + backup_path = str(Path(self.config.arduino_libs_mcu) / f"pioarduino-build.py.{self.config.mcu}") if os.path.exists(build_py_path) and not os.path.exists(backup_path): shutil.copy2(build_py_path, backup_path) @@ -1002,7 +1023,7 @@ class BackupManager: framework build scripts, ensuring that original files can be restored when needed or when builds are cleaned. """ - + def __init__(self, config: ComponentManagerConfig): """ Initialize the backup manager with configuration access. @@ -1014,7 +1035,7 @@ def __init__(self, config: ComponentManagerConfig): config: Configuration manager instance providing access to paths """ self.config = config - + def backup_pioarduino_build_py(self) -> None: """ Create backup of the original pioarduino-build.py file. @@ -1025,13 +1046,13 @@ def backup_pioarduino_build_py(self) -> None: """ if "arduino" not in self.config.env.subst("$PIOFRAMEWORK"): return - - build_py_path = join(self.config.arduino_libs_mcu, "pioarduino-build.py") - backup_path = join(self.config.arduino_libs_mcu, f"pioarduino-build.py.{self.config.mcu}") - + + build_py_path = str(Path(self.config.arduino_libs_mcu) / "pioarduino-build.py") + backup_path = str(Path(self.config.arduino_libs_mcu) / f"pioarduino-build.py.{self.config.mcu}") + if os.path.exists(build_py_path) and not os.path.exists(backup_path): shutil.copy2(build_py_path, backup_path) - + def restore_pioarduino_build_py(self, target=None, source=None, env=None) -> None: """ Restore the original pioarduino-build.py from backup. @@ -1045,9 +1066,9 @@ def restore_pioarduino_build_py(self, target=None, source=None, env=None) -> Non source: Build source (unused, for PlatformIO compatibility) env: Environment (unused, for PlatformIO compatibility) """ - build_py_path = join(self.config.arduino_libs_mcu, "pioarduino-build.py") - backup_path = join(self.config.arduino_libs_mcu, f"pioarduino-build.py.{self.config.mcu}") - + build_py_path = str(Path(self.config.arduino_libs_mcu) / "pioarduino-build.py") + backup_path = str(Path(self.config.arduino_libs_mcu) / f"pioarduino-build.py.{self.config.mcu}") + if os.path.exists(backup_path): shutil.copy2(backup_path, build_py_path) os.remove(backup_path) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 9902237c1..dffaa2c5c 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -31,6 +31,8 @@ import re import requests import platform as sys_platform +from pathlib import Path +from urllib.parse import urlsplit, unquote import click import semantic_version @@ -52,14 +54,21 @@ env.SConscript("_embed_files.py", exports="env") platform = env.PioPlatform() -_penv_setup_file = os.path.join(platform.get_dir(), "builder", "penv_setup.py") +_component_manager_file = Path(platform.get_dir()) / "builder" / "frameworks" / "component_manager.py" +_cm_spec = importlib.util.spec_from_file_location("component_manager", _component_manager_file) +_component_manager = importlib.util.module_from_spec(_cm_spec) +_cm_spec.loader.exec_module(_component_manager) +sys.modules["component_manager"] = _component_manager + +_penv_setup_file = str(Path(platform.get_dir()) / "builder" / "penv_setup.py") _spec = importlib.util.spec_from_file_location("penv_setup", _penv_setup_file) _penv_setup = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(_penv_setup) # type: ignore[attr-defined] +sys.modules["penv_setup"] = _penv_setup get_executable_path = _penv_setup.get_executable_path # remove maybe existing old map file in project root -map_file = os.path.join(env.subst("$PROJECT_DIR"), env.subst("$PROGNAME") + ".map") +map_file = str(Path(env.subst("$PROJECT_DIR")) / (env.subst("$PROGNAME") + ".map")) if os.path.exists(map_file): os.remove(map_file) @@ -77,13 +86,13 @@ flag_custom_component_add = False flag_custom_component_remove = False -IDF5 = ( - platform.get_package_version("framework-espidf") - .split(".")[1] - .startswith("5") -) IDF_ENV_VERSION = "1.0.0" -FRAMEWORK_DIR = platform.get_package_dir("framework-espidf") +_framework_pkg_dir = platform.get_package_dir("framework-espidf") +if not _framework_pkg_dir or not os.path.isdir(_framework_pkg_dir): + sys.stderr.write(f"Error: Missing framework directory '{_framework_pkg_dir}'\n") + env.Exit(1) +FRAMEWORK_DIR_PATH = Path(_framework_pkg_dir).resolve() +FRAMEWORK_DIR = str(FRAMEWORK_DIR_PATH) TOOLCHAIN_DIR = platform.get_package_dir( "toolchain-xtensa-esp-elf" if mcu in ("esp32", "esp32s2", "esp32s3") @@ -91,8 +100,9 @@ ) PLATFORMIO_DIR = env.subst("$PROJECT_CORE_DIR") -assert os.path.isdir(FRAMEWORK_DIR) -assert os.path.isdir(TOOLCHAIN_DIR) +if not TOOLCHAIN_DIR or not os.path.isdir(TOOLCHAIN_DIR): + sys.stderr.write(f"Error: Missing toolchain directory '{TOOLCHAIN_DIR}'\n") + env.Exit(1) def create_silent_action(action_func): @@ -102,44 +112,58 @@ def create_silent_action(action_func): return silent_action if "arduino" in env.subst("$PIOFRAMEWORK"): - ARDUINO_FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32") - ARDUINO_FRMWRK_LIB_DIR = platform.get_package_dir("framework-arduinoespressif32-libs") + _arduino_pkg_dir = platform.get_package_dir("framework-arduinoespressif32") + if not _arduino_pkg_dir or not os.path.isdir(_arduino_pkg_dir): + sys.stderr.write(f"Error: Missing Arduino framework directory '{_arduino_pkg_dir}'\n") + env.Exit(1) + arduino_pkg_dir = Path(_arduino_pkg_dir) + if "@" in arduino_pkg_dir.name: + new_dir = arduino_pkg_dir.with_name(arduino_pkg_dir.name.replace("@", "-")) + if new_dir.exists(): + arduino_pkg_dir = new_dir + else: + os.rename(str(arduino_pkg_dir), str(new_dir)) + arduino_pkg_dir = new_dir + ARDUINO_FRAMEWORK_DIR_PATH = arduino_pkg_dir.resolve() + ARDUINO_FRAMEWORK_DIR = str(ARDUINO_FRAMEWORK_DIR_PATH) + if not ARDUINO_FRAMEWORK_DIR or not os.path.isdir(ARDUINO_FRAMEWORK_DIR): + sys.stderr.write(f"Error: Arduino framework directory not found: {ARDUINO_FRAMEWORK_DIR}\n") + env.Exit(1) + + _arduino_lib_dir = platform.get_package_dir("framework-arduinoespressif32-libs") + if not _arduino_lib_dir: + sys.stderr.write("Error: Missing framework-arduinoespressif32-libs package\n") + env.Exit(1) + arduino_lib_dir = Path(_arduino_lib_dir) + ARDUINO_FRMWRK_LIB_DIR_PATH = arduino_lib_dir.resolve() + ARDUINO_FRMWRK_LIB_DIR = str(ARDUINO_FRMWRK_LIB_DIR_PATH) + if mcu == "esp32c2": - ARDUINO_FRMWRK_C2_LIB_DIR = join(ARDUINO_FRMWRK_LIB_DIR, mcu) + ARDUINO_FRMWRK_C2_LIB_DIR = str(ARDUINO_FRMWRK_LIB_DIR_PATH / mcu) if not os.path.exists(ARDUINO_FRMWRK_C2_LIB_DIR): - ARDUINO_C2_DIR = join(platform.get_package_dir("framework-arduino-c2-skeleton-lib"),mcu) + _arduino_c2_dir = platform.get_package_dir("framework-arduino-c2-skeleton-lib") + if not _arduino_c2_dir: + sys.stderr.write("Error: Missing framework-arduino-c2-skeleton-lib package\n") + env.Exit(1) + arduino_c2_dir = Path(_arduino_c2_dir) + ARDUINO_C2_DIR = str(arduino_c2_dir / mcu) shutil.copytree(ARDUINO_C2_DIR, ARDUINO_FRMWRK_C2_LIB_DIR, dirs_exist_ok=True) - # Possible package names in 'package@version' format is not compatible with CMake - if "@" in os.path.basename(ARDUINO_FRAMEWORK_DIR): - new_path = os.path.join( - os.path.dirname(ARDUINO_FRAMEWORK_DIR), - os.path.basename(ARDUINO_FRAMEWORK_DIR).replace("@", "-"), - ) - os.rename(ARDUINO_FRAMEWORK_DIR, new_path) - ARDUINO_FRAMEWORK_DIR = new_path - assert ARDUINO_FRAMEWORK_DIR and os.path.isdir(ARDUINO_FRAMEWORK_DIR) - arduino_libs_mcu = join(platform.get_package_dir("framework-arduinoespressif32-libs"), mcu) + arduino_libs_mcu = str(ARDUINO_FRMWRK_LIB_DIR_PATH / mcu) BUILD_DIR = env.subst("$BUILD_DIR") PROJECT_DIR = env.subst("$PROJECT_DIR") PROJECT_SRC_DIR = env.subst("$PROJECT_SRC_DIR") -CMAKE_API_REPLY_PATH = os.path.join(".cmake", "api", "v1", "reply") +CMAKE_API_REPLY_PATH = str(Path(".cmake") / "api" / "v1" / "reply") SDKCONFIG_PATH = os.path.expandvars(board.get( "build.esp-idf.sdkconfig_path", - os.path.join(PROJECT_DIR, "sdkconfig.%s" % env.subst("$PIOENV")), + str(Path(PROJECT_DIR) / ("sdkconfig.%s" % env.subst("$PIOENV"))), )) def contains_path_traversal(url): - """Check for Path Traversal patterns""" - dangerous_patterns = [ - '../', '..\\', # Standard Path Traversal - '%2e%2e%2f', '%2e%2e%5c', # URL-encoded - '..%2f', '..%5c', # Mixed - '%252e%252e%252f', # Double encoded - ] - - url_lower = url.lower() - return any(pattern in url_lower for pattern in dangerous_patterns) + """Best-effort detection of path traversal sequences.""" + path = unquote(unquote(urlsplit(url).path)).replace("\\", "/") + parts = [p for p in path.split("/") if p not in ("", ".")] + return any(p == ".." for p in parts) # # generate modified Arduino IDF sdkconfig, applying settings from "custom_sdkconfig" @@ -192,8 +216,12 @@ def load_custom_sdkconfig_file(): # Handle local files if "file://" in file_entry: file_ref = file_entry[7:] if file_entry.startswith("file://") else file_entry - filename = os.path.basename(file_ref) - file_path = join(PROJECT_DIR, filename) + + if os.path.isabs(file_ref): + file_path = file_ref + else: + # if it's a relative path, try relative to PROJECT_DIR + file_path = str(Path(PROJECT_DIR) / file_ref) if os.path.exists(file_path): try: with open(file_path, 'r') as f: @@ -264,8 +292,11 @@ def write_sdkconfig_file(idf_config_flags, checksum_source): print("Error: Arduino framework required for sdkconfig processing") return """Write the final sdkconfig.defaults file with checksum.""" - sdkconfig_src = join(arduino_libs_mcu, "sdkconfig") - sdkconfig_dst = join(PROJECT_DIR, "sdkconfig.defaults") + sdkconfig_src = str(Path(arduino_libs_mcu) / "sdkconfig") + sdkconfig_dst = str(Path(PROJECT_DIR) / "sdkconfig.defaults") + if not os.path.isfile(sdkconfig_src): + sys.stderr.write(f"Error: Missing Arduino sdkconfig template at '{sdkconfig_src}'\n") + env.Exit(1) # Generate checksum for validation (maintains original logic) checksum = get_MD5_hash(checksum_source.strip() + mcu) @@ -274,8 +305,6 @@ def write_sdkconfig_file(idf_config_flags, checksum_source): # Write checksum header (critical for compilation decision logic) dst.write(f"# TASMOTA__{checksum}\n") - processed_flags = set() - # Process each line from source sdkconfig for line in src: flag_name = extract_flag_name(line) @@ -294,7 +323,6 @@ def write_sdkconfig_file(idf_config_flags, checksum_source): dst.write(cleaned_flag + "\n") print(f"Replace: {line.strip()} with: {cleaned_flag}") idf_config_flags.remove(custom_flag) - processed_flags.add(custom_flag_name) flag_replaced = True break @@ -358,10 +386,10 @@ def HandleCOMPONENTsettings(env): if flag_custom_sdkonfig == True and "arduino" in env.subst("$PIOFRAMEWORK") and "espidf" not in env.subst("$PIOFRAMEWORK"): HandleArduinoIDFsettings(env) - LIB_SOURCE = os.path.join(platform.get_dir(), "builder", "build_lib") - if not bool(os.path.exists(os.path.join(PROJECT_DIR, ".dummy"))): - shutil.copytree(LIB_SOURCE, os.path.join(PROJECT_DIR, ".dummy")) - PROJECT_SRC_DIR = os.path.join(PROJECT_DIR, ".dummy") + LIB_SOURCE = str(Path(platform.get_dir()) / "builder" / "build_lib") + if not bool(os.path.exists(str(Path(PROJECT_DIR) / ".dummy"))): + shutil.copytree(LIB_SOURCE, str(Path(PROJECT_DIR) / ".dummy")) + PROJECT_SRC_DIR = str(Path(PROJECT_DIR) / ".dummy") env.Replace( PROJECT_SRC_DIR=PROJECT_SRC_DIR, BUILD_FLAGS="", @@ -389,15 +417,15 @@ def get_project_lib_includes(env): return paths def is_cmake_reconfigure_required(cmake_api_reply_dir): - cmake_cache_file = os.path.join(BUILD_DIR, "CMakeCache.txt") + cmake_cache_file = str(Path(BUILD_DIR) / "CMakeCache.txt") cmake_txt_files = [ - os.path.join(PROJECT_DIR, "CMakeLists.txt"), - os.path.join(PROJECT_SRC_DIR, "CMakeLists.txt"), + str(Path(PROJECT_DIR) / "CMakeLists.txt"), + str(Path(PROJECT_SRC_DIR) / "CMakeLists.txt"), ] - cmake_preconf_dir = os.path.join(BUILD_DIR, "config") - deafult_sdk_config = os.path.join(PROJECT_DIR, "sdkconfig.defaults") - idf_deps_lock = os.path.join(PROJECT_DIR, "dependencies.lock") - ninja_buildfile = os.path.join(BUILD_DIR, "build.ninja") + cmake_preconf_dir = str(Path(BUILD_DIR) / "config") + default_sdk_config = str(Path(PROJECT_DIR) / "sdkconfig.defaults") + idf_deps_lock = str(Path(PROJECT_DIR) / "dependencies.lock") + ninja_buildfile = str(Path(BUILD_DIR) / "build.ninja") for d in (cmake_api_reply_dir, cmake_preconf_dir): if not os.path.isdir(d) or not os.listdir(d): @@ -410,8 +438,8 @@ def is_cmake_reconfigure_required(cmake_api_reply_dir): SDKCONFIG_PATH ) > os.path.getmtime(cmake_cache_file): return True - if os.path.isfile(deafult_sdk_config) and os.path.getmtime( - deafult_sdk_config + if os.path.isfile(default_sdk_config) and os.path.getmtime( + default_sdk_config ) > os.path.getmtime(cmake_cache_file): return True if os.path.isfile(idf_deps_lock) and os.path.getmtime( @@ -431,8 +459,8 @@ def is_proper_idf_project(): return all( os.path.isfile(path) for path in ( - os.path.join(PROJECT_DIR, "CMakeLists.txt"), - os.path.join(PROJECT_SRC_DIR, "CMakeLists.txt"), + str(Path(PROJECT_DIR) / "CMakeLists.txt"), + str(Path(PROJECT_SRC_DIR) / "CMakeLists.txt"), ) ) @@ -451,6 +479,13 @@ def normalize_path(path): return fs.to_unix_path(path) +CMK_TOOL = platform.get_package_dir("tool-cmake") +if not CMK_TOOL or not os.path.isdir(CMK_TOOL): + sys.stderr.write(f"Error: Missing CMake package directory '{CMK_TOOL}'\n") + env.Exit(1) +CMAKE_DIR = str(Path(CMK_TOOL) / "bin" / "cmake") + + def create_default_project_files(): root_cmake_tpl = """cmake_minimum_required(VERSION 3.16.0) include($ENV{IDF_PATH}/tools/cmake/project.cmake) @@ -466,29 +501,29 @@ def create_default_project_files(): if not os.listdir(PROJECT_SRC_DIR): # create a default main file to make CMake happy during first init - with open(os.path.join(PROJECT_SRC_DIR, "main.c"), "w") as fp: + with open(str(Path(PROJECT_SRC_DIR) / "main.c"), "w") as fp: fp.write("void app_main() {}") project_dir = PROJECT_DIR - if not os.path.isfile(os.path.join(project_dir, "CMakeLists.txt")): - with open(os.path.join(project_dir, "CMakeLists.txt"), "w") as fp: + if not os.path.isfile(str(Path(project_dir) / "CMakeLists.txt")): + with open(str(Path(project_dir) / "CMakeLists.txt"), "w") as fp: fp.write(root_cmake_tpl % os.path.basename(project_dir)) project_src_dir = PROJECT_SRC_DIR - if not os.path.isfile(os.path.join(project_src_dir, "CMakeLists.txt")): - with open(os.path.join(project_src_dir, "CMakeLists.txt"), "w") as fp: + if not os.path.isfile(str(Path(project_src_dir) / "CMakeLists.txt")): + with open(str(Path(project_src_dir) / "CMakeLists.txt"), "w") as fp: fp.write(prj_cmake_tpl % normalize_path(PROJECT_SRC_DIR)) def get_cmake_code_model(src_dir, build_dir, extra_args=None): - cmake_api_dir = os.path.join(build_dir, ".cmake", "api", "v1") - cmake_api_query_dir = os.path.join(cmake_api_dir, "query") - cmake_api_reply_dir = os.path.join(cmake_api_dir, "reply") - query_file = os.path.join(cmake_api_query_dir, "codemodel-v2") + cmake_api_dir = str(Path(build_dir) / ".cmake" / "api" / "v1") + cmake_api_query_dir = str(Path(cmake_api_dir) / "query") + cmake_api_reply_dir = str(Path(cmake_api_dir) / "reply") + query_file = str(Path(cmake_api_query_dir) / "codemodel-v2") if not os.path.isfile(query_file): - os.makedirs(os.path.dirname(query_file)) - open(query_file, "a").close() # create an empty file + Path(cmake_api_query_dir).mkdir(parents=True, exist_ok=True) + Path(query_file).touch() if not is_proper_idf_project(): create_default_project_files() @@ -503,35 +538,36 @@ def get_cmake_code_model(src_dir, build_dir, extra_args=None): codemodel = {} for target in os.listdir(cmake_api_reply_dir): if target.startswith("codemodel-v2"): - with open(os.path.join(cmake_api_reply_dir, target), "r") as fp: + with open(str(Path(cmake_api_reply_dir) / target), "r") as fp: codemodel = json.load(fp) + break - assert codemodel["version"]["major"] == 2 + if codemodel.get("version", {}).get("major") != 2: + sys.stderr.write("Error: Unsupported CMake codemodel version (need major=2)\n") + env.Exit(1) return codemodel def populate_idf_env_vars(idf_env): idf_env["IDF_PATH"] = fs.to_unix_path(FRAMEWORK_DIR) + NINJA_DIR = platform.get_package_dir("tool-ninja") + if not NINJA_DIR or not os.path.isdir(NINJA_DIR): + sys.stderr.write(f"Error: Missing ninja package directory '{NINJA_DIR}'\n") + env.Exit(1) additional_packages = [ - os.path.join(TOOLCHAIN_DIR, "bin"), - platform.get_package_dir("tool-ninja"), - os.path.join(platform.get_package_dir("tool-cmake"), "bin"), + str(Path(TOOLCHAIN_DIR) / "bin"), + NINJA_DIR, + CMAKE_DIR, os.path.dirname(get_python_exe()), ] - idf_env["PATH"] = os.pathsep.join(additional_packages + [idf_env["PATH"]]) - - # Some users reported that the `IDF_TOOLS_PATH` var can seep into the - # underlying build system. Unsetting it is a safe workaround. - if "IDF_TOOLS_PATH" in idf_env: - del idf_env["IDF_TOOLS_PATH"] - + idf_env["PATH"] = os.pathsep.join([*additional_packages, idf_env["PATH"]]) idf_env["ESP_ROM_ELF_DIR"] = platform.get_package_dir("tool-esp-rom-elfs") def get_target_config(project_configs, target_index, cmake_api_reply_dir): target_json = project_configs.get("targets")[target_index].get("jsonFile", "") - target_config_file = os.path.join(cmake_api_reply_dir, target_json) + target_config_file = str(Path(cmake_api_reply_dir) / target_json) if not os.path.isfile(target_config_file): sys.stderr.write("Error: Couldn't find target config %s\n" % target_json) env.Exit(1) @@ -559,12 +595,12 @@ def build_library( lib_name = lib_config["nameOnDisk"] lib_path = lib_config["paths"]["build"] if prepend_dir: - lib_path = os.path.join(prepend_dir, lib_path) + lib_path = str(Path(prepend_dir) / lib_path) lib_objects = compile_source_files( lib_config, default_env, project_src_dir, prepend_dir, debug_allowed ) return default_env.Library( - target=os.path.join("$BUILD_DIR", lib_path, lib_name), source=lib_objects + target=str(Path("$BUILD_DIR") / lib_path / lib_name), source=lib_objects ) @@ -656,7 +692,7 @@ def _add_archive(archive_path, link_args): if archive_path.startswith(".."): # Precompiled archives from project component _add_archive( - os.path.normpath(os.path.join(BUILD_DIR, archive_path)), + os.path.normpath(str(Path(BUILD_DIR) / archive_path)), link_args, ) else: @@ -713,14 +749,14 @@ def _extract_flags(config): def get_sdk_configuration(): - config_path = os.path.join(BUILD_DIR, "config", "sdkconfig.json") + config_path = str(Path(BUILD_DIR) / "config" / "sdkconfig.json") if not os.path.isfile(config_path): print('Warning: Could not find "sdkconfig.json" file\n') try: with open(config_path, "r") as fp: return json.load(fp) - except: + except (OSError, json.JSONDecodeError): return {} @@ -728,7 +764,7 @@ def load_component_paths(framework_components_dir, ignored_component_prefixes=No def _scan_components_from_framework(): result = [] for component in os.listdir(framework_components_dir): - component_path = os.path.join(framework_components_dir, component) + component_path = str(Path(framework_components_dir) / component) if component.startswith(ignored_component_prefixes) or not os.path.isdir( component_path ): @@ -740,20 +776,16 @@ def _scan_components_from_framework(): # First of all, try to load the list of used components from the project description components = [] ignored_component_prefixes = ignored_component_prefixes or [] - project_description_file = os.path.join(BUILD_DIR, "project_description.json") + project_description_file = str(Path(BUILD_DIR) / "project_description.json") if os.path.isfile(project_description_file): with open(project_description_file) as fp: try: data = json.load(fp) for path in data.get("build_component_paths", []): - if not os.path.basename(path).startswith( - ignored_component_prefixes - ): + if not os.path.basename(path).startswith(ignored_component_prefixes): components.append(path) - except: - print( - "Warning: Could not find load components from project description!\n" - ) + except (OSError, ValueError) as e: + print(f"Warning: Could not load components from project description: {e}\n") return components or _scan_components_from_framework() @@ -766,7 +798,7 @@ def extract_linker_script_fragments_backup(framework_components_dir, sdk_config) result = [] for component_path in project_components: - linker_fragment = os.path.join(component_path, "linker.lf") + linker_fragment = str(Path(component_path) / "linker.lf") if os.path.isfile(linker_fragment): result.append(linker_fragment) @@ -775,29 +807,27 @@ def extract_linker_script_fragments_backup(framework_components_dir, sdk_config) env.Exit(1) if mcu not in ("esp32", "esp32s2", "esp32s3"): - result.append(os.path.join(framework_components_dir, "riscv", "linker.lf")) + result.append(str(Path(framework_components_dir) / "riscv" / "linker.lf")) # Add extra linker fragments for fragment in ( - os.path.join("esp_system", "app.lf"), - os.path.join("esp_common", "common.lf"), - os.path.join("esp_common", "soc.lf"), - os.path.join("newlib", "system_libs.lf"), - os.path.join("newlib", "newlib.lf"), + str(Path("esp_system") / "app.lf"), + str(Path("esp_common") / "common.lf"), + str(Path("esp_common") / "soc.lf"), + str(Path("newlib") / "system_libs.lf"), + str(Path("newlib") / "newlib.lf"), ): - result.append(os.path.join(framework_components_dir, fragment)) + result.append(str(Path(framework_components_dir) / fragment)) if sdk_config.get("SPIRAM_CACHE_WORKAROUND", False): result.append( - os.path.join( - framework_components_dir, "newlib", "esp32-spiram-rom-functions-c.lf" - ) + str(Path(framework_components_dir) / "newlib" / "esp32-spiram-rom-functions-c.lf") ) if board.get("build.esp-idf.extra_lf_files", ""): result.extend( [ - lf if os.path.isabs(lf) else os.path.join(PROJECT_DIR, lf) + lf if os.path.isabs(lf) else str(Path(PROJECT_DIR) / lf) for lf in board.get("build.esp-idf.extra_lf_files").splitlines() if lf.strip() ] @@ -812,16 +842,19 @@ def extract_linker_script_fragments( def _normalize_fragment_path(base_dir, fragment_path): if not os.path.isabs(fragment_path): fragment_path = os.path.abspath( - os.path.join(base_dir, fragment_path) + str(Path(base_dir) / fragment_path) ) if not os.path.isfile(fragment_path): print("Warning! The `%s` fragment is not found!" % fragment_path) return fragment_path - assert os.path.isfile( - ninja_buildfile - ), "Cannot extract linker fragments! Ninja build file is missing!" + if not os.path.isfile(ninja_buildfile): + sys.stderr.write( + "Error: Missing Ninja build file '%s' for linker fragment extraction\n" + % ninja_buildfile + ) + env.Exit(1) result = [] with open(ninja_buildfile, encoding="utf8") as fp: @@ -880,31 +913,27 @@ def create_custom_libraries_list(ldgen_libraries_file, ignore_targets): def generate_project_ld_script(sdk_config, ignore_targets=None): ignore_targets = ignore_targets or [] linker_script_fragments = extract_linker_script_fragments( - os.path.join(BUILD_DIR, "build.ninja"), - os.path.join(FRAMEWORK_DIR, "components"), + str(Path(BUILD_DIR) / "build.ninja"), + str(Path(FRAMEWORK_DIR) / "components"), sdk_config ) # Create a new file to avoid automatically generated library entry as files # from this library are built internally by PlatformIO libraries_list = create_custom_libraries_list( - os.path.join(BUILD_DIR, "ldgen_libraries"), ignore_targets + str(Path(BUILD_DIR) / "ldgen_libraries"), ignore_targets ) args = { - "script": os.path.join(FRAMEWORK_DIR, "tools", "ldgen", "ldgen.py"), + "script": str(Path(FRAMEWORK_DIR) / "tools" / "ldgen" / "ldgen.py"), "config": SDKCONFIG_PATH, "fragments": " ".join( ['"%s"' % fs.to_unix_path(f) for f in linker_script_fragments] ), - "kconfig": os.path.join(FRAMEWORK_DIR, "Kconfig"), - "env_file": os.path.join("$BUILD_DIR", "config.env"), + "kconfig": str(Path(FRAMEWORK_DIR) / "Kconfig"), + "env_file": str(Path("$BUILD_DIR") / "config.env"), "libraries_list": libraries_list, - "objdump": os.path.join( - TOOLCHAIN_DIR, - "bin", - env.subst("$CC").replace("-gcc", "-objdump"), - ), + "objdump": str(Path(TOOLCHAIN_DIR) / "bin" / env.subst("$CC").replace("-gcc", "-objdump")), } cmd = ( @@ -915,30 +944,17 @@ def generate_project_ld_script(sdk_config, ignore_targets=None): '--objdump "{objdump}"' ).format(**args) - initial_ld_script = os.path.join( - FRAMEWORK_DIR, - "components", - "esp_system", - "ld", - idf_variant, - "sections.ld.in", - ) + initial_ld_script = str(Path(FRAMEWORK_DIR) / "components" / "esp_system" / "ld" / idf_variant / "sections.ld.in") framework_version = [int(v) for v in get_framework_version().split(".")] if framework_version[:2] > [5, 2]: initial_ld_script = preprocess_linker_file( initial_ld_script, - os.path.join( - BUILD_DIR, - "esp-idf", - "esp_system", - "ld", - "sections.ld.in", - ) + str(Path(BUILD_DIR) / "esp-idf" / "esp_system" / "ld" / "sections.ld.in"), ) return env.Command( - os.path.join("$BUILD_DIR", "sections.ld"), + str(Path("$BUILD_DIR") / "sections.ld"), initial_ld_script, env.VerboseAction(cmd, "Generating project linker script $TARGET"), ) @@ -1004,30 +1020,33 @@ def compile_source_files( ): build_envs = prepare_build_envs(config, default_env, debug_allowed) objects = [] - components_dir = fs.to_unix_path(os.path.join(FRAMEWORK_DIR, "components")) + # Canonical, symlink-resolved absolute path of the components directory + components_dir_path = (Path(FRAMEWORK_DIR) / "components").resolve() for source in config.get("sources", []): if source["path"].endswith(".rule"): continue compile_group_idx = source.get("compileGroupIndex") if compile_group_idx is not None: - src_dir = config["paths"]["source"] - if not os.path.isabs(src_dir): - src_dir = os.path.join(project_src_dir, config["paths"]["source"]) src_path = source.get("path") if not os.path.isabs(src_path): # For cases when sources are located near CMakeLists.txt - src_path = os.path.join(project_src_dir, src_path) + src_path = str(Path(project_src_dir) / src_path) - obj_path = os.path.join("$BUILD_DIR", prepend_dir or "") - if src_path.lower().startswith(components_dir.lower()): - obj_path = os.path.join( - obj_path, os.path.relpath(src_path, components_dir) - ) - else: - if not os.path.isabs(source["path"]): - obj_path = os.path.join(obj_path, source["path"]) - else: - obj_path = os.path.join(obj_path, os.path.basename(src_path)) + obj_path = str(Path("$BUILD_DIR") / (prepend_dir or "")) + src_path_obj = Path(src_path).resolve() + try: + rel = src_path_obj.relative_to(components_dir_path) + obj_path = str(Path(obj_path) / str(rel)) + except ValueError: + # Preserve project substructure when possible + try: + rel_prj = src_path_obj.relative_to(Path(project_src_dir).resolve()) + obj_path = str(Path(obj_path) / str(rel_prj)) + except ValueError: + if not os.path.isabs(source["path"]): + obj_path = str(Path(obj_path) / source["path"]) + else: + obj_path = str(Path(obj_path) / os.path.basename(src_path)) preserve_source_file_extension = board.get( "build.esp-idf.preserve_source_file_extension", "yes" @@ -1040,7 +1059,7 @@ def compile_source_files( if preserve_source_file_extension else os.path.splitext(obj_path)[0] ) + ".o", - source=os.path.realpath(src_path), + source=str(src_path_obj), ) ) @@ -1068,7 +1087,7 @@ def RunMenuconfig(target, source, env): rc = subprocess.call( [ - os.path.join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), + CMAKE_DIR, "--build", BUILD_DIR, "--target", @@ -1084,7 +1103,7 @@ def RunMenuconfig(target, source, env): def run_cmake(src_dir, build_dir, extra_args=None): cmd = [ - os.path.join(platform.get_package_dir("tool-cmake") or "", "bin", "cmake"), + CMAKE_DIR, "-S", src_dir, "-B", @@ -1099,21 +1118,45 @@ def run_cmake(src_dir, build_dir, extra_args=None): run_tool(cmd) +def get_lib_ignore_components(): + """ + Get components to ignore from lib_ignore project option using component_manager. + This ensures consistency with the Arduino framework's lib_ignore handling. + """ + try: + # Create a LibraryIgnoreHandler instance to process lib_ignore + config = _component_manager.ComponentManagerConfig(env) + logger = _component_manager.ComponentLogger() + lib_handler = _component_manager.LibraryIgnoreHandler(config, logger) + + # Get the processed lib_ignore entries (already converted to component names) + lib_ignore_entries = lib_handler._get_lib_ignore_entries() + + return lib_ignore_entries + except (OSError, ValueError, RuntimeError, KeyError) as e: + print(f"[ESP-IDF] Warning: Could not process lib_ignore: {e}") + return [] + + def find_lib_deps(components_map, elf_config, link_args, ignore_components=None): ignore_components = ignore_components or [] - result = [ - components_map[d["id"]]["lib"] - for d in elf_config.get("dependencies", []) - if components_map.get(d["id"], {}) - and not d["id"].startswith(tuple(ignore_components)) - ] + ignore_set = set(ignore_components) + result = [] + for d in elf_config.get("dependencies", []): + comp = components_map.get(d["id"]) + if not comp: + continue + comp_name = comp["config"]["name"] + if comp_name in ignore_set: + continue + result.append(comp["lib"]) implicit_lib_deps = link_args.get("__LIB_DEPS", []) for component in components_map.values(): component_config = component["config"] if ( component_config["type"] not in ("STATIC_LIBRARY", "OBJECT_LIBRARY") - or component_config["name"] in ignore_components + or component_config["name"] in ignore_set ): continue if ( @@ -1126,12 +1169,10 @@ def find_lib_deps(components_map, elf_config, link_args, ignore_components=None) def build_bootloader(sdk_config): - bootloader_src_dir = os.path.join( - FRAMEWORK_DIR, "components", "bootloader", "subproject" - ) + bootloader_src_dir = str(Path(FRAMEWORK_DIR) / "components" / "bootloader" / "subproject") code_model = get_cmake_code_model( bootloader_src_dir, - os.path.join(BUILD_DIR, "bootloader"), + str(Path(BUILD_DIR) / "bootloader"), [ "-DIDF_TARGET=" + idf_variant, "-DPYTHON_DEPS_CHECKED=1", @@ -1140,8 +1181,7 @@ def build_bootloader(sdk_config): "-DSDKCONFIG=" + SDKCONFIG_PATH, "-DPROJECT_SOURCE_DIR=" + PROJECT_DIR, "-DLEGACY_INCLUDE_COMMON_HEADERS=", - "-DEXTRA_COMPONENT_DIRS=" - + os.path.join(FRAMEWORK_DIR, "components", "bootloader"), + "-DEXTRA_COMPONENT_DIRS=" + str(Path(FRAMEWORK_DIR) / "components" / "bootloader"), ], ) @@ -1151,7 +1191,7 @@ def build_bootloader(sdk_config): target_configs = load_target_configurations( code_model, - os.path.join(BUILD_DIR, "bootloader", ".cmake", "api", "v1", "reply"), + str(Path(BUILD_DIR) / "bootloader" / ".cmake" / "api" / "v1" / "reply"), ) elf_config = get_project_elf(target_configs) @@ -1191,9 +1231,9 @@ def build_bootloader(sdk_config): ) return bootloader_env.ElfToBin( - os.path.join("$BUILD_DIR", "bootloader"), + str(Path("$BUILD_DIR") / "bootloader"), bootloader_env.Program( - os.path.join("$BUILD_DIR", "bootloader.elf"), bootloader_libs + str(Path("$BUILD_DIR") / "bootloader.elf"), bootloader_libs ), ) @@ -1248,18 +1288,18 @@ def generate_default_component(): file(GLOB component_sources *.c* *.S) idf_component_register(SRCS ${component_sources}) """ - dummy_component_path = os.path.join(FRAMEWORK_DIR, "components", "__pio_env") + dummy_component_path = str(Path(FRAMEWORK_DIR) / "components" / "__pio_env") if os.path.isdir(dummy_component_path): return - os.makedirs(dummy_component_path) + os.makedirs(dummy_component_path, exist_ok=True) for ext in (".cpp", ".c", ".S"): - dummy_file = os.path.join(dummy_component_path, "__dummy" + ext) + dummy_file = str(Path(dummy_component_path) / ("__dummy" + ext)) if not os.path.isfile(dummy_file): open(dummy_file, "a").close() - component_cmake = os.path.join(dummy_component_path, "CMakeLists.txt") + component_cmake = str(Path(dummy_component_path) / "CMakeLists.txt") if not os.path.isfile(component_cmake): with open(component_cmake, "w") as fp: fp.write(prj_cmake_tpl) @@ -1280,9 +1320,7 @@ def find_default_component(target_configs): def get_framework_version(): def _extract_from_cmake_version_file(): - version_cmake_file = os.path.join( - FRAMEWORK_DIR, "tools", "cmake", "version.cmake" - ) + version_cmake_file = str(Path(FRAMEWORK_DIR) / "tools" / "cmake" / "version.cmake") if not os.path.isfile(version_cmake_file): return @@ -1306,7 +1344,7 @@ def _extract_from_cmake_version_file(): def create_version_file(): - version_file = os.path.join(FRAMEWORK_DIR, "version.txt") + version_file = str(Path(FRAMEWORK_DIR) / "version.txt") if not os.path.isfile(version_file): with open(version_file, "w") as fp: fp.write(get_framework_version()) @@ -1319,12 +1357,7 @@ def generate_empty_partition_image(binary_path, image_size): env.VerboseAction( '"$ESPIDF_PYTHONEXE" "%s" %s $TARGET' % ( - os.path.join( - FRAMEWORK_DIR, - "components", - "partition_table", - "gen_empty_partition.py", - ), + str(Path(FRAMEWORK_DIR) / "components" / "partition_table" / "gen_empty_partition.py"), image_size, ), "Generating an empty partition $TARGET", @@ -1344,7 +1377,7 @@ def get_partition_info(pt_path, pt_offset, pt_params): cmd = [ get_python_exe(), - os.path.join(FRAMEWORK_DIR, "components", "partition_table", "parttool.py"), + str(Path(FRAMEWORK_DIR) / "components" / "partition_table" / "parttool.py"), "-q", "--partition-table-offset", hex(pt_offset), @@ -1401,32 +1434,14 @@ def preprocess_linker_file(src_ld_script, target_ld_script): env.VerboseAction( " ".join( [ - os.path.join( - platform.get_package_dir("tool-cmake"), - "bin", - "cmake", - ), - "-DCC=%s" - % os.path.join( - TOOLCHAIN_DIR, - "bin", - "$CC", - ), + f'"{CMAKE_DIR}"', + f'-DCC="{str(Path(TOOLCHAIN_DIR) / "bin" / "$CC")}"', "-DSOURCE=$SOURCE", "-DTARGET=$TARGET", - "-DCONFIG_DIR=%s" % os.path.join(BUILD_DIR, "config"), - "-DLD_DIR=%s" - % os.path.join( - FRAMEWORK_DIR, "components", "esp_system", "ld" - ), + f'-DCONFIG_DIR="{str(Path(BUILD_DIR) / "config")}"', + f'-DLD_DIR="{str(Path(FRAMEWORK_DIR) / "components" / "esp_system" / "ld")}"', "-P", - os.path.join( - "$BUILD_DIR", - "esp-idf", - "esp_system", - "ld", - "linker_script_generator.cmake", - ), + f'"{str(Path("$BUILD_DIR") / "esp-idf" / "esp_system" / "ld" / "linker_script_generator.cmake")}"', ] ), "Generating LD script $TARGET", @@ -1435,25 +1450,23 @@ def preprocess_linker_file(src_ld_script, target_ld_script): def generate_mbedtls_bundle(sdk_config): - bundle_path = os.path.join("$BUILD_DIR", "x509_crt_bundle") + bundle_path = str(Path("$BUILD_DIR") / "x509_crt_bundle") if os.path.isfile(env.subst(bundle_path)): return - default_crt_dir = os.path.join( - FRAMEWORK_DIR, "components", "mbedtls", "esp_crt_bundle" - ) + default_crt_dir = str(Path(FRAMEWORK_DIR) / "components" / "mbedtls" / "esp_crt_bundle") - cmd = [get_python_exe(), os.path.join(default_crt_dir, "gen_crt_bundle.py")] + cmd = [get_python_exe(), str(Path(default_crt_dir) / "gen_crt_bundle.py")] crt_args = ["--input"] if sdk_config.get("MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL", False): - crt_args.append(os.path.join(default_crt_dir, "cacrt_all.pem")) - crt_args.append(os.path.join(default_crt_dir, "cacrt_local.pem")) + crt_args.append(str(Path(default_crt_dir) / "cacrt_all.pem")) + crt_args.append(str(Path(default_crt_dir) / "cacrt_local.pem")) elif sdk_config.get("MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN", False): - crt_args.append(os.path.join(default_crt_dir, "cacrt_all.pem")) - crt_args.append(os.path.join(default_crt_dir, "cacrt_local.pem")) + crt_args.append(str(Path(default_crt_dir) / "cacrt_all.pem")) + crt_args.append(str(Path(default_crt_dir) / "cacrt_local.pem")) cmd.extend( - ["--filter", os.path.join(default_crt_dir, "cmn_crt_authorities.csv")] + ["--filter", str(Path(default_crt_dir) / "cmn_crt_authorities.csv")] ) if sdk_config.get("MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE", False): @@ -1467,27 +1480,16 @@ def generate_mbedtls_bundle(sdk_config): # Use exec_command to change working directory exec_command(cmd + crt_args, cwd=BUILD_DIR) - bundle_path = os.path.join("$BUILD_DIR", "x509_crt_bundle") env.Execute( env.VerboseAction( " ".join( [ - os.path.join( - env.PioPlatform().get_package_dir("tool-cmake"), - "bin", - "cmake", - ), - "-DDATA_FILE=" + bundle_path, - "-DSOURCE_FILE=%s.S" % bundle_path, + f'"{CMAKE_DIR}"', + f'-DDATA_FILE="{bundle_path}"', + f'-DSOURCE_FILE="{bundle_path}.S"', "-DFILE_TYPE=BINARY", "-P", - os.path.join( - FRAMEWORK_DIR, - "tools", - "cmake", - "scripts", - "data_file_embed_asm.cmake", - ), + f'"{str(Path(FRAMEWORK_DIR) / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake")}"', ] ), "Generating assembly for certificate bundle...", @@ -1496,7 +1498,7 @@ def generate_mbedtls_bundle(sdk_config): def _get_uv_exe(): - return get_executable_path(os.path.join(PLATFORMIO_DIR, "penv"), "uv") + return get_executable_path(str(Path(PLATFORMIO_DIR) / "penv"), "uv") def install_python_deps(): @@ -1518,7 +1520,7 @@ def _get_installed_uv_packages(python_exe_path): return result - skip_python_packages = os.path.join(FRAMEWORK_DIR, ".pio_skip_pypackages") + skip_python_packages = str(Path(FRAMEWORK_DIR) / ".pio_skip_pypackages") if os.path.isfile(skip_python_packages): return @@ -1573,7 +1575,7 @@ def get_idf_venv_dir(): # as an IDF component requires a different version of the IDF package and # hence a different set of Python deps or their versions idf_version = get_framework_version() - return os.path.join(PLATFORMIO_DIR, "penv", f".espidf-{idf_version}") + return str(Path(PLATFORMIO_DIR) / "penv" / f".espidf-{idf_version}") def ensure_python_venv_available(): @@ -1611,7 +1613,7 @@ def _is_venv_outdated(venv_data_file): ) return True return False - except: + except (OSError, ValueError): return True def _create_venv(venv_dir): @@ -1638,13 +1640,12 @@ def _create_venv(venv_dir): # Verify that the venv was created successfully by checking for Python executable python_path = get_executable_path(venv_dir, "python") - - assert os.path.isfile( - python_path - ), "Error: Failed to create a proper virtual environment. Missing the Python executable!" + if not os.path.isfile(python_path): + sys.stderr.write("Error: Failed to create a proper virtual environment. Missing the Python executable!\n") + env.Exit(1) venv_dir = get_idf_venv_dir() - venv_data_file = os.path.join(venv_dir, "pio-idf-venv.json") + venv_data_file = str(Path(venv_dir) / "pio-idf-venv.json") if not os.path.isfile(venv_data_file) or _is_venv_outdated(venv_data_file): _create_venv(venv_dir) install_python_deps() @@ -1657,15 +1658,10 @@ def _create_venv(venv_dir): def get_python_exe(): - python_exe_path = os.path.join( - get_idf_venv_dir(), - "Scripts" if IS_WINDOWS else "bin", - "python" + (".exe" if IS_WINDOWS else ""), - ) - - assert os.path.isfile(python_exe_path), ( - "Error: Missing Python executable file `%s`" % python_exe_path - ) + python_exe_path = get_executable_path(get_idf_venv_dir(), "python") + if not os.path.isfile(python_exe_path): + sys.stderr.write("Error: Missing Python executable file `%s`\n" % python_exe_path) + env.Exit(1) return python_exe_path @@ -1692,34 +1688,21 @@ def get_python_exe(): # if not board.get("build.ldscript", ""): - initial_ld_script = board.get("build.esp-idf.ldscript", os.path.join( - FRAMEWORK_DIR, - "components", - "esp_system", - "ld", - idf_variant, - "memory.ld.in", - )) + initial_ld_script = board.get("build.esp-idf.ldscript", str(Path(FRAMEWORK_DIR) / "components" / "esp_system" / "ld" / idf_variant / "memory.ld.in")) framework_version = [int(v) for v in get_framework_version().split(".")] if framework_version[:2] > [5, 2]: initial_ld_script = preprocess_linker_file( initial_ld_script, - os.path.join( - BUILD_DIR, - "esp-idf", - "esp_system", - "ld", - "memory.ld.in", - ) + str(Path(BUILD_DIR) / "esp-idf" / "esp_system" / "ld" / "memory.ld.in") ) linker_script = env.Command( - os.path.join("$BUILD_DIR", "memory.ld"), + str(Path("$BUILD_DIR") / "memory.ld"), initial_ld_script, env.VerboseAction( '$CC -I"$BUILD_DIR/config" -I"%s" -C -P -x c -E $SOURCE -o $TARGET' - % os.path.join(FRAMEWORK_DIR, "components", "esp_system", "ld"), + % str(Path(FRAMEWORK_DIR) / "components" / "esp_system" / "ld"), "Generating LD script $TARGET", ), ) @@ -1751,12 +1734,6 @@ def get_python_exe(): ) ) -if os.path.isfile(os.path.join(PROJECT_SRC_DIR, "sdkconfig.h")): - print( - "Warning! Starting with ESP-IDF v4.0, new project structure is required: \n" - "https://docs.platformio.org/en/latest/frameworks/espidf.html#project-structure" - ) - # # Initial targets loading # @@ -1765,17 +1742,13 @@ def get_python_exe(): # default 'src' folder we need to add this as an extra component. If there is no 'main' # folder CMake won't generate dependencies properly extra_components = [] -if PROJECT_SRC_DIR != os.path.join(PROJECT_DIR, "main"): - extra_components.append(PROJECT_SRC_DIR) +if PROJECT_SRC_DIR != str(Path(PROJECT_DIR) / "main"): + extra_components.append(str(Path(PROJECT_SRC_DIR).resolve())) if "arduino" in env.subst("$PIOFRAMEWORK"): - print( - "Warning! Arduino framework as an ESP-IDF component doesn't handle " - "the `variant` field! The default `esp32` variant will be used." - ) extra_components.append(ARDUINO_FRAMEWORK_DIR) # Add path to internal Arduino libraries so that the LDF will be able to find them env.Append( - LIBSOURCE_DIRS=[os.path.join(ARDUINO_FRAMEWORK_DIR, "libraries")] + LIBSOURCE_DIRS=[str(Path(ARDUINO_FRAMEWORK_DIR) / "libraries")] ) # Set ESP-IDF version environment variables (needed for proper Kconfig processing) @@ -1787,7 +1760,7 @@ def get_python_exe(): extra_cmake_args = [ "-DIDF_TARGET=" + idf_variant, "-DPYTHON_DEPS_CHECKED=1", - "-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components), + "-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(str(Path(p).resolve()) for p in extra_components), "-DPYTHON=" + get_python_exe(), "-DSDKCONFIG=" + SDKCONFIG_PATH, f"-DESP_IDF_VERSION={major_version}", @@ -1797,7 +1770,7 @@ def get_python_exe(): # This will add the linker flag for the map file extra_cmake_args.append( - f'-DCMAKE_EXE_LINKER_FLAGS=-Wl,-Map={fs.to_unix_path(os.path.join(BUILD_DIR, env.subst("$PROGNAME") + ".map"))}' + f'-DCMAKE_EXE_LINKER_FLAGS=-Wl,-Map={fs.to_unix_path(str(Path(BUILD_DIR) / (env.subst("$PROGNAME") + ".map")))}' ) # Add any extra args from board config @@ -1811,16 +1784,16 @@ def get_python_exe(): ) # At this point the sdkconfig file should be generated by the underlying build system -assert os.path.isfile(SDKCONFIG_PATH), ( - "Missing auto-generated SDK configuration file `%s`" % SDKCONFIG_PATH -) +if not os.path.isfile(SDKCONFIG_PATH): + sys.stderr.write("Missing auto-generated SDK configuration file `%s`\n" % SDKCONFIG_PATH) + env.Exit(1) if not project_codemodel: sys.stderr.write("Error: Couldn't find code model generated by CMake\n") env.Exit(1) target_configs = load_target_configurations( - project_codemodel, os.path.join(BUILD_DIR, CMAKE_API_REPLY_PATH) + project_codemodel, str(Path(BUILD_DIR) / CMAKE_API_REPLY_PATH) ) sdk_config = get_sdk_configuration() @@ -1840,10 +1813,10 @@ def get_python_exe(): ) env.Exit(1) -project_ld_scipt = generate_project_ld_script( +project_ld_script = generate_project_ld_script( sdk_config, [project_target_name, "__pio_env"] ) -env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", project_ld_scipt) +env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", project_ld_script) elf_config = get_project_elf(target_configs) default_config_name = find_default_component(target_configs) @@ -1860,7 +1833,7 @@ def get_python_exe(): env.Exit(1) for component_config in framework_components_map.values(): - env.Depends(project_ld_scipt, component_config["lib"]) + env.Depends(project_ld_script, component_config["lib"]) project_config = target_configs.get(project_target_name, {}) default_config = target_configs.get(default_config_name, {}) @@ -1891,8 +1864,14 @@ def get_python_exe(): # Process main parts of the framework # +# Get components to ignore from lib_ignore option +lib_ignore_components = get_lib_ignore_components() +if lib_ignore_components: + print(f"[ESP-IDF] Ignoring components based on lib_ignore: {', '.join(lib_ignore_components)}") +ignore_components_list = [project_target_name, *lib_ignore_components] + libs = find_lib_deps( - framework_components_map, elf_config, link_args, [project_target_name] + framework_components_map, elf_config, link_args, ignore_components_list ) # Extra flags which need to be explicitly specified in LINKFLAGS section because SCons @@ -1915,7 +1894,7 @@ def get_python_exe(): ld_index = extra_flags.index("memory.ld") extra_flags.pop(ld_index) extra_flags.pop(ld_index - 1) -except: +except (ValueError, IndexError): print("Warning! Couldn't find the main linker script in the CMake code model.") # @@ -1926,9 +1905,14 @@ def get_python_exe(): # Remove project source files from following build stages as they're # built as part of the framework def _skip_prj_source_files(node): - if node.srcnode().get_path().lower().startswith(PROJECT_SRC_DIR.lower()): + project_src_resolved = Path(PROJECT_SRC_DIR).resolve() + node_path_resolved = Path(node.srcnode().get_path()).resolve() + try: + node_path_resolved.relative_to(project_src_resolved) + except ValueError: + return node + else: return None - return node env.AddBuildMiddleware(_skip_prj_source_files) @@ -1937,27 +1921,25 @@ def _skip_prj_source_files(node): # Generate partition table # -fwpartitions_dir = os.path.join(FRAMEWORK_DIR, "components", "partition_table") +fwpartitions_dir = str(Path(FRAMEWORK_DIR) / "components" / "partition_table") partitions_csv = board.get("build.partitions", "partitions_singleapp.csv") partition_table_offset = sdk_config.get("PARTITION_TABLE_OFFSET", 0x8000) env.Replace( PARTITIONS_TABLE_CSV=os.path.abspath( - os.path.join(fwpartitions_dir, partitions_csv) - if os.path.isfile(os.path.join(fwpartitions_dir, partitions_csv)) + str(Path(fwpartitions_dir) / partitions_csv) + if os.path.isfile(str(Path(fwpartitions_dir) / partitions_csv)) else partitions_csv ) ) partition_table = env.Command( - os.path.join("$BUILD_DIR", "partitions.bin"), + str(Path("$BUILD_DIR") / "partitions.bin"), "$PARTITIONS_TABLE_CSV", env.VerboseAction( '"$ESPIDF_PYTHONEXE" "%s" -q --offset "%s" --flash-size "%s" $SOURCE $TARGET' % ( - os.path.join( - FRAMEWORK_DIR, "components", "partition_table", "gen_esp32part.py" - ), + str(Path(FRAMEWORK_DIR) / "components" / "partition_table" / "gen_esp32part.py"), partition_table_offset, board.get("upload.flash_size", "4MB"), ), @@ -1985,11 +1967,11 @@ def _skip_prj_source_files(node): "upload.bootloader_offset", "0x1000" if mcu in ["esp32", "esp32s2"] else ("0x2000" if mcu in ["esp32c5", "esp32p4"] else "0x0"), ), - os.path.join("$BUILD_DIR", "bootloader.bin"), + str(Path("$BUILD_DIR") / "bootloader.bin"), ), ( board.get("upload.partition_table_offset", hex(partition_table_offset)), - os.path.join("$BUILD_DIR", "partitions.bin"), + str(Path("$BUILD_DIR") / "partitions.bin"), ), ], ) @@ -1999,19 +1981,12 @@ def _skip_prj_source_files(node): # if "arduino" in env.subst("$PIOFRAMEWORK"): - arduino_config_name = list( - filter( - lambda config_name: config_name.startswith( - "__idf_framework-arduinoespressif32" - ), - target_configs, - ) - )[0] - env.AppendUnique( - CPPDEFINES=extract_defines( - target_configs.get(arduino_config_name, {}).get("compileGroups", [])[0] - ) - ) + arduino_candidates = [n for n in target_configs if n.startswith("__idf_framework-arduinoespressif32")] + if arduino_candidates: + arduino_cfg = target_configs.get(arduino_candidates[0], {}) + cg_list = arduino_cfg.get("compileGroups", []) + if cg_list: + env.AppendUnique(CPPDEFINES=extract_defines(cg_list[0])) # Project files should be compiled only when a special # option is enabled when running 'test' command @@ -2102,7 +2077,7 @@ def _skip_prj_source_files(node): # Compile ULP sources in 'ulp' folder # -ulp_dir = os.path.join(PROJECT_DIR, "ulp") +ulp_dir = str(Path(PROJECT_DIR) / "ulp") if os.path.isdir(ulp_dir) and os.listdir(ulp_dir) and mcu not in ("esp32c2", "esp32c3", "esp32h2"): env.SConscript("ulp.py", exports="env sdk_config project_config app_includes idf_variant") @@ -2112,45 +2087,61 @@ def _skip_prj_source_files(node): if ("arduino" in env.subst("$PIOFRAMEWORK")) and ("espidf" not in env.subst("$PIOFRAMEWORK")): def idf_lib_copy(source, target, env): - env_build = join(env["PROJECT_BUILD_DIR"],env["PIOENV"]) - sdkconfig_h_path = join(env_build,"config","sdkconfig.h") - arduino_libs = ARDUINO_FRMWRK_LIB_DIR - lib_src = join(env_build,"esp-idf") - lib_dst = join(arduino_libs,mcu,"lib") - ld_dst = join(arduino_libs,mcu,"ld") - mem_var = join(arduino_libs,mcu,board.get("build.arduino.memory_type", (board.get("build.flash_mode", "dio") + "_qspi"))) - src = [join(lib_src,x) for x in os.listdir(lib_src)] + def _replace_move(src, dst): + dst_p = Path(dst) + dst_p.parent.mkdir(parents=True, exist_ok=True) + try: + os.remove(dst) + except FileNotFoundError: + pass + try: + os.replace(src, dst) + except OSError: + shutil.move(src, dst) + env_build = str(Path(env["PROJECT_BUILD_DIR"]) / env["PIOENV"]) + sdkconfig_h_path = str(Path(env_build) / "config" / "sdkconfig.h") + arduino_libs = str(Path(ARDUINO_FRMWRK_LIB_DIR)) + lib_src = str(Path(env_build) / "esp-idf") + lib_dst = str(Path(arduino_libs) / mcu / "lib") + ld_dst = str(Path(arduino_libs) / mcu / "ld") + mem_var = str(Path(arduino_libs) / mcu / board.get("build.arduino.memory_type", (board.get("build.flash_mode", "dio") + "_qspi"))) + # Ensure destinations exist + for d in (lib_dst, ld_dst, mem_var, str(Path(mem_var) / "include")): + Path(d).mkdir(parents=True, exist_ok=True) + src = [str(Path(lib_src) / x) for x in os.listdir(lib_src)] src = [folder for folder in src if not os.path.isfile(folder)] # folders only for folder in src: - files = [join(folder,x) for x in os.listdir(folder)] + files = [str(Path(folder) / x) for x in os.listdir(folder)] for file in files: if file.strip().endswith(".a"): - shutil.copyfile(file,join(lib_dst,file.split(os.path.sep)[-1])) + shutil.copyfile(file, str(Path(lib_dst) / file.split(os.path.sep)[-1])) - shutil.move(join(lib_dst,"libspi_flash.a"),join(mem_var,"libspi_flash.a")) - shutil.move(join(env_build,"memory.ld"),join(ld_dst,"memory.ld")) + _replace_move(str(Path(lib_dst) / "libspi_flash.a"), str(Path(mem_var) / "libspi_flash.a")) + _replace_move(str(Path(env_build) / "memory.ld"), str(Path(ld_dst) / "memory.ld")) if mcu == "esp32s3": - shutil.move(join(lib_dst,"libesp_psram.a"),join(mem_var,"libesp_psram.a")) - shutil.move(join(lib_dst,"libesp_system.a"),join(mem_var,"libesp_system.a")) - shutil.move(join(lib_dst,"libfreertos.a"),join(mem_var,"libfreertos.a")) - shutil.move(join(lib_dst,"libbootloader_support.a"),join(mem_var,"libbootloader_support.a")) - shutil.move(join(lib_dst,"libesp_hw_support.a"),join(mem_var,"libesp_hw_support.a")) - shutil.move(join(lib_dst,"libesp_lcd.a"),join(mem_var,"libesp_lcd.a")) - - shutil.copyfile(sdkconfig_h_path,join(mem_var,"include","sdkconfig.h")) - if not bool(os.path.isfile(join(arduino_libs,mcu,"sdkconfig.orig"))): - shutil.move(join(arduino_libs,mcu,"sdkconfig"),join(arduino_libs,mcu,"sdkconfig.orig")) - shutil.copyfile(join(env.subst("$PROJECT_DIR"),"sdkconfig."+env["PIOENV"]),join(arduino_libs,mcu,"sdkconfig")) - shutil.copyfile(join(env.subst("$PROJECT_DIR"),"sdkconfig."+env["PIOENV"]),join(arduino_libs,"sdkconfig")) + _replace_move(str(Path(lib_dst) / "libesp_psram.a"), str(Path(mem_var) / "libesp_psram.a")) + _replace_move(str(Path(lib_dst) / "libesp_system.a"), str(Path(mem_var) / "libesp_system.a")) + _replace_move(str(Path(lib_dst) / "libfreertos.a"), str(Path(mem_var) / "libfreertos.a")) + _replace_move(str(Path(lib_dst) / "libbootloader_support.a"), str(Path(mem_var) / "libbootloader_support.a")) + _replace_move(str(Path(lib_dst) / "libesp_hw_support.a"), str(Path(mem_var) / "libesp_hw_support.a")) + _replace_move(str(Path(lib_dst) / "libesp_lcd.a"), str(Path(mem_var) / "libesp_lcd.a")) + + shutil.copyfile(sdkconfig_h_path, str(Path(mem_var) / "include" / "sdkconfig.h")) + if not bool(os.path.isfile(str(Path(arduino_libs) / mcu / "sdkconfig.orig"))): + shutil.move(str(Path(arduino_libs) / mcu / "sdkconfig"), str(Path(arduino_libs) / mcu / "sdkconfig.orig")) + shutil.copyfile(str(Path(env.subst("$PROJECT_DIR")) / ("sdkconfig." + env["PIOENV"])), str(Path(arduino_libs) / mcu / "sdkconfig")) + shutil.copyfile(str(Path(env.subst("$PROJECT_DIR")) / ("sdkconfig." + env["PIOENV"])), str(Path(arduino_libs) / "sdkconfig")) try: - os.remove(join(env.subst("$PROJECT_DIR"),"dependencies.lock")) - os.remove(join(env.subst("$PROJECT_DIR"),"CMakeLists.txt")) - except: + os.remove(str(Path(env.subst("$PROJECT_DIR")) / "dependencies.lock")) + os.remove(str(Path(env.subst("$PROJECT_DIR")) / "CMakeLists.txt")) + except FileNotFoundError: pass + except OSError as e: + print(f"Warning: cleanup failed: {e}") print("*** Copied compiled %s IDF libraries to Arduino framework ***" % idf_variant) PYTHON_EXE = env.subst("$PYTHONEXE") - pio_exe_path = os.path.join(os.path.dirname(PYTHON_EXE), "pio" + (".exe" if IS_WINDOWS else "")) + pio_exe_path = str(Path(os.path.dirname(PYTHON_EXE)) / ("pio" + (".exe" if IS_WINDOWS else ""))) pio_cmd = env["PIOENV"] env.Execute( env.VerboseAction( @@ -2163,9 +2154,9 @@ def idf_lib_copy(source, target, env): ) if flag_custom_component_add == True or flag_custom_component_remove == True: try: - shutil.copy(join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml.orig"),join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml")) + shutil.copy(str(Path(ARDUINO_FRAMEWORK_DIR) / "idf_component.yml.orig"), str(Path(ARDUINO_FRAMEWORK_DIR) / "idf_component.yml")) print("*** Original Arduino \"idf_component.yml\" restored ***") - except: + except (FileNotFoundError, PermissionError, OSError): print("*** Original Arduino \"idf_component.yml\" couldnt be restored ***") # Restore original pioarduino-build.py from component_manager import ComponentManager @@ -2177,17 +2168,19 @@ def idf_lib_copy(source, target, env): if "espidf" in env.subst("$PIOFRAMEWORK") and (flag_custom_component_add == True or flag_custom_component_remove == True): def idf_custom_component(source, target, env): try: - shutil.copy(join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml.orig"),join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml")) - print("*** Original Arduino \"idf_component.yml\" restored ***") - except: + if "arduino" in env.subst("$PIOFRAMEWORK"): + shutil.copy(str(Path(ARDUINO_FRAMEWORK_DIR) / "idf_component.yml.orig"), + str(Path(ARDUINO_FRAMEWORK_DIR) / "idf_component.yml")) + print("*** Original Arduino \"idf_component.yml\" restored ***") + except (FileNotFoundError, PermissionError, OSError): try: - shutil.copy(join(PROJECT_SRC_DIR,"idf_component.yml.orig"),join(PROJECT_SRC_DIR,"idf_component.yml")) + shutil.copy(str(Path(PROJECT_SRC_DIR) / "idf_component.yml.orig"), str(Path(PROJECT_SRC_DIR) / "idf_component.yml")) print("*** Original \"idf_component.yml\" restored ***") - except: # no "idf_component.yml" in source folder + except (FileNotFoundError, PermissionError, OSError): # no "idf_component.yml" in source folder try: - os.remove(join(PROJECT_SRC_DIR,"idf_component.yml")) + os.remove(str(Path(PROJECT_SRC_DIR) / "idf_component.yml")) print("*** pioarduino generated \"idf_component.yml\" removed ***") - except: + except (FileNotFoundError, PermissionError, OSError): print("*** no custom \"idf_component.yml\" found for removing ***") if "arduino" in env.subst("$PIOFRAMEWORK"): # Restore original pioarduino-build.py, only used with Arduino @@ -2196,6 +2189,7 @@ def idf_custom_component(source, target, env): component_manager.restore_pioarduino_build_py() silent_action = create_silent_action(idf_custom_component) env.AddPostAction("checkprogsize", silent_action) + # # Process OTA partition and image # @@ -2208,9 +2202,9 @@ def idf_custom_component(source, target, env): if ota_partition_params["size"] and ota_partition_params["offset"]: # Generate an empty image if OTA is enabled in partition table - ota_partition_image = os.path.join("$BUILD_DIR", "ota_data_initial.bin") + ota_partition_image = str(Path("$BUILD_DIR") / "ota_data_initial.bin") if "arduino" in env.subst("$PIOFRAMEWORK"): - ota_partition_image = os.path.join(ARDUINO_FRAMEWORK_DIR, "tools", "partitions", "boot_app0.bin") + ota_partition_image = str(Path(ARDUINO_FRAMEWORK_DIR) / "tools" / "partitions" / "boot_app0.bin") else: generate_empty_partition_image(ota_partition_image, ota_partition_params["size"]) @@ -2224,12 +2218,12 @@ def idf_custom_component(source, target, env): ) ] ) - EXTRA_IMG_DIR = join(env.subst("$PROJECT_DIR"), "variants", "tasmota") - env.Append( - FLASH_EXTRA_IMAGES=[ - (offset, join(EXTRA_IMG_DIR, img)) for offset, img in board.get("upload.arduino.flash_extra_images", []) - ] - ) + extra_imgs = board.get("upload.arduino.flash_extra_images", []) + if extra_imgs: + extra_img_dir = Path(env.subst("$PROJECT_DIR")) / "variants" / "tasmota" + env.Append( + FLASH_EXTRA_IMAGES=[(offset, str(extra_img_dir / img)) for offset, img in extra_imgs] + ) def _parse_size(value): if isinstance(value, int): diff --git a/builder/frameworks/ulp.py b/builder/frameworks/ulp.py index 7b989eb0e..be7828e93 100644 --- a/builder/frameworks/ulp.py +++ b/builder/frameworks/ulp.py @@ -14,6 +14,7 @@ import os import sys +from pathlib import Path from platformio import fs from platformio.util import get_systype @@ -27,9 +28,7 @@ platform = ulp_env.PioPlatform() FRAMEWORK_DIR = platform.get_package_dir("framework-espidf") BUILD_DIR = ulp_env.subst("$BUILD_DIR") -ULP_BUILD_DIR = os.path.join( - BUILD_DIR, "esp-idf", project_config["name"].replace("__idf_", ""), "ulp_main" -) +ULP_BUILD_DIR = str(Path(BUILD_DIR) / "esp-idf" / project_config["name"].replace("__idf_", "") / "ulp_main") is_xtensa = idf_variant in ("esp32", "esp32s2", "esp32s3") @@ -52,7 +51,7 @@ def prepare_ulp_env_vars(env): toolchain_path, toolchain_path_ulp, platform.get_package_dir("tool-ninja"), - os.path.join(platform.get_package_dir("tool-cmake"), "bin"), + str(Path(platform.get_package_dir("tool-cmake")) / "bin"), ] for package in additional_packages: @@ -62,8 +61,8 @@ def prepare_ulp_env_vars(env): def collect_ulp_sources(): return [ - os.path.join(ulp_env.subst("$PROJECT_DIR"), "ulp", f) - for f in os.listdir(os.path.join(ulp_env.subst("$PROJECT_DIR"), "ulp")) + str(Path(ulp_env.subst("$PROJECT_DIR")) / "ulp" / f) + for f in os.listdir(str(Path(ulp_env.subst("$PROJECT_DIR")) / "ulp")) if f.endswith((".c", ".S", ".s")) ] @@ -78,7 +77,7 @@ def get_component_includes(target_config): ] ] - return [os.path.join(BUILD_DIR, "config")] + return [str(Path(BUILD_DIR) / "config")] def generate_ulp_config(target_config): @@ -99,31 +98,25 @@ def _generate_ulp_configuration_action(env, target, source): comp_includes = ";".join(comp_includes_list + plain_includes_list) cmd = ( - os.path.join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), + str(Path(platform.get_package_dir("tool-cmake")) / "bin" / "cmake"), "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "-DCMAKE_GENERATOR=Ninja", "-DCMAKE_TOOLCHAIN_FILE=" - + os.path.join( - FRAMEWORK_DIR, - "components", - "ulp", - "cmake", - ulp_toolchain, - ), + + str(Path(FRAMEWORK_DIR) / "components" / "ulp" / "cmake" / ulp_toolchain), "-DULP_S_SOURCES=%s" % ";".join([fs.to_unix_path(s.get_abspath()) for s in source]), "-DULP_APP_NAME=ulp_main", - "-DCOMPONENT_DIR=" + os.path.join(ulp_env.subst("$PROJECT_DIR"), "ulp"), + "-DCOMPONENT_DIR=" + str(Path(ulp_env.subst("$PROJECT_DIR")) / "ulp"), "-DCOMPONENT_INCLUDES=%s" % comp_includes, "-DIDF_TARGET=%s" % idf_variant, "-DIDF_PATH=" + fs.to_unix_path(FRAMEWORK_DIR), - "-DSDKCONFIG_HEADER=" + os.path.join(BUILD_DIR, "config", "sdkconfig.h"), + "-DSDKCONFIG_HEADER=" + str(Path(BUILD_DIR) / "config" / "sdkconfig.h"), "-DPYTHON=" + env.subst("$PYTHONEXE"), - "-DSDKCONFIG_CMAKE=" + os.path.join(BUILD_DIR, "config", "sdkconfig.cmake"), - "-DCMAKE_MODULE_PATH=" + fs.to_unix_path(os.path.join(FRAMEWORK_DIR, "components", "ulp", "cmake")), + "-DSDKCONFIG_CMAKE=" + str(Path(BUILD_DIR) / "config" / "sdkconfig.cmake"), + "-DCMAKE_MODULE_PATH=" + fs.to_unix_path(str(Path(FRAMEWORK_DIR) / "components" / "ulp" / "cmake")), "-GNinja", "-B", ULP_BUILD_DIR, - os.path.join(FRAMEWORK_DIR, "components", "ulp", "cmake"), + str(Path(FRAMEWORK_DIR) / "components" / "ulp" / "cmake"), ) result = exec_command(cmd) @@ -135,7 +128,7 @@ def _generate_ulp_configuration_action(env, target, source): ulp_sources.sort() return ulp_env.Command( - os.path.join(ULP_BUILD_DIR, "build.ninja"), + str(Path(ULP_BUILD_DIR) / "build.ninja"), ulp_sources, ulp_env.VerboseAction( _generate_ulp_configuration_action, "Generating ULP configuration" @@ -145,7 +138,7 @@ def _generate_ulp_configuration_action(env, target, source): def compile_ulp_binary(): cmd = ( - os.path.join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), + str(Path(platform.get_package_dir("tool-cmake")) / "bin" / "cmake"), "--build", ULP_BUILD_DIR, "--target", @@ -159,9 +152,9 @@ def compile_ulp_binary(): return ulp_binary_env.Command( [ - os.path.join(ULP_BUILD_DIR, "ulp_main.h"), - os.path.join(ULP_BUILD_DIR, "ulp_main.ld"), - os.path.join(ULP_BUILD_DIR, "ulp_main.bin"), + str(Path(ULP_BUILD_DIR) / "ulp_main.h"), + str(Path(ULP_BUILD_DIR) / "ulp_main.ld"), + str(Path(ULP_BUILD_DIR) / "ulp_main.bin"), ], None, ulp_binary_env.VerboseAction(" ".join(cmd), "Generating ULP project files $TARGETS"), @@ -170,19 +163,17 @@ def compile_ulp_binary(): def generate_ulp_assembly(): cmd = ( - os.path.join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), + str(Path(platform.get_package_dir("tool-cmake")) / "bin" / "cmake"), "-DDATA_FILE=$SOURCE", "-DSOURCE_FILE=$TARGET", "-DFILE_TYPE=BINARY", "-P", - os.path.join( - FRAMEWORK_DIR, "tools", "cmake", "scripts", "data_file_embed_asm.cmake" - ), + str(Path(FRAMEWORK_DIR) / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake"), ) return ulp_env.Command( - os.path.join(BUILD_DIR, "ulp_main.bin.S"), - os.path.join(ULP_BUILD_DIR, "ulp_main.bin"), + str(Path(BUILD_DIR) / "ulp_main.bin.S"), + str(Path(ULP_BUILD_DIR) / "ulp_main.bin"), ulp_env.VerboseAction(" ".join(cmd), "Generating ULP assembly file $TARGET"), ) @@ -191,7 +182,7 @@ def generate_ulp_assembly(): ulp_assembly = generate_ulp_assembly() ulp_env.Depends(compile_ulp_binary(), generate_ulp_config(project_config)) -ulp_env.Depends(os.path.join("$BUILD_DIR", "${PROGNAME}.elf"), ulp_assembly) -ulp_env.Requires(os.path.join("$BUILD_DIR", "${PROGNAME}.elf"), ulp_assembly) +ulp_env.Depends(str(Path("$BUILD_DIR") / "${PROGNAME}.elf"), ulp_assembly) +ulp_env.Requires(str(Path("$BUILD_DIR") / "${PROGNAME}.elf"), ulp_assembly) env.AppendUnique(CPPPATH=ULP_BUILD_DIR, LIBPATH=ULP_BUILD_DIR) diff --git a/builder/main.py b/builder/main.py index c20636acf..4857b30d3 100644 --- a/builder/main.py +++ b/builder/main.py @@ -19,6 +19,7 @@ import subprocess import sys from os.path import isfile, join +from pathlib import Path from SCons.Script import ( ARGUMENTS, @@ -464,8 +465,8 @@ def switch_off_ldf(): SIZECHECKCMD="$SIZETOOL -A -d $SOURCES", SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES", ERASEFLAGS=["--chip", mcu, "--port", '"$UPLOAD_PORT"'], - ERASECMD='$OBJCOPY $ERASEFLAGS erase-flash', - # mkspiffs package contains two different binaries for IDF and Arduino + ERASETOOL=uploader_path, + ERASECMD='$ERASETOOL $ERASEFLAGS erase-flash', MKFSTOOL="mk%s" % filesystem + ( ( @@ -510,7 +511,7 @@ def switch_off_ldf(): action=env.VerboseAction( " ".join( [ - "$OBJCOPY", + "$ERASETOOL", "--chip", mcu, "elf2image", @@ -521,8 +522,8 @@ def switch_off_ldf(): "--flash-size", board.get("upload.flash_size", "4MB"), "-o", - "$TARGET", - "$SOURCES", + "\"$TARGET\"", + "\"$SOURCES\"", ] ), "Building $TARGET", @@ -572,12 +573,12 @@ def firmware_metrics(target, source, env): print("Firmware metrics can not be shown. Set the terminal codepage to \"utf-8\"") return - map_file = os.path.join(env.subst("$BUILD_DIR"), env.subst("$PROGNAME") + ".map") - if not os.path.isfile(map_file): + map_file = str(Path(env.subst("$BUILD_DIR")) / (env.subst("$PROGNAME") + ".map")) + if not Path(map_file).is_file(): # map file can be in project dir - map_file = os.path.join(get_project_dir(), env.subst("$PROGNAME") + ".map") + map_file = str(Path(get_project_dir()) / (env.subst("$PROGNAME") + ".map")) - if not os.path.isfile(map_file): + if not Path(map_file).is_file(): print(f"Error: Map file not found: {map_file}") print("Make sure the project is built first with 'pio run'") return @@ -619,7 +620,7 @@ def firmware_metrics(target, source, env): print("Check your Python installation.") except Exception as e: print(f"Error: Failed to run firmware metrics: {e}") - print("Make sure esp-idf-size is installed: pip install esp-idf-size") + print(f'Make sure esp-idf-size is installed: uv pip install --python "{PYTHON_EXE}" esp-idf-size') # @@ -628,12 +629,12 @@ def firmware_metrics(target, source, env): target_elf = None if "nobuild" in COMMAND_LINE_TARGETS: - target_elf = join("$BUILD_DIR", "${PROGNAME}.elf") + target_elf = str(Path("$BUILD_DIR") / "${PROGNAME}.elf") if set(["uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS): fetch_fs_size(env) - target_firm = join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}.bin") + target_firm = str(Path("$BUILD_DIR") / "${ESP32_FS_IMAGE_NAME}.bin") else: - target_firm = join("$BUILD_DIR", "${PROGNAME}.bin") + target_firm = str(Path("$BUILD_DIR") / "${PROGNAME}.bin") else: target_elf = env.BuildProgram() silent_action = env.Action(firmware_metrics) @@ -642,12 +643,12 @@ def firmware_metrics(target, source, env): env.AddPostAction(target_elf, silent_action) if set(["buildfs", "uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS): target_firm = env.DataToBin( - join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR" + str(Path("$BUILD_DIR") / "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR" ) env.NoCache(target_firm) AlwaysBuild(target_firm) else: - target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf) + target_firm = env.ElfToBin(str(Path("$BUILD_DIR") / "${PROGNAME}"), target_elf) env.Depends(target_firm, "checkprogsize") # Configure platform targets @@ -705,7 +706,7 @@ def firmware_metrics(target, source, env): "espressif32.html#over-the-air-ota-update\n" ) env.Replace( - UPLOADER=join(FRAMEWORK_DIR, "tools", "espota.py"), + UPLOADER=str(Path(FRAMEWORK_DIR).resolve() / "tools" / "espota.py"), UPLOADERFLAGS=["--debug", "--progress", "-i", "$UPLOAD_PORT"], UPLOADCMD=f'"{PYTHON_EXE}" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE', ) @@ -782,8 +783,8 @@ def firmware_metrics(target, source, env): upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")] env.Replace( - UPLOADER=join( - platform.get_package_dir("tool-dfuutil-arduino") or "", "dfu-util" + UPLOADER=str( + Path(platform.get_package_dir("tool-dfuutil-arduino")).resolve() / "dfu-util" ), UPLOADERFLAGS=[ "-d", @@ -791,7 +792,7 @@ def firmware_metrics(target, source, env): "-Q", "-D", ], - UPLOADCMD='$UPLOADER $UPLOADERFLAGS "$SOURCE"', + UPLOADCMD='"$UPLOADER" $UPLOADERFLAGS "$SOURCE"', ) # Configure upload protocol: Debug tools (OpenOCD) diff --git a/builder/penv_setup.py b/builder/penv_setup.py index 4909b5a72..f04dc18b9 100644 --- a/builder/penv_setup.py +++ b/builder/penv_setup.py @@ -20,6 +20,7 @@ import subprocess import sys import socket +from pathlib import Path from platformio.package.version import pepver_to_semver from platformio.compat import IS_WINDOWS @@ -77,7 +78,7 @@ def get_executable_path(penv_dir, executable_name): exe_suffix = ".exe" if IS_WINDOWS else "" scripts_dir = "Scripts" if IS_WINDOWS else "bin" - return os.path.join(penv_dir, scripts_dir, f"{executable_name}{exe_suffix}") + return str(Path(penv_dir) / scripts_dir / f"{executable_name}{exe_suffix}") def setup_pipenv_in_package(env, penv_dir): @@ -97,7 +98,7 @@ def setup_pipenv_in_package(env, penv_dir): python_exe = env.subst("$PYTHONEXE") python_dir = os.path.dirname(python_exe) uv_exe_suffix = ".exe" if IS_WINDOWS else "" - uv_cmd = os.path.join(python_dir, f"uv{uv_exe_suffix}") + uv_cmd = str(Path(python_dir) / f"uv{uv_exe_suffix}") # Fall back to system uv if derived path doesn't exist if not os.path.isfile(uv_cmd): @@ -141,8 +142,8 @@ def setup_python_paths(penv_dir): # Add site-packages directory python_ver = f"python{sys.version_info.major}.{sys.version_info.minor}" site_packages = ( - os.path.join(penv_dir, "Lib", "site-packages") if IS_WINDOWS - else os.path.join(penv_dir, "lib", python_ver, "site-packages") + str(Path(penv_dir) / "Lib" / "site-packages") if IS_WINDOWS + else str(Path(penv_dir) / "lib" / python_ver / "site-packages") ) if os.path.isdir(site_packages): @@ -423,7 +424,7 @@ def setup_python_environment(env, platform, platformio_dir): ) sys.exit(1) - penv_dir = os.path.join(platformio_dir, "penv") + penv_dir = str(Path(platformio_dir) / "penv") # Setup virtual environment if needed used_uv_executable = setup_pipenv_in_package(env, penv_dir) diff --git a/examples/arduino-blink/platformio.ini b/examples/arduino-blink/platformio.ini index dd606fe47..b290307cf 100644 --- a/examples/arduino-blink/platformio.ini +++ b/examples/arduino-blink/platformio.ini @@ -13,24 +13,31 @@ framework = arduino board = esp32-solo1 build_flags = -DLED_BUILTIN=2 lib_ignore = wifi + ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker custom_sdkconfig = - '# CONFIG_ZB_ENABLED is not set' -custom_component_remove = - espressif/esp_hosted - espressif/esp_wifi_remote - espressif/esp-dsp - espressif/esp32-camera - espressif/libsodium - espressif/esp-modbus - espressif/qrcode - espressif/esp_insights - espressif/esp_diag_data_store - espressif/esp_diagnostics - espressif/esp_rainmaker - espressif/rmaker_common + '# CONFIG_ZB_ENABLED is not set' +custom_component_remove = espressif/esp_hosted + espressif/esp_wifi_remote + espressif/network_provisioning + espressif/mdns + espressif/esp-dsp + espressif/esp_modem + espressif/esp32-camera + espressif/libsodium + espressif/esp-modbus + espressif/qrcode + espressif/esp_insights + espressif/esp_diag_data_store + espressif/esp_diagnostics + espressif/cbor + espressif/esp_rainmaker + espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32-c2-devkitm-1] platform = espressif32 @@ -38,14 +45,20 @@ framework = arduino board = esp32-c2-devkitm-1 monitor_speed = 115200 lib_ignore = wifi + ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker custom_sdkconfig = - '# CONFIG_ZB_ENABLED is not set' + '# CONFIG_ZB_ENABLED is not set' custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning + espressif/mdns espressif/esp-dsp + espressif/esp_modem espressif/esp32-camera espressif/libsodium espressif/esp-modbus @@ -53,8 +66,10 @@ custom_component_remove = espressif/esp_hosted espressif/esp_insights espressif/esp_diag_data_store espressif/esp_diagnostics + espressif/cbor espressif/esp_rainmaker espressif/rmaker_common + chmorgan/esp-libhelix-mp3 custom_component_add = espressif/cmake_utilities @ 0.* [env:esp32-s3-arduino_nano_esp32] @@ -63,12 +78,18 @@ framework = arduino board = arduino_nano_esp32 monitor_speed = 115200 lib_ignore = wifi + ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning + espressif/mdns espressif/esp-dsp + espressif/esp_modem espressif/esp32-camera espressif/libsodium espressif/esp-modbus @@ -76,18 +97,23 @@ custom_component_remove = espressif/esp_hosted espressif/esp_insights espressif/esp_diag_data_store espressif/esp_diagnostics + espressif/cbor espressif/esp_rainmaker espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32s3-qio_opi_per] ; OPI Performance settings -> Display use platform = espressif32 framework = arduino board = esp32s3_120_16_8-qio_opi -lib_ignore = +lib_ignore = wifi + ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker custom_sdkconfig = CONFIG_SPIRAM_MODE_OCT=y CONFIG_SPIRAM_SPEED_120M=y CONFIG_LCD_RGB_ISR_IRAM_SAFE=y @@ -101,14 +127,23 @@ custom_sdkconfig = CONFIG_SPIRAM_MODE_OCT=y CONFIG_ESP32S3_DATA_CACHE_64KB=y CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y '# CONFIG_ZB_ENABLED is not set' -custom_component_remove = espressif/esp_hosted - espressif/esp_wifi_remote - espressif/qrcode - espressif/esp_insights - espressif/esp_diag_data_store - espressif/esp_diagnostics - espressif/esp_rainmaker - espressif/rmaker_common +custom_component_remove = espressif/esp_hosted + espressif/esp_wifi_remote + espressif/network_provisioning + espressif/mdns + espressif/esp-dsp + espressif/esp_modem + espressif/esp32-camera + espressif/libsodium + espressif/esp-modbus + espressif/qrcode + espressif/esp_insights + espressif/esp_diag_data_store + espressif/esp_diagnostics + espressif/cbor + espressif/esp_rainmaker + espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32-c6-devkitc-1] platform = espressif32 @@ -117,31 +152,58 @@ build_type = debug board = esp32-c6-devkitc-1 monitor_speed = 115200 lib_ignore = wifi + ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem espressif/esp32-camera + espressif/libsodium + espressif/esp-modbus + espressif/qrcode + espressif/esp_insights + espressif/esp_diag_data_store + espressif/esp_diagnostics + espressif/cbor + espressif/esp_rainmaker + espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32-h2-devkitm-1] platform = espressif32 framework = arduino board = esp32-h2-devkitm-1 monitor_speed = 115200 -lib_ignore = +lib_ignore = ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem espressif/esp32-camera + espressif/libsodium + espressif/esp-modbus + espressif/qrcode + espressif/esp_insights + espressif/esp_diag_data_store + espressif/esp_diagnostics + espressif/cbor + espressif/esp_rainmaker + espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32-p4] platform = espressif32 @@ -149,13 +211,27 @@ framework = arduino board = esp32-p4 build_flags = -DLED_BUILTIN=2 lib_ignore = wifi + ble spiffs NetworkClientSecure + Matter Zigbee + ESP RainMaker monitor_speed = 115200 custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem espressif/esp32-camera + espressif/libsodium + espressif/esp-modbus + espressif/qrcode + espressif/esp_insights + espressif/esp_diag_data_store + espressif/esp_diagnostics + espressif/cbor + espressif/esp_rainmaker + espressif/rmaker_common + chmorgan/esp-libhelix-mp3 diff --git a/examples/arduino-rmt-blink/platformio.ini b/examples/arduino-rmt-blink/platformio.ini index 699eef90b..0973eaa58 100644 --- a/examples/arduino-rmt-blink/platformio.ini +++ b/examples/arduino-rmt-blink/platformio.ini @@ -2,9 +2,13 @@ platform = espressif32 framework = arduino board = esp32-s2-saola-1 -lib_ignore = wifi +lib_ignore = ble + wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=18 -DNR_OF_LEDS=1 @@ -12,9 +16,13 @@ build_flags = -DBUILTIN_RGBLED_PIN=18 platform = espressif32 framework = arduino board = esp32-s3-devkitc-1 -lib_ignore = wifi +lib_ignore = ble + wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=48 -DNR_OF_LEDS=1 @@ -22,9 +30,13 @@ build_flags = -DBUILTIN_RGBLED_PIN=48 platform = espressif32 framework = arduino board = esp32-c3-devkitm-1 -lib_ignore = wifi +lib_ignore = ble + wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=8 -DNR_OF_LEDS=1 @@ -32,6 +44,13 @@ build_flags = -DBUILTIN_RGBLED_PIN=8 platform = espressif32 framework = arduino board = esp32-c5-devkitc-1 +lib_ignore = ble + wifi + ESP RainMaker + ESP Insights + spiffs + NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=27 -DNR_OF_LEDS=1 @@ -39,8 +58,12 @@ build_flags = -DBUILTIN_RGBLED_PIN=27 platform = espressif32 framework = arduino board = esp32-c6-devkitm-1 -lib_ignore = wifi +lib_ignore = ble + wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=8 -DNR_OF_LEDS=1