2016-10-22 02:20:57 +03:00
|
|
|
# Copyright 2014-present PlatformIO <contact@platformio.org>
|
|
|
|
|
#
|
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
|
#
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
#
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
|
# limitations under the License.
|
2018-07-30 18:23:41 +03:00
|
|
|
|
2016-10-22 02:20:57 +03:00
|
|
|
"""
|
|
|
|
|
Arduino
|
|
|
|
|
|
|
|
|
|
Arduino Wiring-based Framework allows writing cross-platform software to
|
|
|
|
|
control devices attached to a wide range of Arduino boards to create all
|
|
|
|
|
kinds of creative coding, interactive objects, spaces or physical experiences.
|
|
|
|
|
|
|
|
|
|
http://arduino.cc/en/Reference/HomePage
|
|
|
|
|
"""
|
|
|
|
|
|
2024-12-10 18:12:53 +01:00
|
|
|
import os
|
2025-01-18 15:47:53 +01:00
|
|
|
import sys
|
2024-12-14 12:42:42 +01:00
|
|
|
import shutil
|
2025-07-03 17:12:25 +02:00
|
|
|
import hashlib
|
|
|
|
|
import threading
|
|
|
|
|
from contextlib import suppress
|
|
|
|
|
from os.path import join, exists, isabs, splitdrive, commonpath, relpath
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import Union, List
|
2016-10-24 20:23:25 +03:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
from SCons.Script import DefaultEnvironment, SConscript
|
2025-01-16 22:49:42 +01:00
|
|
|
from platformio import fs
|
2024-12-14 12:42:42 +01:00
|
|
|
from platformio.package.manager.tool import ToolPackageManager
|
2025-09-17 00:09:47 +02:00
|
|
|
from platformio.compat import IS_WINDOWS
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
# Constants for better performance
|
|
|
|
|
UNICORE_FLAGS = {
|
|
|
|
|
"CORE32SOLO1",
|
|
|
|
|
"CONFIG_FREERTOS_UNICORE=y"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Thread-safe global flags to prevent message spam
|
|
|
|
|
_PATH_SHORTENING_LOCK = threading.Lock()
|
|
|
|
|
_PATH_SHORTENING_MESSAGES = {
|
|
|
|
|
'shortening_applied': False,
|
|
|
|
|
'no_framework_paths_warning': False,
|
|
|
|
|
'long_path_warning_shown': False
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_platform_default_threshold(mcu):
|
|
|
|
|
"""
|
|
|
|
|
Platform-specific max performance default values for
|
|
|
|
|
INCLUDE_PATH_LENGTH_THRESHOLD
|
|
|
|
|
These values push the limits for maximum performance and minimal path
|
|
|
|
|
shortening
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
mcu: MCU type (esp32, esp32s2, esp32s3, etc.)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int: Platform-specific default threshold
|
|
|
|
|
"""
|
|
|
|
|
# Max. performance values - pushing Windows command line limits
|
|
|
|
|
# Windows CMD has ~32768 character limit, we use aggressive values close
|
|
|
|
|
# to this
|
|
|
|
|
platform_defaults = {
|
|
|
|
|
"esp32": 32000, # Standard ESP32
|
|
|
|
|
"esp32s2": 32000, # ESP32-S2
|
|
|
|
|
"esp32s3": 32766, # ESP32-S3
|
2025-09-17 00:09:47 +02:00
|
|
|
"esp32c3": 32000, # ESP32-C3
|
2025-07-03 17:12:25 +02:00
|
|
|
"esp32c2": 32000, # ESP32-C2
|
|
|
|
|
"esp32c6": 31600, # ESP32-C6
|
|
|
|
|
"esp32h2": 32000, # ESP32-H2
|
|
|
|
|
"esp32p4": 32000, # ESP32-P4
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default_value = platform_defaults.get(mcu, 31600)
|
|
|
|
|
|
|
|
|
|
return default_value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_threshold(threshold, mcu):
|
|
|
|
|
"""
|
|
|
|
|
Validates threshold value with max. performance limits
|
|
|
|
|
Uses aggressive boundaries for maximum performance
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
threshold: Threshold value to validate
|
|
|
|
|
mcu: MCU type for context-specific validation
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int: Validated threshold value
|
|
|
|
|
"""
|
|
|
|
|
# Absolute limits - pushing boundaries
|
|
|
|
|
min_threshold = 20000 # Minimum reasonable value for complex projects
|
|
|
|
|
# Maximum aggressive value (beyond Windows CMD limit for testing)
|
|
|
|
|
max_threshold = 32767
|
|
|
|
|
|
|
|
|
|
# MCU-specific adjustments - all values are aggressive
|
|
|
|
|
mcu_adjustments = {
|
|
|
|
|
"esp32c2": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32c3": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32s2": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32s3": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32p4": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32c6": {"min": 30000, "max": 32767},
|
|
|
|
|
"esp32h2": {"min": 30000, "max": 32767},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Apply MCU-specific max. limits
|
|
|
|
|
if mcu in mcu_adjustments:
|
|
|
|
|
min_threshold = max(min_threshold, mcu_adjustments[mcu]["min"])
|
|
|
|
|
max_threshold = min(max_threshold, mcu_adjustments[mcu]["max"])
|
|
|
|
|
|
|
|
|
|
original_threshold = threshold
|
|
|
|
|
|
|
|
|
|
if threshold < min_threshold:
|
|
|
|
|
print(f"*** Warning: Include path threshold {threshold} too "
|
|
|
|
|
f"conservative for {mcu}, using safe minimum "
|
|
|
|
|
f"{min_threshold} ***")
|
|
|
|
|
threshold = min_threshold
|
|
|
|
|
elif threshold > max_threshold:
|
|
|
|
|
print(f"*** Warning: Include path threshold {threshold} exceeds "
|
|
|
|
|
f"possible maximum for {mcu}, using {max_threshold} ***")
|
|
|
|
|
threshold = max_threshold
|
|
|
|
|
|
|
|
|
|
# Warning for conservative values (opposite of original - warn if too low)
|
|
|
|
|
platform_default = get_platform_default_threshold(mcu)
|
|
|
|
|
if threshold < platform_default * 0.7: # More than 30% below max. default
|
|
|
|
|
print(f"*** Info: Include path threshold {threshold} is conservative "
|
|
|
|
|
f"compared to maximum default {platform_default} for "
|
|
|
|
|
f"{mcu} ***")
|
|
|
|
|
print("*** Consider using higher values for maximum performance ***")
|
|
|
|
|
|
|
|
|
|
if original_threshold != threshold:
|
|
|
|
|
print(f"Threshold adjusted from {original_threshold} to "
|
|
|
|
|
f"max. possible value {threshold} for {mcu}")
|
|
|
|
|
|
|
|
|
|
return threshold
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_include_path_threshold(env, config, current_env_section):
|
|
|
|
|
"""
|
|
|
|
|
Determines Windows INCLUDE_PATH_LENGTH_THRESHOLD from various sources
|
|
|
|
|
with priority order and max. possible validation
|
|
|
|
|
|
|
|
|
|
Priority order:
|
|
|
|
|
1. Environment variable PLATFORMIO_INCLUDE_PATH_THRESHOLD
|
|
|
|
|
2. Environment-specific setting in platformio.ini
|
|
|
|
|
3. Global setting in [env] section
|
|
|
|
|
4. Setting in [platformio] section
|
|
|
|
|
5. MCU-specific max. possible default value
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
env: PlatformIO Environment
|
|
|
|
|
config: Project Configuration
|
|
|
|
|
current_env_section: Current environment section
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int: Validated max. threshold value
|
|
|
|
|
"""
|
|
|
|
|
mcu = env.BoardConfig().get("build.mcu", "esp32")
|
|
|
|
|
default_threshold = get_platform_default_threshold(mcu)
|
|
|
|
|
setting_name = "custom_include_path_length_threshold"
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# 1. Check environment variable (highest priority)
|
|
|
|
|
env_var = os.environ.get("PLATFORMIO_INCLUDE_PATH_THRESHOLD")
|
|
|
|
|
if env_var:
|
|
|
|
|
try:
|
|
|
|
|
threshold = int(env_var)
|
|
|
|
|
threshold = validate_threshold(threshold, mcu)
|
|
|
|
|
print(f"*** Using environment variable max. possible include "
|
|
|
|
|
f"path threshold: {threshold} (MCU: {mcu}) ***")
|
|
|
|
|
return threshold
|
|
|
|
|
except ValueError:
|
|
|
|
|
print(f"*** Warning: Invalid environment variable "
|
|
|
|
|
f"PLATFORMIO_INCLUDE_PATH_THRESHOLD='{env_var}', "
|
|
|
|
|
f"ignoring ***")
|
|
|
|
|
|
|
|
|
|
# 2. Check environment-specific setting
|
|
|
|
|
if config.has_option(current_env_section, setting_name):
|
|
|
|
|
threshold = config.getint(current_env_section, setting_name)
|
|
|
|
|
threshold = validate_threshold(threshold, mcu)
|
|
|
|
|
print(f"*** Using environment-specific max. possible include "
|
|
|
|
|
f"path threshold: {threshold} (MCU: {mcu}) ***")
|
|
|
|
|
return threshold
|
|
|
|
|
|
|
|
|
|
# 3. Check global setting in [env] section
|
|
|
|
|
if config.has_option("env", setting_name):
|
|
|
|
|
threshold = config.getint("env", setting_name)
|
|
|
|
|
threshold = validate_threshold(threshold, mcu)
|
|
|
|
|
print(f"*** Using global [env] max. possible include path "
|
|
|
|
|
f"threshold: {threshold} (MCU: {mcu}) ***")
|
|
|
|
|
return threshold
|
|
|
|
|
|
|
|
|
|
# 4. Check setting in [platformio] section
|
|
|
|
|
if config.has_option("platformio", setting_name):
|
|
|
|
|
threshold = config.getint("platformio", setting_name)
|
|
|
|
|
threshold = validate_threshold(threshold, mcu)
|
|
|
|
|
print(f"*** Using [platformio] section max. possible include "
|
|
|
|
|
f"path threshold: {threshold} (MCU: {mcu}) ***")
|
|
|
|
|
return threshold
|
|
|
|
|
|
|
|
|
|
# 5. Use MCU-specific max. possible default value
|
|
|
|
|
threshold = validate_threshold(default_threshold, mcu)
|
|
|
|
|
if env.get("VERBOSE"):
|
|
|
|
|
print(f"*** Using platform-specific max. possible default "
|
|
|
|
|
f"include path threshold: {threshold} (MCU: {mcu}) ***")
|
|
|
|
|
|
|
|
|
|
return threshold
|
|
|
|
|
|
|
|
|
|
except (ValueError, TypeError) as e:
|
|
|
|
|
print(f"*** Warning: Invalid include path threshold value, using "
|
|
|
|
|
f"max. possible platform default {default_threshold} for "
|
|
|
|
|
f"{mcu}: {e} ***")
|
|
|
|
|
return validate_threshold(default_threshold, mcu)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_threshold_info(env, config, current_env_section):
|
|
|
|
|
"""
|
|
|
|
|
Helper function for debug information about max. possible threshold
|
|
|
|
|
configuration
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
env: PlatformIO Environment
|
|
|
|
|
config: Project Configuration
|
|
|
|
|
current_env_section: Current environment section
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
dict: Information about threshold configuration
|
|
|
|
|
"""
|
|
|
|
|
mcu = env.BoardConfig().get("build.mcu", "esp32")
|
|
|
|
|
setting_name = "custom_include_path_length_threshold"
|
|
|
|
|
|
|
|
|
|
info = {
|
|
|
|
|
"mcu": mcu,
|
|
|
|
|
"platform_default": get_platform_default_threshold(mcu),
|
|
|
|
|
"env_variable": os.environ.get("PLATFORMIO_INCLUDE_PATH_THRESHOLD"),
|
|
|
|
|
"env_specific": None,
|
|
|
|
|
"global_env": None,
|
|
|
|
|
"platformio_section": None,
|
|
|
|
|
"final_threshold": None,
|
|
|
|
|
"source": "bleeding_edge_platform_default",
|
|
|
|
|
"is_bleeding_edge": True
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Collect all possible sources
|
|
|
|
|
if config.has_option(current_env_section, setting_name):
|
|
|
|
|
with suppress(ValueError):
|
|
|
|
|
info["env_specific"] = config.getint(current_env_section,
|
|
|
|
|
setting_name)
|
|
|
|
|
|
|
|
|
|
if config.has_option("env", setting_name):
|
|
|
|
|
with suppress(ValueError):
|
|
|
|
|
info["global_env"] = config.getint("env", setting_name)
|
|
|
|
|
|
|
|
|
|
if config.has_option("platformio", setting_name):
|
|
|
|
|
with suppress(ValueError):
|
|
|
|
|
info["platformio_section"] = config.getint("platformio",
|
|
|
|
|
setting_name)
|
|
|
|
|
|
|
|
|
|
# Determine final threshold and source
|
|
|
|
|
info["final_threshold"] = get_include_path_threshold(env, config,
|
|
|
|
|
current_env_section)
|
|
|
|
|
|
|
|
|
|
# Determine source
|
|
|
|
|
if info["env_variable"]:
|
|
|
|
|
info["source"] = "environment_variable"
|
|
|
|
|
elif info["env_specific"] is not None:
|
|
|
|
|
info["source"] = "env_specific"
|
|
|
|
|
elif info["global_env"] is not None:
|
|
|
|
|
info["source"] = "global_env"
|
|
|
|
|
elif info["platformio_section"] is not None:
|
|
|
|
|
info["source"] = "platformio_section"
|
|
|
|
|
|
|
|
|
|
return info
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Cache class for frequently used paths
|
|
|
|
|
class PathCache:
|
|
|
|
|
def __init__(self, platform, mcu):
|
|
|
|
|
self.platform = platform
|
|
|
|
|
self.mcu = mcu
|
|
|
|
|
self._framework_dir = None
|
|
|
|
|
self._framework_lib_dir = None
|
|
|
|
|
self._sdk_dir = None
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def framework_dir(self):
|
|
|
|
|
if self._framework_dir is None:
|
|
|
|
|
self._framework_dir = self.platform.get_package_dir(
|
|
|
|
|
"framework-arduinoespressif32")
|
|
|
|
|
return self._framework_dir
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def framework_lib_dir(self):
|
|
|
|
|
if self._framework_lib_dir is None:
|
|
|
|
|
self._framework_lib_dir = self.platform.get_package_dir(
|
|
|
|
|
"framework-arduinoespressif32-libs")
|
|
|
|
|
return self._framework_lib_dir
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def sdk_dir(self):
|
|
|
|
|
if self._sdk_dir is None:
|
|
|
|
|
self._sdk_dir = fs.to_unix_path(
|
2025-09-17 00:09:47 +02:00
|
|
|
str(Path(self.framework_lib_dir) / self.mcu / "include")
|
2025-07-03 17:12:25 +02:00
|
|
|
)
|
|
|
|
|
return self._sdk_dir
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_and_warn_long_path_support():
|
|
|
|
|
"""Checks Windows long path support and issues warning if disabled"""
|
|
|
|
|
with _PATH_SHORTENING_LOCK: # Thread-safe access
|
|
|
|
|
if not IS_WINDOWS or _PATH_SHORTENING_MESSAGES[
|
|
|
|
|
'long_path_warning_shown']:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
import winreg
|
|
|
|
|
key = winreg.OpenKey(
|
|
|
|
|
winreg.HKEY_LOCAL_MACHINE,
|
|
|
|
|
r"SYSTEM\CurrentControlSet\Control\FileSystem"
|
|
|
|
|
)
|
|
|
|
|
value, _ = winreg.QueryValueEx(key, "LongPathsEnabled")
|
|
|
|
|
winreg.CloseKey(key)
|
|
|
|
|
|
|
|
|
|
if value != 1:
|
|
|
|
|
print("*** WARNING: Windows Long Path Support is disabled ***")
|
|
|
|
|
print("*** Enable it for better performance: ***")
|
|
|
|
|
print("*** 1. Run as Administrator: gpedit.msc ***")
|
|
|
|
|
print("*** 2. Navigate to: Computer Configuration > "
|
|
|
|
|
"Administrative Templates > System > Filesystem ***")
|
|
|
|
|
print("*** 3. Enable 'Enable Win32 long paths' ***")
|
|
|
|
|
print("*** OR run PowerShell as Admin: ***")
|
|
|
|
|
print("*** New-ItemProperty -Path "
|
|
|
|
|
"'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\FileSystem' "
|
|
|
|
|
"-Name 'LongPathsEnabled' -Value 1 -PropertyType DWORD "
|
|
|
|
|
"-Force ***")
|
|
|
|
|
print("*** Restart required after enabling ***")
|
|
|
|
|
except Exception:
|
|
|
|
|
print("*** WARNING: Could not check Long Path Support status ***")
|
|
|
|
|
print("*** Consider enabling Windows Long Path Support for "
|
|
|
|
|
"better performance ***")
|
|
|
|
|
|
|
|
|
|
_PATH_SHORTENING_MESSAGES['long_path_warning_shown'] = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Secure deletion functions
|
|
|
|
|
def safe_delete_file(file_path: Union[str, Path],
|
|
|
|
|
force: bool = False) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Secure file deletion
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
file_path: Path to file to be deleted
|
|
|
|
|
force: Forces deletion even for write-protected files
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: True if successfully deleted
|
|
|
|
|
"""
|
|
|
|
|
file_path = Path(file_path)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Check existence
|
|
|
|
|
if not file_path.exists():
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Remove write protection if necessary
|
|
|
|
|
if force and not os.access(file_path, os.W_OK):
|
|
|
|
|
file_path.chmod(0o666)
|
|
|
|
|
|
|
|
|
|
# Delete file
|
|
|
|
|
file_path.unlink()
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except PermissionError:
|
|
|
|
|
return False
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def safe_delete_directory(dir_path: Union[str, Path]) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Secure directory deletion
|
|
|
|
|
"""
|
|
|
|
|
dir_path = Path(dir_path)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if not dir_path.exists():
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
shutil.rmtree(dir_path)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_platformio_path(path: Union[str, Path]) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Enhanced validation for PlatformIO package paths
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
path = Path(path).resolve()
|
|
|
|
|
path_str = str(path)
|
|
|
|
|
|
|
|
|
|
# Must be within .platformio directory structure
|
|
|
|
|
if ".platformio" not in path_str:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Must be a packages directory
|
|
|
|
|
if "packages" not in path_str:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Must be framework-related
|
|
|
|
|
framework_indicators = [
|
|
|
|
|
"framework-arduinoespressif32",
|
|
|
|
|
"framework-arduinoespressif32-libs"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if not any(indicator in path_str for indicator in framework_indicators):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Must not be a critical system path
|
|
|
|
|
critical_paths = ["/usr", "/bin", "/sbin", "/etc", "/boot",
|
|
|
|
|
"C:\\Windows", "C:\\Program Files"]
|
|
|
|
|
return not any(critical in path_str for critical in critical_paths)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_deletion_path(path: Union[str, Path],
|
|
|
|
|
allowed_patterns: List[str]) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Validates if a path can be safely deleted
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
path: Path to be checked
|
|
|
|
|
allowed_patterns: Allowed path patterns
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: True if deletion is safe
|
|
|
|
|
"""
|
|
|
|
|
path = Path(path).resolve()
|
|
|
|
|
|
|
|
|
|
# Check against critical system paths
|
|
|
|
|
critical_paths = [
|
|
|
|
|
Path.home(),
|
|
|
|
|
Path("/"),
|
|
|
|
|
Path("C:\\") if IS_WINDOWS else None,
|
|
|
|
|
Path("/usr"),
|
|
|
|
|
Path("/etc"),
|
|
|
|
|
Path("/bin"),
|
|
|
|
|
Path("/sbin")
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for critical in filter(None, critical_paths):
|
|
|
|
|
try:
|
|
|
|
|
normalized_path = path.resolve()
|
|
|
|
|
normalized_critical = critical.resolve()
|
|
|
|
|
if (normalized_path == normalized_critical or
|
|
|
|
|
normalized_critical in normalized_path.parents):
|
|
|
|
|
return False
|
|
|
|
|
except (OSError, ValueError):
|
|
|
|
|
# Path comparison failed, reject for safety
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Check against allowed patterns
|
|
|
|
|
path_str = str(path)
|
|
|
|
|
is_allowed = any(pattern in path_str for pattern in allowed_patterns)
|
|
|
|
|
|
|
|
|
|
return is_allowed
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def safe_framework_cleanup():
|
|
|
|
|
"""Secure cleanup of Arduino Framework with enhanced error handling"""
|
|
|
|
|
success = True
|
|
|
|
|
|
|
|
|
|
# Framework directory cleanup
|
|
|
|
|
if exists(FRAMEWORK_DIR):
|
|
|
|
|
if validate_platformio_path(FRAMEWORK_DIR):
|
|
|
|
|
if not safe_delete_directory(FRAMEWORK_DIR):
|
|
|
|
|
print("Error removing framework")
|
|
|
|
|
success = False
|
|
|
|
|
|
|
|
|
|
# Framework libs directory cleanup
|
|
|
|
|
if exists(FRAMEWORK_LIB_DIR):
|
|
|
|
|
if validate_platformio_path(FRAMEWORK_LIB_DIR):
|
|
|
|
|
if not safe_delete_directory(FRAMEWORK_LIB_DIR):
|
|
|
|
|
print("Error removing framework libs")
|
|
|
|
|
success = False
|
|
|
|
|
|
|
|
|
|
return success
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def safe_remove_sdkconfig_files():
|
|
|
|
|
"""Secure removal of SDKConfig files"""
|
|
|
|
|
envs = [section.replace("env:", "") for section in config.sections()
|
|
|
|
|
if section.startswith("env:")]
|
|
|
|
|
for env_name in envs:
|
2025-09-17 00:09:47 +02:00
|
|
|
file_path = str(Path(project_dir) / f"sdkconfig.{env_name}")
|
2025-07-03 17:12:25 +02:00
|
|
|
if exists(file_path):
|
|
|
|
|
safe_delete_file(file_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Initialization
|
2018-07-09 22:47:38 +03:00
|
|
|
env = DefaultEnvironment()
|
2024-12-14 12:42:42 +01:00
|
|
|
pm = ToolPackageManager()
|
|
|
|
|
platform = env.PioPlatform()
|
|
|
|
|
config = env.GetProjectConfig()
|
2020-03-17 10:51:38 +00:00
|
|
|
board = env.BoardConfig()
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
# Cached values
|
2024-12-14 12:42:42 +01:00
|
|
|
mcu = board.get("build.mcu", "esp32")
|
2025-07-03 17:12:25 +02:00
|
|
|
pioenv = env["PIOENV"]
|
|
|
|
|
project_dir = env.subst("$PROJECT_DIR")
|
|
|
|
|
path_cache = PathCache(platform, mcu)
|
|
|
|
|
current_env_section = f"env:{pioenv}"
|
|
|
|
|
|
|
|
|
|
# Board configuration
|
2024-12-14 12:42:42 +01:00
|
|
|
board_sdkconfig = board.get("espidf.custom_sdkconfig", "")
|
|
|
|
|
entry_custom_sdkconfig = "\n"
|
|
|
|
|
flag_custom_sdkconfig = False
|
2025-07-03 17:12:25 +02:00
|
|
|
flag_custom_component_remove = False
|
|
|
|
|
flag_custom_component_add = False
|
|
|
|
|
flag_lib_ignore = False
|
|
|
|
|
|
|
|
|
|
if mcu == "esp32c2":
|
|
|
|
|
flag_custom_sdkconfig = True
|
|
|
|
|
|
|
|
|
|
# pio lib_ignore check
|
|
|
|
|
if config.has_option(current_env_section, "lib_ignore"):
|
|
|
|
|
flag_lib_ignore = True
|
2024-12-14 12:42:42 +01:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
# Custom Component remove check
|
|
|
|
|
if config.has_option(current_env_section, "custom_component_remove"):
|
|
|
|
|
flag_custom_component_remove = True
|
|
|
|
|
|
|
|
|
|
# Custom SDKConfig check
|
|
|
|
|
if config.has_option(current_env_section, "custom_sdkconfig"):
|
2024-12-14 12:42:42 +01:00
|
|
|
entry_custom_sdkconfig = env.GetProjectOption("custom_sdkconfig")
|
|
|
|
|
flag_custom_sdkconfig = True
|
|
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
if board_sdkconfig:
|
2024-12-14 12:42:42 +01:00
|
|
|
flag_custom_sdkconfig = True
|
|
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
extra_flags_raw = board.get("build.extra_flags", [])
|
|
|
|
|
if isinstance(extra_flags_raw, list):
|
|
|
|
|
extra_flags = " ".join(extra_flags_raw).replace("-D", " ")
|
|
|
|
|
else:
|
|
|
|
|
extra_flags = str(extra_flags_raw).replace("-D", " ")
|
|
|
|
|
|
2024-12-14 12:42:42 +01:00
|
|
|
framework_reinstall = False
|
2018-07-09 22:47:38 +03:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
FRAMEWORK_DIR = path_cache.framework_dir
|
|
|
|
|
FRAMEWORK_LIB_DIR = path_cache.framework_lib_dir
|
2025-01-16 22:49:42 +01:00
|
|
|
|
2019-10-18 18:19:53 +03:00
|
|
|
SConscript("_embed_files.py", exports="env")
|
2018-07-09 22:47:38 +03:00
|
|
|
|
2025-09-17 00:09:47 +02:00
|
|
|
flag_any_custom_sdkconfig = exists(str(Path(FRAMEWORK_LIB_DIR) / "sdkconfig"))
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def has_unicore_flags():
|
|
|
|
|
"""Check if any UNICORE flags are present in configuration"""
|
|
|
|
|
return any(flag in extra_flags or flag in entry_custom_sdkconfig
|
|
|
|
|
or flag in board_sdkconfig for flag in UNICORE_FLAGS)
|
|
|
|
|
|
2024-12-14 12:42:42 +01:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
# Esp32-solo1 libs settings
|
|
|
|
|
if flag_custom_sdkconfig and has_unicore_flags():
|
|
|
|
|
if not env.get('BUILD_UNFLAGS'): # Initialize if not set
|
|
|
|
|
env['BUILD_UNFLAGS'] = []
|
|
|
|
|
|
|
|
|
|
build_unflags = (" ".join(env['BUILD_UNFLAGS']) +
|
|
|
|
|
" -mdisable-hardware-atomics -ustart_app_other_cores")
|
2024-12-14 12:42:42 +01:00
|
|
|
new_build_unflags = build_unflags.split()
|
2025-07-03 17:12:25 +02:00
|
|
|
env.Replace(BUILD_UNFLAGS=new_build_unflags)
|
|
|
|
|
|
|
|
|
|
|
2024-12-14 12:42:42 +01:00
|
|
|
def get_MD5_hash(phrase):
|
2025-07-03 17:12:25 +02:00
|
|
|
return hashlib.md5(phrase.encode('utf-8')).hexdigest()[:16]
|
2024-12-14 12:42:42 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def matching_custom_sdkconfig():
|
2025-07-03 17:12:25 +02:00
|
|
|
"""Checks if current environment matches existing sdkconfig"""
|
2024-12-14 12:42:42 +01:00
|
|
|
cust_sdk_is_present = False
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
if not flag_any_custom_sdkconfig:
|
|
|
|
|
return True, cust_sdk_is_present
|
|
|
|
|
|
2025-09-17 00:09:47 +02:00
|
|
|
last_sdkconfig_path = str(Path(project_dir) / "sdkconfig.defaults")
|
2025-07-03 17:12:25 +02:00
|
|
|
if not exists(last_sdkconfig_path):
|
|
|
|
|
return False, cust_sdk_is_present
|
|
|
|
|
|
|
|
|
|
if not flag_custom_sdkconfig:
|
|
|
|
|
return False, cust_sdk_is_present
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with open(last_sdkconfig_path) as src:
|
|
|
|
|
line = src.readline()
|
|
|
|
|
if line.startswith("# TASMOTA__"):
|
|
|
|
|
cust_sdk_is_present = True
|
|
|
|
|
custom_options = entry_custom_sdkconfig
|
|
|
|
|
expected_hash = get_MD5_hash(custom_options.strip() + mcu)
|
|
|
|
|
if line.split("__")[1].strip() == expected_hash:
|
|
|
|
|
return True, cust_sdk_is_present
|
|
|
|
|
except (IOError, IndexError):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
return False, cust_sdk_is_present
|
|
|
|
|
|
2024-12-14 12:42:42 +01:00
|
|
|
|
|
|
|
|
def check_reinstall_frwrk():
|
2025-07-03 17:12:25 +02:00
|
|
|
if not flag_custom_sdkconfig and flag_any_custom_sdkconfig:
|
|
|
|
|
# case custom sdkconfig exists and an env without "custom_sdkconfig"
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
if flag_custom_sdkconfig:
|
|
|
|
|
matching_sdkconfig, _ = matching_custom_sdkconfig()
|
|
|
|
|
if not matching_sdkconfig:
|
|
|
|
|
# check if current custom sdkconfig is different from existing
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
2025-01-16 22:49:42 +01:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
def call_compile_libs():
|
|
|
|
|
print(f"*** Compile Arduino IDF libs for {pioenv} ***")
|
|
|
|
|
SConscript("espidf.py")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FRAMEWORK_SDK_DIR = path_cache.sdk_dir
|
2025-01-16 22:49:42 +01:00
|
|
|
IS_INTEGRATION_DUMP = env.IsIntegrationDump()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_framework_subfolder(potential_subfolder):
|
2025-07-03 17:12:25 +02:00
|
|
|
"""Check if a path is a subfolder of the framework SDK directory"""
|
|
|
|
|
# carefully check before change this function
|
|
|
|
|
if not isabs(potential_subfolder):
|
2025-01-16 22:49:42 +01:00
|
|
|
return False
|
2025-07-03 17:12:25 +02:00
|
|
|
if (splitdrive(FRAMEWORK_SDK_DIR)[0] !=
|
|
|
|
|
splitdrive(potential_subfolder)[0]):
|
2025-01-16 22:49:42 +01:00
|
|
|
return False
|
2025-07-03 17:12:25 +02:00
|
|
|
return (commonpath([FRAMEWORK_SDK_DIR]) ==
|
|
|
|
|
commonpath([FRAMEWORK_SDK_DIR, potential_subfolder]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Performance optimization with caching
|
|
|
|
|
def calculate_include_path_length(includes):
|
|
|
|
|
"""Calculate total character count of all include paths with caching"""
|
|
|
|
|
if not hasattr(calculate_include_path_length, '_cache'):
|
|
|
|
|
calculate_include_path_length._cache = {}
|
|
|
|
|
|
|
|
|
|
cache_key = tuple(includes)
|
|
|
|
|
if cache_key not in calculate_include_path_length._cache:
|
|
|
|
|
calculate_include_path_length._cache[cache_key] = sum(
|
|
|
|
|
len(str(inc)) for inc in includes)
|
|
|
|
|
|
|
|
|
|
return calculate_include_path_length._cache[cache_key]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def analyze_path_distribution(includes):
|
|
|
|
|
"""Analyze the distribution of include path lengths for optimization
|
|
|
|
|
insights"""
|
|
|
|
|
if not includes:
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
lengths = [len(str(inc)) for inc in includes]
|
|
|
|
|
framework_lengths = [len(str(inc)) for inc in includes
|
|
|
|
|
if is_framework_subfolder(inc)]
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'total_paths': len(includes),
|
|
|
|
|
'total_length': sum(lengths),
|
|
|
|
|
'average_length': sum(lengths) / len(lengths),
|
|
|
|
|
'max_length': max(lengths),
|
|
|
|
|
'min_length': min(lengths),
|
|
|
|
|
'framework_paths': len(framework_lengths),
|
|
|
|
|
'framework_total_length': sum(framework_lengths),
|
|
|
|
|
'framework_avg_length': (sum(framework_lengths) /
|
|
|
|
|
len(framework_lengths)
|
|
|
|
|
if framework_lengths else 0)
|
|
|
|
|
}
|
2025-01-16 22:49:42 +01:00
|
|
|
|
|
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
def debug_framework_paths(env, include_count, total_length):
|
|
|
|
|
"""Debug framework paths to understand the issue (verbose mode only)"""
|
|
|
|
|
if not env.get("VERBOSE"):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("*** Debug Framework Paths ***")
|
|
|
|
|
print(f"*** MCU: {mcu} ***")
|
|
|
|
|
print(f"*** FRAMEWORK_DIR: {FRAMEWORK_DIR} ***")
|
|
|
|
|
print(f"*** FRAMEWORK_SDK_DIR: {FRAMEWORK_SDK_DIR} ***")
|
|
|
|
|
print(f"*** SDK exists: {exists(FRAMEWORK_SDK_DIR)} ***")
|
|
|
|
|
print(f"*** Include count: {include_count} ***")
|
|
|
|
|
print(f"*** Total path length: {total_length} ***")
|
|
|
|
|
|
|
|
|
|
includes = env.get("CPPPATH", [])
|
|
|
|
|
framework_count = 0
|
|
|
|
|
longest_paths = sorted(includes, key=len, reverse=True)[:5]
|
|
|
|
|
|
|
|
|
|
print("*** Longest include paths: ***")
|
|
|
|
|
for i, inc in enumerate(longest_paths):
|
|
|
|
|
is_fw = is_framework_subfolder(inc)
|
|
|
|
|
if is_fw:
|
|
|
|
|
framework_count += 1
|
|
|
|
|
print(f"*** {i+1}: {inc} (length: {len(str(inc))}) -> "
|
|
|
|
|
f"Framework: {is_fw} ***")
|
|
|
|
|
|
|
|
|
|
print(f"*** Framework includes found: {framework_count}/"
|
|
|
|
|
f"{len(includes)} ***")
|
|
|
|
|
|
|
|
|
|
# Show path distribution analysis
|
|
|
|
|
analysis = analyze_path_distribution(includes)
|
|
|
|
|
print(f"*** Path Analysis: Avg={analysis.get('average_length', 0):.1f}, "
|
|
|
|
|
f"Max={analysis.get('max_length', 0)}, "
|
|
|
|
|
f"Framework Avg={analysis.get('framework_avg_length', 0):.1f} ***")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_include_shortening(env, node, includes, total_length):
|
|
|
|
|
"""Applies include path shortening technique"""
|
|
|
|
|
env_get = env.get
|
|
|
|
|
to_unix_path = fs.to_unix_path
|
|
|
|
|
ccflags = env["CCFLAGS"]
|
|
|
|
|
asflags = env["ASFLAGS"]
|
|
|
|
|
|
|
|
|
|
includes = [to_unix_path(inc) for inc in env_get("CPPPATH", [])]
|
2025-01-16 22:49:42 +01:00
|
|
|
shortened_includes = []
|
|
|
|
|
generic_includes = []
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
original_length = total_length
|
|
|
|
|
saved_chars = 0
|
|
|
|
|
|
2025-01-16 22:49:42 +01:00
|
|
|
for inc in includes:
|
|
|
|
|
if is_framework_subfolder(inc):
|
2025-07-03 17:12:25 +02:00
|
|
|
relative_path = to_unix_path(relpath(inc, FRAMEWORK_SDK_DIR))
|
|
|
|
|
shortened_path = "-iwithprefix/" + relative_path
|
|
|
|
|
shortened_includes.append(shortened_path)
|
|
|
|
|
|
|
|
|
|
# Calculate character savings
|
|
|
|
|
# Original: full path in -I flag
|
|
|
|
|
# New: -iprefix + shortened relative path
|
|
|
|
|
original_chars = len(f"-I{inc}")
|
|
|
|
|
new_chars = len(shortened_path)
|
|
|
|
|
saved_chars += max(0, original_chars - new_chars)
|
2025-01-16 22:49:42 +01:00
|
|
|
else:
|
|
|
|
|
generic_includes.append(inc)
|
|
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
# Show result message only once with thread safety
|
|
|
|
|
with _PATH_SHORTENING_LOCK:
|
|
|
|
|
if not _PATH_SHORTENING_MESSAGES['shortening_applied']:
|
|
|
|
|
if shortened_includes:
|
|
|
|
|
# Each -I is 2 chars
|
|
|
|
|
removed_i_flags = len(shortened_includes) * 2
|
|
|
|
|
new_total_length = (original_length - saved_chars +
|
|
|
|
|
len(f"-iprefix{FRAMEWORK_SDK_DIR}") -
|
|
|
|
|
removed_i_flags)
|
|
|
|
|
print(f"*** Applied include path shortening for "
|
|
|
|
|
f"{len(shortened_includes)} framework paths ***")
|
|
|
|
|
print(f"*** Path length reduced from {original_length} to "
|
|
|
|
|
f"~{new_total_length} characters ***")
|
|
|
|
|
print(f"*** Estimated savings: {saved_chars} characters ***")
|
|
|
|
|
else:
|
|
|
|
|
if not _PATH_SHORTENING_MESSAGES[
|
|
|
|
|
'no_framework_paths_warning']:
|
|
|
|
|
print("*** Warning: Path length high but no framework "
|
|
|
|
|
"paths found for shortening ***")
|
|
|
|
|
print("*** This may indicate an architecture-specific "
|
|
|
|
|
"issue ***")
|
|
|
|
|
print("*** Run with -v (verbose) for detailed path "
|
|
|
|
|
"analysis ***")
|
|
|
|
|
_PATH_SHORTENING_MESSAGES[
|
|
|
|
|
'no_framework_paths_warning'] = True
|
|
|
|
|
_PATH_SHORTENING_MESSAGES['shortening_applied'] = True
|
|
|
|
|
|
|
|
|
|
common_flags = ["-iprefix", FRAMEWORK_SDK_DIR] + shortened_includes
|
|
|
|
|
|
2025-01-16 22:49:42 +01:00
|
|
|
return env.Object(
|
|
|
|
|
node,
|
|
|
|
|
CPPPATH=generic_includes,
|
2025-07-03 17:12:25 +02:00
|
|
|
CCFLAGS=ccflags + common_flags,
|
|
|
|
|
ASFLAGS=asflags + common_flags,
|
2025-01-16 22:49:42 +01:00
|
|
|
)
|
|
|
|
|
|
2024-12-14 12:42:42 +01:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
def smart_include_length_shorten(env, node):
|
|
|
|
|
"""
|
|
|
|
|
Include path shortening based on max. performance configurable threshold
|
|
|
|
|
with enhanced MCU support
|
|
|
|
|
Uses aggressive thresholds for maximum performance
|
|
|
|
|
"""
|
|
|
|
|
if IS_INTEGRATION_DUMP:
|
|
|
|
|
return node
|
|
|
|
|
|
|
|
|
|
if not IS_WINDOWS:
|
|
|
|
|
return env.Object(node)
|
|
|
|
|
|
|
|
|
|
# Get dynamically configurable bleeding edge threshold
|
|
|
|
|
include_path_threshold = get_include_path_threshold(env, config,
|
|
|
|
|
current_env_section)
|
|
|
|
|
|
|
|
|
|
check_and_warn_long_path_support()
|
|
|
|
|
|
|
|
|
|
includes = env.get("CPPPATH", [])
|
|
|
|
|
include_count = len(includes)
|
|
|
|
|
total_path_length = calculate_include_path_length(includes)
|
|
|
|
|
|
|
|
|
|
# Debug information in verbose mode
|
|
|
|
|
if env.get("VERBOSE"):
|
|
|
|
|
debug_framework_paths(env, include_count, total_path_length)
|
|
|
|
|
|
|
|
|
|
# Extended debug information about maximum edge threshold
|
|
|
|
|
# configuration
|
|
|
|
|
threshold_info = get_threshold_info(env, config, current_env_section)
|
|
|
|
|
print("*** Maximum Threshold Configuration Debug ***")
|
|
|
|
|
print(f"*** MCU: {threshold_info['mcu']} ***")
|
|
|
|
|
print(f"*** Maximum Platform Default: "
|
|
|
|
|
f"{threshold_info['platform_default']} ***")
|
|
|
|
|
print(f"*** Final maximum Threshold: "
|
|
|
|
|
f"{threshold_info['final_threshold']} ***")
|
|
|
|
|
print(f"*** Source: {threshold_info['source']} ***")
|
|
|
|
|
print("*** Performance Mode: Maximum Aggressive ***")
|
|
|
|
|
if threshold_info['env_variable']:
|
|
|
|
|
print(f"*** Env Variable: {threshold_info['env_variable']} ***")
|
|
|
|
|
if threshold_info['env_specific']:
|
|
|
|
|
print(f"*** Env Specific: {threshold_info['env_specific']} ***")
|
|
|
|
|
if threshold_info['global_env']:
|
|
|
|
|
print(f"*** Global Env: {threshold_info['global_env']} ***")
|
|
|
|
|
if threshold_info['platformio_section']:
|
|
|
|
|
print(f"*** PlatformIO Section: "
|
|
|
|
|
f"{threshold_info['platformio_section']} ***")
|
|
|
|
|
|
|
|
|
|
# Use the configurable and validated bleeding edge threshold
|
|
|
|
|
if total_path_length <= include_path_threshold:
|
|
|
|
|
return env.Object(node)
|
|
|
|
|
|
|
|
|
|
return apply_include_shortening(env, node, includes, total_path_length)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_frameworks_in_current_env():
|
|
|
|
|
"""Determines the frameworks of the current environment"""
|
|
|
|
|
if "framework" in config.options(current_env_section):
|
|
|
|
|
return config.get(current_env_section, "framework", "")
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Framework check
|
|
|
|
|
current_env_frameworks = get_frameworks_in_current_env()
|
|
|
|
|
if "arduino" in current_env_frameworks and "espidf" in current_env_frameworks:
|
|
|
|
|
# Arduino as component is set, switch off Hybrid compile
|
|
|
|
|
flag_custom_sdkconfig = False
|
|
|
|
|
|
|
|
|
|
# Framework reinstallation if required - Enhanced with secure deletion and
|
|
|
|
|
# error handling
|
|
|
|
|
if check_reinstall_frwrk():
|
|
|
|
|
# Secure removal of SDKConfig files
|
|
|
|
|
safe_remove_sdkconfig_files()
|
|
|
|
|
|
2025-02-28 18:38:21 +01:00
|
|
|
print("*** Reinstall Arduino framework ***")
|
2025-07-03 17:12:25 +02:00
|
|
|
|
|
|
|
|
# Secure framework cleanup with enhanced error handling
|
|
|
|
|
if safe_framework_cleanup():
|
|
|
|
|
arduino_frmwrk_url = str(platform.get_package_spec(
|
|
|
|
|
"framework-arduinoespressif32")).split("uri=", 1)[1][:-1]
|
|
|
|
|
arduino_frmwrk_lib_url = str(platform.get_package_spec(
|
|
|
|
|
"framework-arduinoespressif32-libs")).split("uri=", 1)[1][:-1]
|
|
|
|
|
pm.install(arduino_frmwrk_url)
|
|
|
|
|
pm.install(arduino_frmwrk_lib_url)
|
|
|
|
|
|
|
|
|
|
if flag_custom_sdkconfig:
|
|
|
|
|
call_compile_libs()
|
|
|
|
|
flag_custom_sdkconfig = False
|
|
|
|
|
else:
|
|
|
|
|
print("Framework cleanup failed - installation aborted")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
if flag_custom_sdkconfig and not flag_any_custom_sdkconfig:
|
2024-12-14 12:42:42 +01:00
|
|
|
call_compile_libs()
|
|
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
# Main logic for Arduino Framework
|
|
|
|
|
pioframework = env.subst("$PIOFRAMEWORK")
|
|
|
|
|
arduino_lib_compile_flag = env.subst("$ARDUINO_LIB_COMPILE_FLAG")
|
|
|
|
|
|
|
|
|
|
if ("arduino" in pioframework and "espidf" not in pioframework and
|
|
|
|
|
arduino_lib_compile_flag in ("Inactive", "True")):
|
2025-07-23 17:48:25 +02:00
|
|
|
|
2025-07-03 17:12:25 +02:00
|
|
|
# try to remove not needed include path if an lib_ignore entry exists
|
|
|
|
|
from component_manager import ComponentManager
|
|
|
|
|
component_manager = ComponentManager(env)
|
|
|
|
|
component_manager.handle_component_settings()
|
|
|
|
|
silent_action = env.Action(component_manager.restore_pioarduino_build_py)
|
2025-09-17 00:09:47 +02:00
|
|
|
# silence scons command output
|
2025-07-03 17:12:25 +02:00
|
|
|
silent_action.strfunction = lambda target, source, env: ''
|
|
|
|
|
env.AddPostAction("checkprogsize", silent_action)
|
|
|
|
|
|
2025-01-18 15:47:53 +01:00
|
|
|
if IS_WINDOWS:
|
2025-07-03 17:12:25 +02:00
|
|
|
env.AddBuildMiddleware(smart_include_length_shorten)
|
|
|
|
|
|
2025-09-17 00:09:47 +02:00
|
|
|
build_script_path = str(Path(FRAMEWORK_DIR) / "tools" / "pioarduino-build.py")
|
2025-07-03 17:12:25 +02:00
|
|
|
SConscript(build_script_path)
|