@@ -116,50 +116,113 @@ def _get_lib_ignore_entries(self) -> List[str]:
116
116
except Exception :
117
117
return []
118
118
119
- def _has_bt_ble_dependencies (self ) -> bool :
120
- """Check if lib_deps contains any BT/BLE related dependencies."""
119
+ def _analyze_project_dependencies (self ) -> Set [str ]:
120
+ """Analyze project files to detect actually used components/libraries."""
121
+ used_components = set ()
122
+
121
123
try :
122
- # Get lib_deps from current environment
123
- lib_deps = self .env .GetProjectOption ("lib_deps" , [])
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 ))
124
132
133
+ # Analyze lib_deps for explicit dependencies (if present)
134
+ lib_deps = self .env .GetProjectOption ("lib_deps" , [])
125
135
if isinstance (lib_deps , str ):
126
136
lib_deps = [lib_deps ]
127
- elif lib_deps is None :
128
- lib_deps = []
129
-
130
- # Convert to string and check for BT/BLE keywords
131
- lib_deps_str = ' ' .join (str (dep ) for dep in lib_deps ).upper ()
132
-
133
- bt_ble_keywords = ['BLE' , 'BT' , 'NIMBLE' , 'BLUETOOTH' ]
134
-
135
- for keyword in bt_ble_keywords :
136
- if keyword in lib_deps_str :
137
- return True
138
-
139
- return False
140
137
138
+ for dep in lib_deps :
139
+ used_components .update (self ._extract_components_from_lib_dep (str (dep )))
140
+
141
141
except Exception :
142
- return False
142
+ pass
143
+
144
+ return used_components
143
145
144
- def _is_bt_related_library (self , lib_name : str ) -> bool :
145
- """Check if a library name is related to Bluetooth/BLE functionality."""
146
- lib_name_upper = lib_name .upper ()
147
-
148
- bt_related_names = [
149
- 'BT' ,
150
- 'BLE' ,
151
- 'BLUETOOTH' ,
152
- 'NIMBLE' ,
153
- 'ESP32_BLE' ,
154
- 'ESP32BLE' ,
155
- 'BLUETOOTHSERIAL' ,
156
- 'BLE_ARDUINO' ,
157
- 'ESP_BLE' ,
158
- 'ESP_BT'
159
- ]
160
-
161
- for bt_name in bt_related_names :
162
- if bt_name in lib_name_upper :
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 code 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' , 'dsps_fft2r' ], # Enhanced DSP detection
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
+ 'esp_eth' : ['esp_eth' , 'ethernet' ],
168
+ 'esp_websocket_client' : ['websocket' , 'esp_websocket' ],
169
+ 'cjson' : ['cjson' , 'json' ],
170
+ 'mbedtls' : ['mbedtls' , 'ssl' ],
171
+ 'openssl' : ['openssl' ]
172
+ }
173
+
174
+ try :
175
+ with open (file_path , 'r' , encoding = 'utf-8' , errors = 'ignore' ) as f :
176
+ content = f .read ().lower ()
177
+
178
+ for component , patterns in component_patterns .items ():
179
+ if any (pattern in content for pattern in patterns ):
180
+ components .add (component )
181
+
182
+ except Exception :
183
+ pass
184
+
185
+ return components
186
+
187
+ def _extract_components_from_lib_dep (self , lib_dep : str ) -> Set [str ]:
188
+ """Extract components from lib_deps entry by mapping library names to ESP-IDF components."""
189
+ components = set ()
190
+ lib_dep_upper = lib_dep .upper ()
191
+
192
+ # Map lib_deps entries to ESP-IDF components
193
+ lib_dep_mapping = {
194
+ 'bt' : ['BLE' , 'BT' , 'BLUETOOTH' , 'NIMBLE' ],
195
+ 'esp_wifi' : ['WIFI' , 'ASYNCTCP' , 'ESPASYNCWEBSERVER' ],
196
+ 'esp_dsp' : ['DSP' , 'FFT' , 'JPEG' ],
197
+ 'esp_http_client' : ['HTTP' , 'HTTPCLIENT' ],
198
+ 'mqtt' : ['MQTT' , 'PUBSUB' ],
199
+ 'esp_camera' : ['CAMERA' , 'ESP32CAM' ],
200
+ 'esp_now' : ['ESPNOW' , 'ESP_NOW' ],
201
+ 'mdns' : ['MDNS' ],
202
+ 'esp_eth' : ['ETHERNET' ]
203
+ }
204
+
205
+ for component , keywords in lib_dep_mapping .items ():
206
+ if any (keyword in lib_dep_upper for keyword in keywords ):
207
+ components .add (component )
208
+
209
+ return components
210
+
211
+ def _is_component_used_in_project (self , lib_name : str ) -> bool :
212
+ """Check if a component/library is actually used in the project."""
213
+ # Cache project analysis for performance
214
+ if not hasattr (self , '_project_components_cache' ):
215
+ self ._project_components_cache = self ._analyze_project_dependencies ()
216
+
217
+ lib_name_lower = lib_name .lower ()
218
+
219
+ # Direct match
220
+ if lib_name_lower in self ._project_components_cache :
221
+ return True
222
+
223
+ # Partial match for related components
224
+ for used_component in self ._project_components_cache :
225
+ if lib_name_lower in used_component or used_component in lib_name_lower :
163
226
return True
164
227
165
228
return False
@@ -352,9 +415,6 @@ def _remove_ignored_lib_includes(self) -> None:
352
415
if not os .path .exists (build_py_path ):
353
416
return
354
417
355
- # Check if BT/BLE dependencies exist in lib_deps
356
- bt_ble_protected = self ._has_bt_ble_dependencies ()
357
-
358
418
try :
359
419
with open (build_py_path , 'r' ) as f :
360
420
content = f .read ()
@@ -364,14 +424,9 @@ def _remove_ignored_lib_includes(self) -> None:
364
424
365
425
# Remove CPPPATH entries for each ignored library
366
426
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" )
370
- continue
371
-
372
- # Hard protection for DSP components
373
- if lib_name .lower () in ['dsp' , 'esp_dsp' , 'dsps' , 'fft2r' , 'dsps_fft2r' ]:
374
- print (f"Hard-protected DSP component '{ lib_name } ' from removal" )
427
+ # Universal protection: Skip if component is actually used in project
428
+ if self ._is_component_used_in_project (lib_name ):
429
+ print (f"Skipping removal of library '{ lib_name } ' - detected as used in project" )
375
430
continue
376
431
377
432
# Multiple patterns to catch different include formats
0 commit comments