From 7a0aa2592258bbc6f176bde0dd33f2a71938378d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 3 Sep 2025 19:20:33 +0200 Subject: [PATCH 01/38] refactor espidf.py with path --- builder/frameworks/espidf.py | 612 ++++++++++++++++------------------- 1 file changed, 282 insertions(+), 330 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 9902237c1..2e5116544 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,14 @@ env.SConscript("_embed_files.py", exports="env") platform = env.PioPlatform() -_penv_setup_file = os.path.join(platform.get_dir(), "builder", "penv_setup.py") +_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] 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 +79,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 +93,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 +105,59 @@ 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" @@ -193,7 +211,7 @@ def load_custom_sdkconfig_file(): 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) + file_path = str(Path(PROJECT_DIR) / filename) if os.path.exists(file_path): try: with open(file_path, 'r') as f: @@ -264,8 +282,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 +295,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 +313,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 +376,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 +407,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 +428,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 +449,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 +469,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 +491,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,8 +528,9 @@ 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 return codemodel @@ -512,10 +538,14 @@ def get_cmake_code_model(src_dir, build_dir, extra_args=None): 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()), ] @@ -531,7 +561,7 @@ def populate_idf_env_vars(idf_env): 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 +589,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 +686,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 +743,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 +758,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 +770,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 +792,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 +801,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,7 +836,7 @@ 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) @@ -880,31 +904,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 +935,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 +1011,28 @@ 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: + 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: if not os.path.isabs(source["path"]): - obj_path = os.path.join(obj_path, source["path"]) + obj_path = str(Path(obj_path) / source["path"]) else: - obj_path = os.path.join(obj_path, os.path.basename(src_path)) + 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 +1045,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 +1073,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 +1089,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", @@ -1126,12 +1131,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 +1143,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 +1153,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 +1193,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 +1250,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 +1282,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 +1306,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 +1319,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 +1339,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 +1396,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", - ), + CMAKE_DIR, + "-DCC=%s" % 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" - ), + "-DCONFIG_DIR=%s" % str(Path(BUILD_DIR) / "config"), + "-DLD_DIR=%s" % str(Path(FRAMEWORK_DIR) / "components" / "esp_system" / "ld"), "-P", - os.path.join( - "$BUILD_DIR", - "esp-idf", - "esp_system", - "ld", - "linker_script_generator.cmake", - ), + str(Path("$BUILD_DIR") / "esp-idf" / "esp_system" / "ld" / "linker_script_generator.cmake"), ] ), "Generating LD script $TARGET", @@ -1435,25 +1412,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 +1442,17 @@ 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") + bundle_path = str(Path("$BUILD_DIR") / "x509_crt_bundle") env.Execute( env.VerboseAction( " ".join( [ - os.path.join( - env.PioPlatform().get_package_dir("tool-cmake"), - "bin", - "cmake", - ), + CMAKE_DIR, "-DDATA_FILE=" + bundle_path, "-DSOURCE_FILE=%s.S" % bundle_path, "-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"), ] ), "Generating assembly for certificate bundle...", @@ -1496,7 +1461,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 +1483,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 +1538,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 +1576,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 +1603,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 +1621,11 @@ 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 ""), - ) + python_exe_path = str(Path(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 - ) + 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 +1652,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 +1698,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 +1706,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 +1724,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 +1734,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 @@ -1820,7 +1757,7 @@ def get_python_exe(): 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() @@ -1915,7 +1852,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 +1863,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 +1879,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 +1925,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 +1939,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 +2035,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 +2045,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 +2112,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 +2126,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 +2147,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 +2160,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,10 +2176,10 @@ def idf_custom_component(source, target, env): ) ] ) - EXTRA_IMG_DIR = join(env.subst("$PROJECT_DIR"), "variants", "tasmota") + EXTRA_IMG_DIR = str(Path(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", []) + (offset, str(Path(EXTRA_IMG_DIR) / img)) for offset, img in board.get("upload.arduino.flash_extra_images", []) ] ) From 7283c55946cf2c688b9b6918d6430651339e9509 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 09:45:35 +0200 Subject: [PATCH 02/38] Fix cache check for Arduino libraries --- builder/frameworks/component_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index d775816c1..ebb70640d 100644 --- a/builder/frameworks/component_manager.py +++ b/builder/frameworks/component_manager.py @@ -837,7 +837,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() From 41ef65acdf749b05cfebf5c6bb3b7b6fd3fe2ac2 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 09:57:19 +0200 Subject: [PATCH 03/38] Update lib_ignore to exclude 'ble' for ESP32 boards --- examples/arduino-rmt-blink/platformio.ini | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/arduino-rmt-blink/platformio.ini b/examples/arduino-rmt-blink/platformio.ini index 699eef90b..02b23182a 100644 --- a/examples/arduino-rmt-blink/platformio.ini +++ b/examples/arduino-rmt-blink/platformio.ini @@ -2,7 +2,8 @@ platform = espressif32 framework = arduino board = esp32-s2-saola-1 -lib_ignore = wifi +lib_ignore = ble + wifi spiffs NetworkClientSecure build_flags = -DBUILTIN_RGBLED_PIN=18 @@ -12,7 +13,8 @@ build_flags = -DBUILTIN_RGBLED_PIN=18 platform = espressif32 framework = arduino board = esp32-s3-devkitc-1 -lib_ignore = wifi +lib_ignore = ble + wifi spiffs NetworkClientSecure build_flags = -DBUILTIN_RGBLED_PIN=48 @@ -22,7 +24,8 @@ build_flags = -DBUILTIN_RGBLED_PIN=48 platform = espressif32 framework = arduino board = esp32-c3-devkitm-1 -lib_ignore = wifi +lib_ignore = ble + wifi spiffs NetworkClientSecure build_flags = -DBUILTIN_RGBLED_PIN=8 @@ -32,6 +35,10 @@ build_flags = -DBUILTIN_RGBLED_PIN=8 platform = espressif32 framework = arduino board = esp32-c5-devkitc-1 +lib_ignore = ble + wifi + spiffs + NetworkClientSecure build_flags = -DBUILTIN_RGBLED_PIN=27 -DNR_OF_LEDS=1 @@ -39,7 +46,8 @@ build_flags = -DBUILTIN_RGBLED_PIN=27 platform = espressif32 framework = arduino board = esp32-c6-devkitm-1 -lib_ignore = wifi +lib_ignore = ble + wifi spiffs NetworkClientSecure build_flags = -DBUILTIN_RGBLED_PIN=8 From 7f1b4e1ac9890452c404048ab113f7e4516d7829 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 10:48:39 +0200 Subject: [PATCH 04/38] Update platformio.ini to ignore 'ble' library --- examples/arduino-blink/platformio.ini | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/examples/arduino-blink/platformio.ini b/examples/arduino-blink/platformio.ini index dd606fe47..e97c6a792 100644 --- a/examples/arduino-blink/platformio.ini +++ b/examples/arduino-blink/platformio.ini @@ -13,9 +13,12 @@ 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 = @@ -38,11 +41,14 @@ 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/esp-dsp @@ -63,9 +69,12 @@ 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/esp-dsp @@ -84,10 +93,13 @@ custom_component_remove = espressif/esp_hosted 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 @@ -117,9 +129,12 @@ 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/mdns @@ -132,10 +147,12 @@ 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/mdns @@ -149,9 +166,12 @@ 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 From 56104c90a6f380fc2ccda90837d89ff2474d089e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 12:10:50 +0200 Subject: [PATCH 05/38] Add Arduino TinyUSB library support in component manager --- builder/frameworks/component_manager.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index ebb70640d..a4bdab182 100644 --- a/builder/frameworks/component_manager.py +++ b/builder/frameworks/component_manager.py @@ -577,7 +577,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 = [] @@ -808,7 +810,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 From 13e7ee6c7173573669cafa39dde0df96ff8b4061 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 12:28:18 +0200 Subject: [PATCH 06/38] remove assert from code model check --- builder/frameworks/espidf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 2e5116544..77b709d1b 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -532,7 +532,9 @@ def get_cmake_code_model(src_dir, build_dir, extra_args=None): codemodel = json.load(fp) break - assert codemodel["version"]["major"] == 2 + if codemodel.get("version", {}).get("major") != 2: + sys.stderr.write("Error: Unsupported CMake codemodel major version\n") + env.Exit(1) return codemodel @@ -1447,7 +1449,7 @@ def generate_mbedtls_bundle(sdk_config): env.VerboseAction( " ".join( [ - CMAKE_DIR, + f'"{CMAKE_DIR}"', "-DDATA_FILE=" + bundle_path, "-DSOURCE_FILE=%s.S" % bundle_path, "-DFILE_TYPE=BINARY", From d762d39e796c67a7a0b5afe0109d339c33e72ed0 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 12:33:18 +0200 Subject: [PATCH 07/38] remove another assert --- builder/frameworks/espidf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 77b709d1b..543a38c40 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1750,9 +1750,9 @@ 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") From 1985e3109b1b042380f0ea56850f95e0c3df40f6 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 12:37:25 +0200 Subject: [PATCH 08/38] Remove unsetting of IDF_TOOLS_PATH in espidf.py --- builder/frameworks/espidf.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 543a38c40..b52494025 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -552,12 +552,6 @@ def populate_idf_env_vars(idf_env): ] 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["ESP_ROM_ELF_DIR"] = platform.get_package_dir("tool-esp-rom-elfs") From c19ed742cd764c6957214878f0c913a45070e479 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:37:54 +0200 Subject: [PATCH 09/38] Refactor component_manager.py to use pathlib Refactor paths to use pathlib for better path handling. --- builder/frameworks/component_manager.py | 70 +++++++++++++++---------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index a4bdab182..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 @@ -667,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: @@ -699,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 @@ -891,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") @@ -930,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 @@ -992,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) @@ -1007,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. @@ -1019,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. @@ -1030,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. @@ -1050,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) From 6acbe3743d10b3f1210412d4429416294d674336 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:50:20 +0200 Subject: [PATCH 10/38] Implement component ignore handling and improve error messages Added functionality to retrieve components to ignore based on the lib_ignore project option using the component_manager. Updated error messages for better clarity. --- builder/frameworks/espidf.py | 48 +++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index b52494025..b993d097a 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -54,10 +54,17 @@ env.SConscript("_embed_files.py", exports="env") platform = env.PioPlatform() +_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 @@ -130,7 +137,7 @@ def create_silent_action(action_func): 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 = str(ARDUINO_FRMWRK_LIB_DIR_PATH / mcu) if not os.path.exists(ARDUINO_FRMWRK_C2_LIB_DIR): @@ -143,7 +150,6 @@ def create_silent_action(action_func): shutil.copytree(ARDUINO_C2_DIR, ARDUINO_FRMWRK_C2_LIB_DIR, dirs_exist_ok=True) 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") @@ -210,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 = str(Path(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: @@ -533,7 +543,7 @@ def get_cmake_code_model(src_dir, build_dir, extra_args=None): break if codemodel.get("version", {}).get("major") != 2: - sys.stderr.write("Error: Unsupported CMake codemodel major version\n") + sys.stderr.write("Error: Unsupported CMake codemodel version (need major=2)\n") env.Exit(1) return codemodel @@ -1100,6 +1110,26 @@ 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 Exception 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 = [ @@ -1824,8 +1854,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 From fbaab58f777e7a6fc9d966458411560d40021265 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:00:19 +0200 Subject: [PATCH 11/38] Refactor path handling to use pathlib --- builder/penv_setup.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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) From 5fe26f6be5f6ff72df3f211343a732549a0f7ab5 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:01:39 +0200 Subject: [PATCH 12/38] Refactor file path handling to use pathlib --- builder/frameworks/_embed_files.py | 31 +++++++++++------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/builder/frameworks/_embed_files.py b/builder/frameworks/_embed_files.py index 4e2ba2329..04d11e3dd 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 @@ -33,7 +34,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]) else: files_define = "COMPONENT_" + files_type.upper() for define in cppdefines: @@ -55,7 +56,7 @@ def extract_files(cppdefines, files_type): for f in value.split(":"): 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)): @@ -77,7 +78,7 @@ def prepare_file(source, target, env): with open(filepath, "rb+") as fp: fp.seek(-1, SEEK_END) - if fp.read(1) != "\0": + if fp.read(1) != b"\0": fp.seek(0, SEEK_CUR) fp.write(b"\0") @@ -91,16 +92,16 @@ 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] + files = [str(Path("$BUILD_DIR") / (s.name + ".S")) for s in source] return files, source @@ -133,22 +134,12 @@ def transform_to_asm(target, source, env): action=env.VerboseAction( " ".join( [ - join( - env.PioPlatform().get_package_dir("tool-cmake") or "", - "bin", - "cmake", - ), + str(Path(env.PioPlatform().get_package_dir("tool-cmake") or "") / "bin" / "cmake"), "-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", - ), + str(Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake"), ] ), "Generating assembly for $TARGET", @@ -171,7 +162,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", From c44641e2fe50f6a4cf6e8879e997110c144a45b5 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:03:13 +0200 Subject: [PATCH 13/38] Refactor path handling to use pathlib --- builder/frameworks/ulp.py | 57 +++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 33 deletions(-) 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) From 0650fc7920aaff5cdb27eaa9affc9fcc4bc88438 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:13:34 +0200 Subject: [PATCH 14/38] Refactor file paths to use pathlib --- builder/main.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/builder/main.py b/builder/main.py index c20636acf..b8ccfd898 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", @@ -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', ) @@ -727,6 +728,7 @@ def firmware_metrics(target, source, env): "--before", board.get("upload.before_reset", "default-reset"), "--after", + board.get("upload.after_reset", "hard-reset"), "write-flash", "-z", @@ -782,8 +784,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", From 79b170d6cfc02913fac1b4c29e290b66cf8c24d4 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:26:18 +0200 Subject: [PATCH 15/38] Refactor path handling to use Path objects --- builder/frameworks/arduino.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) 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) From 2eab54025ef9df4f5f32a4ffc97163599e95668e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:22:20 +0200 Subject: [PATCH 16/38] Fix formatting for CMAKE_DIR in espidf.py --- builder/frameworks/espidf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index b993d097a..afd030e91 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1422,7 +1422,7 @@ def preprocess_linker_file(src_ld_script, target_ld_script): env.VerboseAction( " ".join( [ - CMAKE_DIR, + f'"{CMAKE_DIR}"', "-DCC=%s" % str(Path(TOOLCHAIN_DIR) / "bin" / "$CC"), "-DSOURCE=$SOURCE", "-DTARGET=$TARGET", From a8cb3fdcb7f542e56a5881bac4bee3efda580fa8 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:38:00 +0200 Subject: [PATCH 17/38] Fix formatting in upload command Removed unnecessary newline in upload command. --- builder/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/builder/main.py b/builder/main.py index b8ccfd898..ddba6b6f9 100644 --- a/builder/main.py +++ b/builder/main.py @@ -728,7 +728,6 @@ def firmware_metrics(target, source, env): "--before", board.get("upload.before_reset", "default-reset"), "--after", - board.get("upload.after_reset", "hard-reset"), "write-flash", "-z", From 82f30fc9beb7473b8a72f87ce19184bf82fc26e0 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 18:18:42 +0200 Subject: [PATCH 18/38] Refactor find_lib_deps to use a set for ignores (more reliable) --- builder/frameworks/espidf.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index afd030e91..4a78161be 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1132,19 +1132,23 @@ def get_lib_ignore_components(): 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 ( From 9faaa9e608636972b174a00f2b30dcee8b02a7e4 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 18:22:51 +0200 Subject: [PATCH 19/38] Fix formatting in UPLOADER path assignment --- builder/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/main.py b/builder/main.py index ddba6b6f9..1f919e1ca 100644 --- a/builder/main.py +++ b/builder/main.py @@ -784,7 +784,7 @@ def firmware_metrics(target, source, env): env.Replace( UPLOADER=str( - Path(platform.get_package_dir("tool-dfuutil-arduino")).resolve() / "dfu-util") + Path(platform.get_package_dir("tool-dfuutil-arduino")).resolve() / "dfu-util" ), UPLOADERFLAGS=[ "-d", From 8a458385cd8a553a5caf5242ba9ed4beb6a9544f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 18:35:03 +0200 Subject: [PATCH 20/38] Update examples by removing components to reduce compile time --- examples/arduino-blink/platformio.ini | 31 ++++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/arduino-blink/platformio.ini b/examples/arduino-blink/platformio.ini index e97c6a792..efcafdb9a 100644 --- a/examples/arduino-blink/platformio.ini +++ b/examples/arduino-blink/platformio.ini @@ -25,6 +25,7 @@ custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote espressif/esp-dsp + espressif/esp_modem espressif/esp32-camera espressif/libsodium espressif/esp-modbus @@ -34,6 +35,7 @@ custom_component_remove = espressif/esp_diagnostics espressif/esp_rainmaker espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32-c2-devkitm-1] platform = espressif32 @@ -52,6 +54,7 @@ custom_sdkconfig = custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote espressif/esp-dsp + espressif/esp_modem espressif/esp32-camera espressif/libsodium espressif/esp-modbus @@ -61,6 +64,7 @@ custom_component_remove = espressif/esp_hosted espressif/esp_diagnostics espressif/esp_rainmaker espressif/rmaker_common + chmorgan/esp-libhelix-mp3 custom_component_add = espressif/cmake_utilities @ 0.* [env:esp32-s3-arduino_nano_esp32] @@ -78,6 +82,7 @@ lib_ignore = wifi custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote espressif/esp-dsp + espressif/esp_modem espressif/esp32-camera espressif/libsodium espressif/esp-modbus @@ -87,6 +92,7 @@ custom_component_remove = espressif/esp_hosted espressif/esp_diagnostics espressif/esp_rainmaker espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32s3-qio_opi_per] ; OPI Performance settings -> Display use @@ -113,14 +119,20 @@ 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/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/esp_rainmaker + espressif/rmaker_common + chmorgan/esp-libhelix-mp3 [env:esp32-c6-devkitc-1] platform = espressif32 @@ -141,6 +153,7 @@ custom_component_remove = espressif/esp_hosted espressif/esp-dsp espressif/esp_modem espressif/esp32-camera + chmorgan/esp-libhelix-mp3 [env:esp32-h2-devkitm-1] platform = espressif32 @@ -159,6 +172,7 @@ custom_component_remove = espressif/esp_hosted espressif/esp-dsp espressif/esp_modem espressif/esp32-camera + chmorgan/esp-libhelix-mp3 [env:esp32-p4] platform = espressif32 @@ -179,3 +193,4 @@ custom_component_remove = espressif/esp_hosted espressif/esp-dsp espressif/esp_modem espressif/esp32-camera + chmorgan/esp-libhelix-mp3 From 625b8a72779f9f670b4c744642baa8ecbd7cd7e2 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 18:52:55 +0200 Subject: [PATCH 21/38] Align custom_component_remove entries in platformio.ini --- examples/arduino-blink/platformio.ini | 66 ++++++++++++++++++++------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/examples/arduino-blink/platformio.ini b/examples/arduino-blink/platformio.ini index efcafdb9a..b72e933d9 100644 --- a/examples/arduino-blink/platformio.ini +++ b/examples/arduino-blink/platformio.ini @@ -20,22 +20,23 @@ lib_ignore = wifi 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/esp_modem - 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 - chmorgan/esp-libhelix-mp3 + '# CONFIG_ZB_ENABLED is not set' +custom_component_remove = espressif/esp_hosted + espressif/esp_wifi_remote + 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 @@ -53,6 +54,7 @@ custom_sdkconfig = '# CONFIG_ZB_ENABLED is not set' custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/mdns espressif/esp-dsp espressif/esp_modem espressif/esp32-camera @@ -62,6 +64,7 @@ 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 @@ -81,6 +84,7 @@ lib_ignore = wifi ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/mdns espressif/esp-dsp espressif/esp_modem espressif/esp32-camera @@ -90,6 +94,7 @@ 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 @@ -121,6 +126,7 @@ custom_sdkconfig = CONFIG_SPIRAM_MODE_OCT=y '# CONFIG_ZB_ENABLED is not set' custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/mdns espressif/esp-dsp espressif/esp_modem espressif/esp32-camera @@ -130,6 +136,7 @@ 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 @@ -153,6 +160,15 @@ custom_component_remove = espressif/esp_hosted 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] @@ -172,6 +188,15 @@ custom_component_remove = espressif/esp_hosted 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] @@ -193,4 +218,13 @@ custom_component_remove = espressif/esp_hosted 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 From f4bf0330e65ebd089d54fd2678e63a85ba16fc51 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:03:27 +0200 Subject: [PATCH 22/38] Add network_provisioning to custom_component_remove --- examples/arduino-blink/platformio.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/arduino-blink/platformio.ini b/examples/arduino-blink/platformio.ini index b72e933d9..b290307cf 100644 --- a/examples/arduino-blink/platformio.ini +++ b/examples/arduino-blink/platformio.ini @@ -23,6 +23,7 @@ custom_sdkconfig = '# 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 @@ -54,6 +55,7 @@ custom_sdkconfig = '# 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 @@ -84,6 +86,7 @@ lib_ignore = wifi ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem @@ -126,6 +129,7 @@ custom_sdkconfig = CONFIG_SPIRAM_MODE_OCT=y '# 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 @@ -156,6 +160,7 @@ lib_ignore = wifi ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem @@ -184,6 +189,7 @@ lib_ignore = ble ESP RainMaker custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem @@ -214,6 +220,7 @@ lib_ignore = wifi monitor_speed = 115200 custom_component_remove = espressif/esp_hosted espressif/esp_wifi_remote + espressif/network_provisioning espressif/mdns espressif/esp-dsp espressif/esp_modem From c4218bcd17830b47180cda0b1c3d119dbb5ba30f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 4 Sep 2025 19:23:48 +0200 Subject: [PATCH 23/38] Add some more libs to lib_ignore --- examples/arduino-rmt-blink/platformio.ini | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/examples/arduino-rmt-blink/platformio.ini b/examples/arduino-rmt-blink/platformio.ini index 02b23182a..0973eaa58 100644 --- a/examples/arduino-rmt-blink/platformio.ini +++ b/examples/arduino-rmt-blink/platformio.ini @@ -4,8 +4,11 @@ framework = arduino board = esp32-s2-saola-1 lib_ignore = ble wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=18 -DNR_OF_LEDS=1 @@ -15,8 +18,11 @@ framework = arduino board = esp32-s3-devkitc-1 lib_ignore = ble wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=48 -DNR_OF_LEDS=1 @@ -26,8 +32,11 @@ framework = arduino board = esp32-c3-devkitm-1 lib_ignore = ble wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=8 -DNR_OF_LEDS=1 @@ -37,8 +46,11 @@ 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 @@ -48,7 +60,10 @@ framework = arduino board = esp32-c6-devkitm-1 lib_ignore = ble wifi + ESP RainMaker + ESP Insights spiffs NetworkClientSecure + Zigbee build_flags = -DBUILTIN_RGBLED_PIN=8 -DNR_OF_LEDS=1 From 77bc425429ee1f430a9637e46579a90a5902f636 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:16:46 +0200 Subject: [PATCH 24/38] Specify exception types in lib_ignore processing --- builder/frameworks/espidf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 4a78161be..585b8bd58 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1125,7 +1125,7 @@ def get_lib_ignore_components(): lib_ignore_entries = lib_handler._get_lib_ignore_entries() return lib_ignore_entries - except Exception as e: + except (OSError, ValueError, RuntimeError, KeyError) as e: print(f"[ESP-IDF] Warning: Could not process lib_ignore: {e}") return [] From 5b43aea534cc6f213f4f4aa5044e719548146cc6 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:42:52 +0200 Subject: [PATCH 25/38] Refactor file handling for custom_sdkconfig --- builder/frameworks/espidf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 585b8bd58..254c52aac 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -213,8 +213,8 @@ def load_custom_sdkconfig_file(): print(f"Error decoding response from {file_entry}: {e}") return "" - # Handle local files - if "file://" in file_entry: + # Treat as local file if explicitly file:// or a non-URL token that isn't a flag + if "file://" in file_entry or ("://" not in file_entry and "=" not in file_entry): file_ref = file_entry[7:] if file_entry.startswith("file://") else file_entry if os.path.isabs(file_ref): @@ -561,7 +561,7 @@ def populate_idf_env_vars(idf_env): os.path.dirname(get_python_exe()), ] - idf_env["PATH"] = os.pathsep.join(additional_packages + [idf_env["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") From 2b3b2caf40a1bf722131e49b8a41b908cce897e5 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:52:24 +0200 Subject: [PATCH 26/38] revert custom_sdkconfig file detection --- builder/frameworks/espidf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 254c52aac..d738ceb9c 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -213,8 +213,8 @@ def load_custom_sdkconfig_file(): print(f"Error decoding response from {file_entry}: {e}") return "" - # Treat as local file if explicitly file:// or a non-URL token that isn't a flag - if "file://" in file_entry or ("://" not in file_entry and "=" not in file_entry): + # Handle local files + if "file://" in file_entry: file_ref = file_entry[7:] if file_entry.startswith("file://") else file_entry if os.path.isabs(file_ref): From 90e5e09cb4b1d9d54b7a2862c70e9ac9dba55aa4 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:03:22 +0200 Subject: [PATCH 27/38] Improve error handling for Ninja build file and Python path find --- builder/frameworks/espidf.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index d738ceb9c..2300fb595 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -849,9 +849,12 @@ def _normalize_fragment_path(base_dir, 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: @@ -1651,8 +1654,7 @@ def _create_venv(venv_dir): def get_python_exe(): - python_exe_path = str(Path(get_idf_venv_dir()) / ("Scripts" if IS_WINDOWS else "bin") / ("python" + (".exe" if IS_WINDOWS else ""))) - + 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) From cb8668808bfeee400158f267a4009cdb41fea76d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:10:24 +0200 Subject: [PATCH 28/38] project link script and improve extra images handling --- builder/frameworks/espidf.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 2300fb595..c548f52af 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1475,7 +1475,6 @@ def generate_mbedtls_bundle(sdk_config): # Use exec_command to change working directory exec_command(cmd + crt_args, cwd=BUILD_DIR) - bundle_path = str(Path("$BUILD_DIR") / "x509_crt_bundle") env.Execute( env.VerboseAction( " ".join( @@ -1809,10 +1808,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) @@ -1829,7 +1828,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, {}) @@ -2214,12 +2213,12 @@ def idf_custom_component(source, target, env): ) ] ) - EXTRA_IMG_DIR = str(Path(env.subst("$PROJECT_DIR")) / "variants" / "tasmota") - env.Append( - FLASH_EXTRA_IMAGES=[ - (offset, str(Path(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): From ada05dbd2b44f03905f07ceca856ca81f696755b Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:19:10 +0200 Subject: [PATCH 29/38] Improve object path resolution in espidf.py --- builder/frameworks/espidf.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index c548f52af..4bfe93796 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1038,10 +1038,15 @@ def compile_source_files( rel = src_path_obj.relative_to(components_dir_path) obj_path = str(Path(obj_path) / str(rel)) 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 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" From 838e072dbb080c879a0301cdd294ffa8c8f7daed Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:25:34 +0200 Subject: [PATCH 30/38] Refactor string formatting to be whitespace safe in path --- builder/frameworks/espidf.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 4bfe93796..dffaa2c5c 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -1435,13 +1435,13 @@ def preprocess_linker_file(src_ld_script, target_ld_script): " ".join( [ f'"{CMAKE_DIR}"', - "-DCC=%s" % str(Path(TOOLCHAIN_DIR) / "bin" / "$CC"), + f'-DCC="{str(Path(TOOLCHAIN_DIR) / "bin" / "$CC")}"', "-DSOURCE=$SOURCE", "-DTARGET=$TARGET", - "-DCONFIG_DIR=%s" % str(Path(BUILD_DIR) / "config"), - "-DLD_DIR=%s" % str(Path(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", - str(Path("$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", @@ -1485,11 +1485,11 @@ def generate_mbedtls_bundle(sdk_config): " ".join( [ f'"{CMAKE_DIR}"', - "-DDATA_FILE=" + bundle_path, - "-DSOURCE_FILE=%s.S" % bundle_path, + f'-DDATA_FILE="{bundle_path}"', + f'-DSOURCE_FILE="{bundle_path}.S"', "-DFILE_TYPE=BINARY", "-P", - str(Path(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...", From 355ca4462cbd59e2cac7b8420d135d02422b6ada Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:27:01 +0200 Subject: [PATCH 31/38] Refactor file paths to use f-strings --- builder/frameworks/_embed_files.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/frameworks/_embed_files.py b/builder/frameworks/_embed_files.py index 04d11e3dd..470bb7c11 100644 --- a/builder/frameworks/_embed_files.py +++ b/builder/frameworks/_embed_files.py @@ -134,12 +134,12 @@ def transform_to_asm(target, source, env): action=env.VerboseAction( " ".join( [ - str(Path(env.PioPlatform().get_package_dir("tool-cmake") or "") / "bin" / "cmake"), + f'"{str(Path(env.PioPlatform().get_package_dir("tool-cmake") or "") / "bin" / "cmake")}"', "-DDATA_FILE=$SOURCE", "-DSOURCE_FILE=$TARGET", "-DFILE_TYPE=$FILE_TYPE", "-P", - str(Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake"), + f'"{str(Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake")}"',, ] ), "Generating assembly for $TARGET", From e00157c2caa25b1ba2db58dfbf80a44cc94e3395 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:53:00 +0200 Subject: [PATCH 32/38] Fix file extraction and null byte handling --- builder/frameworks/_embed_files.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/builder/frameworks/_embed_files.py b/builder/frameworks/_embed_files.py index 470bb7c11..b4469b166 100644 --- a/builder/frameworks/_embed_files.py +++ b/builder/frameworks/_embed_files.py @@ -34,7 +34,7 @@ def extract_files(cppdefines, files_type): result = [] files = env.GetProjectOption("board_build.%s" % files_type, "").splitlines() if files: - result.extend([str(Path("$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: @@ -54,6 +54,7 @@ def extract_files(cppdefines, files_type): return [] for f in value.split(":"): + f = f.strip() if not f: continue result.append(str(Path("$PROJECT_DIR") / f)) @@ -77,10 +78,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) != b"\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): @@ -139,7 +144,7 @@ def transform_to_asm(target, source, env): "-DSOURCE_FILE=$TARGET", "-DFILE_TYPE=$FILE_TYPE", "-P", - f'"{str(Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake")}"',, + f'"{str(Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake")}"', ] ), "Generating assembly for $TARGET", From b83440e98859d139f167fc69ae32de5eed8e063f Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:00:36 +0200 Subject: [PATCH 33/38] Refactor Arduino libraries path assignment --- builder/frameworks/component_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index 90a0e5433..17dd5c3eb 100644 --- a/builder/frameworks/component_manager.py +++ b/builder/frameworks/component_manager.py @@ -52,9 +52,8 @@ def __init__(self, env): # Get Arduino libraries installation directory ald = self.platform.get_package_dir("framework-arduinoespressif32-libs") # Get MCU-specific Arduino libraries directory - self.arduino_libs_mcu = ( - str(Path(ald) / self.mcu) if ald else "" - ) + self.arduino_libs_mcu_path = (Path(ald) / self.mcu) + self.arduino_libs_mcu = str(self.arduino_libs_mcu_path) class ComponentLogger: From c00e5666a4313ac2edee073892c35b73f06bd193 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:07:07 +0200 Subject: [PATCH 34/38] no str in function string --- builder/frameworks/_embed_files.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/frameworks/_embed_files.py b/builder/frameworks/_embed_files.py index b4469b166..b3fbe4f7e 100644 --- a/builder/frameworks/_embed_files.py +++ b/builder/frameworks/_embed_files.py @@ -139,12 +139,12 @@ def transform_to_asm(target, source, env): action=env.VerboseAction( " ".join( [ - f'"{str(Path(env.PioPlatform().get_package_dir("tool-cmake") or "") / "bin" / "cmake")}"', + f'"{Path(env.PioPlatform().get_package_dir("tool-cmake") or "") / "bin" / "cmake"}"', "-DDATA_FILE=$SOURCE", "-DSOURCE_FILE=$TARGET", "-DFILE_TYPE=$FILE_TYPE", "-P", - f'"{str(Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake")}"', + f'"{Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake"}"', ] ), "Generating assembly for $TARGET", From 2165443b44b07cc2824647bc0d5e7d5496762352 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:36:37 +0200 Subject: [PATCH 35/38] Refactor arduino_libs_mcu assignment for clarity --- builder/frameworks/component_manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index 17dd5c3eb..04f3d4dcf 100644 --- a/builder/frameworks/component_manager.py +++ b/builder/frameworks/component_manager.py @@ -52,8 +52,7 @@ def __init__(self, env): # Get Arduino libraries installation directory ald = self.platform.get_package_dir("framework-arduinoespressif32-libs") # Get MCU-specific Arduino libraries directory - self.arduino_libs_mcu_path = (Path(ald) / self.mcu) - self.arduino_libs_mcu = str(self.arduino_libs_mcu_path) + self.arduino_libs_mcu = (str(Path(ald) / self.mcu)) class ComponentLogger: From 54ece58dfa69d85055aa41639803f08f71d561ff Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:44:00 +0200 Subject: [PATCH 36/38] Handle case when ald is not defined --- builder/frameworks/component_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builder/frameworks/component_manager.py b/builder/frameworks/component_manager.py index 04f3d4dcf..90a0e5433 100644 --- a/builder/frameworks/component_manager.py +++ b/builder/frameworks/component_manager.py @@ -52,7 +52,9 @@ def __init__(self, env): # Get Arduino libraries installation directory ald = self.platform.get_package_dir("framework-arduinoespressif32-libs") # Get MCU-specific Arduino libraries directory - self.arduino_libs_mcu = (str(Path(ald) / self.mcu)) + self.arduino_libs_mcu = ( + str(Path(ald) / self.mcu) if ald else "" + ) class ComponentLogger: From 85ab51fe4895e195f358f8811b7fee0459be404b Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 16:46:59 +0200 Subject: [PATCH 37/38] Update embedded file handling in _embed_files.py --- builder/frameworks/_embed_files.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/builder/frameworks/_embed_files.py b/builder/frameworks/_embed_files.py index b3fbe4f7e..65953f25d 100644 --- a/builder/frameworks/_embed_files.py +++ b/builder/frameworks/_embed_files.py @@ -25,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 # @@ -106,8 +114,8 @@ def embed_files(files, files_type): def transform_to_asm(target, source, env): - files = [str(Path("$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( @@ -139,12 +147,12 @@ def transform_to_asm(target, source, env): action=env.VerboseAction( " ".join( [ - f'"{Path(env.PioPlatform().get_package_dir("tool-cmake") or "") / "bin" / "cmake"}"', + cmake_cmd, "-DDATA_FILE=$SOURCE", "-DSOURCE_FILE=$TARGET", "-DFILE_TYPE=$FILE_TYPE", "-P", - f'"{Path(env.PioPlatform().get_package_dir("framework-espidf") or "") / "tools" / "cmake" / "scripts" / "data_file_embed_asm.cmake"}"', + data_embed_script, ] ), "Generating assembly for $TARGET", From 9217fbb4e5a418c21b04c11e076402ccdb5c70fd Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 5 Sep 2025 16:58:55 +0200 Subject: [PATCH 38/38] Escape TARGET and SOURCES variables in command --- builder/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/main.py b/builder/main.py index 1f919e1ca..4857b30d3 100644 --- a/builder/main.py +++ b/builder/main.py @@ -522,8 +522,8 @@ def switch_off_ldf(): "--flash-size", board.get("upload.flash_size", "4MB"), "-o", - "$TARGET", - "$SOURCES", + "\"$TARGET\"", + "\"$SOURCES\"", ] ), "Building $TARGET", @@ -792,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)