Skip to content

Commit f2880bc

Browse files
authored
include path remove with smart components detection logic
1 parent 19ab750 commit f2880bc

File tree

1 file changed

+107
-54
lines changed

1 file changed

+107
-54
lines changed

builder/frameworks/component_manager.py

Lines changed: 107 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,110 @@ def _get_lib_ignore_entries(self) -> List[str]:
116116
except Exception:
117117
return []
118118

119+
def _analyze_project_dependencies(self) -> Set[str]:
120+
"""Analyze project files to detect actually used components/libraries."""
121+
used_components = set()
122+
123+
try:
124+
# Analyze project source files
125+
src_dir = self.env.subst("$PROJECT_SRC_DIR")
126+
if os.path.exists(src_dir):
127+
for root, dirs, files in os.walk(src_dir):
128+
for file in files:
129+
if file.endswith(('.cpp', '.c', '.h', '.hpp', '.ino')):
130+
file_path = os.path.join(root, file)
131+
used_components.update(self._extract_components_from_file(file_path))
132+
133+
# Analyze lib_deps for explicit dependencies (if available)
134+
lib_deps = self.env.GetProjectOption("lib_deps", [])
135+
if isinstance(lib_deps, str):
136+
lib_deps = [lib_deps]
137+
138+
for dep in lib_deps:
139+
used_components.update(self._extract_components_from_lib_dep(str(dep)))
140+
141+
except Exception:
142+
pass
143+
144+
return used_components
145+
146+
def _extract_components_from_file(self, file_path: str) -> Set[str]:
147+
"""Extract component usage from a single file by analyzing includes and function calls."""
148+
components = set()
149+
150+
# Component detection patterns - maps component names to search patterns
151+
component_patterns = {
152+
'bt': ['bluetooth', 'ble', 'nimble', 'bt_', 'esp_bt', 'esp_ble'],
153+
'esp_wifi': ['wifi', 'esp_wifi', 'tcpip_adapter'],
154+
'esp_dsp': ['dsps_', 'esp_dsp', 'fft2r'],
155+
'esp_http_client': ['esp_http_client', 'http_client'],
156+
'esp_https_ota': ['esp_https_ota', 'esp_ota'],
157+
'mdns': ['mdns', 'esp_mdns'],
158+
'mqtt': ['mqtt', 'esp_mqtt'],
159+
'spiffs': ['spiffs', 'esp_spiffs'],
160+
'fatfs': ['fatfs', 'ff.h'],
161+
'nvs_flash': ['nvs', 'nvs_flash'],
162+
'esp_timer': ['esp_timer', 'timer_'],
163+
'driver': ['gpio_', 'uart_', 'spi_', 'i2c_', 'adc_', 'dac_'],
164+
'esp_camera': ['esp_camera', 'camera.h'],
165+
'esp_now': ['esp_now', 'espnow'],
166+
'esp_smartconfig': ['smartconfig', 'esp_smartconfig']
167+
}
168+
169+
try:
170+
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
171+
content = f.read().lower()
172+
173+
# Check each component pattern against file content
174+
for component, patterns in component_patterns.items():
175+
if any(pattern in content for pattern in patterns):
176+
components.add(component)
177+
178+
except Exception:
179+
pass
180+
181+
return components
182+
183+
def _extract_components_from_lib_dep(self, lib_dep: str) -> Set[str]:
184+
"""Extract components from lib_deps entry by mapping library names to components."""
185+
components = set()
186+
lib_dep_upper = lib_dep.upper()
187+
188+
# Map lib_deps entries to ESP-IDF components
189+
lib_dep_mapping = {
190+
'bt': ['BLE', 'BT', 'BLUETOOTH', 'NIMBLE'],
191+
'esp_wifi': ['WIFI', 'ASYNCTCP', 'ESPASYNCWEBSERVER'],
192+
'esp_dsp': ['DSP', 'FFT', 'JPEG'],
193+
'esp_http_client': ['HTTP', 'HTTPCLIENT'],
194+
'mqtt': ['MQTT', 'PUBSUB'],
195+
'esp_camera': ['CAMERA', 'ESP32CAM']
196+
}
197+
198+
for component, keywords in lib_dep_mapping.items():
199+
if any(keyword in lib_dep_upper for keyword in keywords):
200+
components.add(component)
201+
202+
return components
203+
204+
def _is_component_used_in_project(self, lib_name: str) -> bool:
205+
"""Check if a component/library is actually used in the project."""
206+
# Cache project analysis results for performance
207+
if not hasattr(self, '_project_components_cache'):
208+
self._project_components_cache = self._analyze_project_dependencies()
209+
210+
lib_name_lower = lib_name.lower()
211+
212+
# Direct match
213+
if lib_name_lower in self._project_components_cache:
214+
return True
215+
216+
# Partial match for related components
217+
for used_component in self._project_components_cache:
218+
if lib_name_lower in used_component or used_component in lib_name_lower:
219+
return True
220+
221+
return False
222+
119223
def _get_arduino_core_libraries(self) -> Dict[str, str]:
120224
"""Get all Arduino core libraries and their corresponding include paths."""
121225
libraries_mapping = {}
@@ -297,64 +401,13 @@ def _convert_lib_name_to_include(self, lib_name: str) -> str:
297401

298402
return cleaned_name
299403

300-
def _has_bt_ble_dependencies(self) -> bool:
301-
"""Check if lib_deps contains any BT/BLE related dependencies."""
302-
try:
303-
# Get lib_deps from current environment
304-
lib_deps = self.env.GetProjectOption("lib_deps", [])
305-
306-
if isinstance(lib_deps, str):
307-
lib_deps = [lib_deps]
308-
elif lib_deps is None:
309-
lib_deps = []
310-
311-
# Convert to string and check for BT/BLE keywords
312-
lib_deps_str = ' '.join(str(dep) for dep in lib_deps).upper()
313-
314-
bt_ble_keywords = ['BLE', 'BT', 'NIMBLE', 'BLUETOOTH']
315-
316-
for keyword in bt_ble_keywords:
317-
if keyword in lib_deps_str:
318-
return True
319-
320-
return False
321-
322-
except Exception:
323-
return False
324-
325-
def _is_bt_related_library(self, lib_name: str) -> bool:
326-
"""Check if a library name is related to Bluetooth/BLE functionality."""
327-
lib_name_upper = lib_name.upper()
328-
329-
bt_related_names = [
330-
'BT',
331-
'BLE',
332-
'BLUETOOTH',
333-
'NIMBLE',
334-
'ESP32_BLE',
335-
'ESP32BLE',
336-
'BLUETOOTHSERIAL',
337-
'BLE_ARDUINO',
338-
'ESP_BLE',
339-
'ESP_BT'
340-
]
341-
342-
for bt_name in bt_related_names:
343-
if bt_name in lib_name_upper:
344-
return True
345-
346-
return False
347-
348404
def _remove_ignored_lib_includes(self) -> None:
349405
"""Remove include entries for ignored libraries from pioarduino-build.py."""
350406
build_py_path = join(self.arduino_libs_mcu, "pioarduino-build.py")
351407

352408
if not os.path.exists(build_py_path):
353409
return
354410

355-
# Check if BT/BLE dependencies exist in lib_deps
356-
bt_ble_protected = self._has_bt_ble_dependencies()
357-
358411
try:
359412
with open(build_py_path, 'r') as f:
360413
content = f.read()
@@ -364,9 +417,9 @@ def _remove_ignored_lib_includes(self) -> None:
364417

365418
# Remove CPPPATH entries for each ignored library
366419
for lib_name in self.ignored_libs:
367-
# Skip BT-related libraries if BT/BLE dependencies are present
368-
if bt_ble_protected and self._is_bt_related_library(lib_name):
369-
print(f"Skipping removal of BT-related library '{lib_name}' due to BT/BLE dependency in lib_deps")
420+
# Universal protection: Skip if component is actually used in project
421+
if self._is_component_used_in_project(lib_name):
422+
print(f"Skipping removal of library '{lib_name}' - detected as used in project")
370423
continue
371424

372425
# Multiple patterns to catch different include formats

0 commit comments

Comments
 (0)