fbuild 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fbuild might be problematic. Click here for more details.

Files changed (93) hide show
  1. fbuild/__init__.py +0 -0
  2. fbuild/assets/example.txt +1 -0
  3. fbuild/build/__init__.py +117 -0
  4. fbuild/build/archive_creator.py +186 -0
  5. fbuild/build/binary_generator.py +444 -0
  6. fbuild/build/build_component_factory.py +131 -0
  7. fbuild/build/build_state.py +325 -0
  8. fbuild/build/build_utils.py +98 -0
  9. fbuild/build/compilation_executor.py +422 -0
  10. fbuild/build/compiler.py +165 -0
  11. fbuild/build/compiler_avr.py +574 -0
  12. fbuild/build/configurable_compiler.py +612 -0
  13. fbuild/build/configurable_linker.py +637 -0
  14. fbuild/build/flag_builder.py +186 -0
  15. fbuild/build/library_dependency_processor.py +185 -0
  16. fbuild/build/linker.py +708 -0
  17. fbuild/build/orchestrator.py +67 -0
  18. fbuild/build/orchestrator_avr.py +656 -0
  19. fbuild/build/orchestrator_esp32.py +797 -0
  20. fbuild/build/orchestrator_teensy.py +543 -0
  21. fbuild/build/source_compilation_orchestrator.py +220 -0
  22. fbuild/build/source_scanner.py +516 -0
  23. fbuild/cli.py +566 -0
  24. fbuild/cli_utils.py +312 -0
  25. fbuild/config/__init__.py +16 -0
  26. fbuild/config/board_config.py +457 -0
  27. fbuild/config/board_loader.py +92 -0
  28. fbuild/config/ini_parser.py +209 -0
  29. fbuild/config/mcu_specs.py +88 -0
  30. fbuild/daemon/__init__.py +34 -0
  31. fbuild/daemon/client.py +929 -0
  32. fbuild/daemon/compilation_queue.py +293 -0
  33. fbuild/daemon/daemon.py +474 -0
  34. fbuild/daemon/daemon_context.py +196 -0
  35. fbuild/daemon/error_collector.py +263 -0
  36. fbuild/daemon/file_cache.py +332 -0
  37. fbuild/daemon/lock_manager.py +270 -0
  38. fbuild/daemon/logging_utils.py +149 -0
  39. fbuild/daemon/messages.py +301 -0
  40. fbuild/daemon/operation_registry.py +288 -0
  41. fbuild/daemon/process_tracker.py +366 -0
  42. fbuild/daemon/processors/__init__.py +12 -0
  43. fbuild/daemon/processors/build_processor.py +157 -0
  44. fbuild/daemon/processors/deploy_processor.py +327 -0
  45. fbuild/daemon/processors/monitor_processor.py +146 -0
  46. fbuild/daemon/request_processor.py +401 -0
  47. fbuild/daemon/status_manager.py +216 -0
  48. fbuild/daemon/subprocess_manager.py +316 -0
  49. fbuild/deploy/__init__.py +17 -0
  50. fbuild/deploy/deployer.py +67 -0
  51. fbuild/deploy/deployer_esp32.py +314 -0
  52. fbuild/deploy/monitor.py +495 -0
  53. fbuild/interrupt_utils.py +34 -0
  54. fbuild/packages/__init__.py +53 -0
  55. fbuild/packages/archive_utils.py +1098 -0
  56. fbuild/packages/arduino_core.py +412 -0
  57. fbuild/packages/cache.py +249 -0
  58. fbuild/packages/downloader.py +366 -0
  59. fbuild/packages/framework_esp32.py +538 -0
  60. fbuild/packages/framework_teensy.py +346 -0
  61. fbuild/packages/github_utils.py +96 -0
  62. fbuild/packages/header_trampoline_cache.py +394 -0
  63. fbuild/packages/library_compiler.py +203 -0
  64. fbuild/packages/library_manager.py +549 -0
  65. fbuild/packages/library_manager_esp32.py +413 -0
  66. fbuild/packages/package.py +163 -0
  67. fbuild/packages/platform_esp32.py +383 -0
  68. fbuild/packages/platform_teensy.py +312 -0
  69. fbuild/packages/platform_utils.py +131 -0
  70. fbuild/packages/platformio_registry.py +325 -0
  71. fbuild/packages/sdk_utils.py +231 -0
  72. fbuild/packages/toolchain.py +436 -0
  73. fbuild/packages/toolchain_binaries.py +196 -0
  74. fbuild/packages/toolchain_esp32.py +484 -0
  75. fbuild/packages/toolchain_metadata.py +185 -0
  76. fbuild/packages/toolchain_teensy.py +404 -0
  77. fbuild/platform_configs/esp32.json +150 -0
  78. fbuild/platform_configs/esp32c2.json +144 -0
  79. fbuild/platform_configs/esp32c3.json +143 -0
  80. fbuild/platform_configs/esp32c5.json +151 -0
  81. fbuild/platform_configs/esp32c6.json +151 -0
  82. fbuild/platform_configs/esp32p4.json +149 -0
  83. fbuild/platform_configs/esp32s3.json +151 -0
  84. fbuild/platform_configs/imxrt1062.json +56 -0
  85. fbuild-1.1.0.dist-info/METADATA +447 -0
  86. fbuild-1.1.0.dist-info/RECORD +93 -0
  87. fbuild-1.1.0.dist-info/WHEEL +5 -0
  88. fbuild-1.1.0.dist-info/entry_points.txt +5 -0
  89. fbuild-1.1.0.dist-info/licenses/LICENSE +21 -0
  90. fbuild-1.1.0.dist-info/top_level.txt +2 -0
  91. fbuild_lint/__init__.py +0 -0
  92. fbuild_lint/ruff_plugins/__init__.py +0 -0
  93. fbuild_lint/ruff_plugins/keyboard_interrupt_checker.py +158 -0
@@ -0,0 +1,538 @@
1
+ """ESP32 Framework Management.
2
+
3
+ This module handles downloading, extracting, and managing the Arduino-ESP32 framework
4
+ and ESP-IDF libraries needed for ESP32 builds.
5
+
6
+ Framework Download Process:
7
+ 1. Download Arduino-ESP32 core (framework-arduinoespressif32)
8
+ 2. Download ESP-IDF precompiled libraries (framework-arduinoespressif32-libs)
9
+ 3. Extract both archives (.tar.xz format)
10
+ 4. Provide access to cores/, variants/, libraries/, and tools/
11
+
12
+ Framework Structure (after extraction):
13
+ framework-arduinoespressif32/
14
+ ├── cores/
15
+ │ └── esp32/ # Arduino core implementation
16
+ │ ├── Arduino.h
17
+ │ ├── main.cpp
18
+ │ ├── wiring.c
19
+ │ └── ...
20
+ ├── variants/
21
+ │ └── esp32c6/ # Board-specific variant
22
+ │ ├── pins_arduino.h
23
+ │ └── ...
24
+ ├── libraries/ # Built-in libraries (Wire, SPI, WiFi, etc.)
25
+ │ ├── WiFi/
26
+ │ ├── Wire/
27
+ │ ├── SPI/
28
+ │ └── ...
29
+ ├── tools/ # Build tools and SDKs
30
+ │ └── sdk/
31
+ │ └── esp32c6/
32
+ │ ├── include/ # ESP-IDF headers
33
+ │ └── lib/ # Precompiled libraries
34
+ └── package.json # Framework metadata
35
+
36
+ ESP-IDF Libraries Structure:
37
+ framework-arduinoespressif32-libs/
38
+ └── esp32c6/
39
+ ├── include/ # ESP-IDF headers
40
+ │ ├── esp_system/
41
+ │ ├── freertos/
42
+ │ └── ...
43
+ └── lib/ # Precompiled .a libraries
44
+ ├── libesp_system.a
45
+ ├── libfreertos.a
46
+ └── ...
47
+ """
48
+
49
+ import _thread
50
+ import json
51
+ from pathlib import Path
52
+ from typing import Any, Dict, List, Optional
53
+
54
+ from .archive_utils import ArchiveExtractor, URLVersionExtractor
55
+ from .cache import Cache
56
+ from .downloader import DownloadError, ExtractionError
57
+ from .header_trampoline_cache import HeaderTrampolineCache
58
+ from .package import IFramework, PackageError
59
+ from .sdk_utils import SDKPathResolver
60
+
61
+
62
+ class FrameworkErrorESP32(PackageError):
63
+ """Raised when ESP32 framework operations fail."""
64
+
65
+ pass
66
+
67
+
68
+ class FrameworkESP32(IFramework):
69
+ """Manages ESP32 framework download, extraction, and access.
70
+
71
+ This class handles the Arduino-ESP32 framework which includes:
72
+ - Arduino core for ESP32 family (cores/, variants/)
73
+ - Built-in Arduino libraries (WiFi, Wire, SPI, etc.)
74
+ - ESP-IDF precompiled libraries
75
+ - Tools and SDKs for ESP32 development
76
+ """
77
+
78
+ def __init__(
79
+ self,
80
+ cache: Cache,
81
+ framework_url: str,
82
+ libs_url: str,
83
+ skeleton_lib_url: Optional[str] = None,
84
+ show_progress: bool = True,
85
+ ):
86
+ """Initialize ESP32 framework manager.
87
+
88
+ Args:
89
+ cache: Cache manager instance
90
+ framework_url: URL to Arduino-ESP32 core package (.tar.xz)
91
+ libs_url: URL to ESP-IDF precompiled libraries (.tar.xz)
92
+ skeleton_lib_url: Optional URL to MCU-specific skeleton library
93
+ show_progress: Whether to show download/extraction progress
94
+ """
95
+ self.cache = cache
96
+ self.framework_url = framework_url
97
+ self.libs_url = libs_url
98
+ self.skeleton_lib_url = skeleton_lib_url
99
+ self.show_progress = show_progress
100
+ self.archive_extractor = ArchiveExtractor(show_progress=show_progress)
101
+
102
+ # Extract version from URL (e.g., "3.3.4" from release tag)
103
+ self.version = URLVersionExtractor.extract_version_from_url(framework_url, prefix="esp32-")
104
+
105
+ # Get framework paths from cache
106
+ # We'll use a combined hash for all URLs to keep them together
107
+ combined_url = f"{framework_url}|{libs_url}|{skeleton_lib_url or ''}"
108
+ self.framework_path = cache.get_platform_path(combined_url, self.version)
109
+
110
+ def ensure_package(self) -> Path:
111
+ """Ensure framework is downloaded and extracted.
112
+
113
+ Returns:
114
+ Path to the extracted framework directory
115
+
116
+ Raises:
117
+ FrameworkErrorESP32: If download or extraction fails
118
+ """
119
+ return self.ensure_framework()
120
+
121
+ def ensure_framework(self) -> Path:
122
+ """Ensure framework is downloaded and extracted.
123
+
124
+ Returns:
125
+ Path to the extracted framework directory
126
+
127
+ Raises:
128
+ FrameworkErrorESP32: If download or extraction fails
129
+ """
130
+ if self.is_installed():
131
+ if self.show_progress:
132
+ print(f"Using cached ESP32 framework {self.version}")
133
+ return self.framework_path
134
+
135
+ try:
136
+ if self.show_progress:
137
+ print(f"Downloading ESP32 framework {self.version}...")
138
+
139
+ # Download and extract framework package
140
+ self.cache.ensure_directories()
141
+
142
+ # Create framework directory
143
+ self.framework_path.mkdir(parents=True, exist_ok=True)
144
+
145
+ # Download and extract Arduino core
146
+ self.archive_extractor.download_and_extract(self.framework_url, self.framework_path, "Arduino-ESP32 core")
147
+
148
+ # Download and extract ESP-IDF libraries (if URL is not empty)
149
+ if self.libs_url:
150
+ self.archive_extractor.download_and_extract(
151
+ self.libs_url,
152
+ self.framework_path / "tools" / "sdk",
153
+ "ESP-IDF libraries",
154
+ )
155
+
156
+ # Download and extract skeleton library if provided
157
+ if self.skeleton_lib_url:
158
+ if self.show_progress:
159
+ print("Downloading skeleton library...")
160
+ self.archive_extractor.download_and_extract(
161
+ self.skeleton_lib_url,
162
+ self.framework_path / "tools" / "sdk",
163
+ "Skeleton library",
164
+ )
165
+
166
+ if self.show_progress:
167
+ print(f"ESP32 framework installed to {self.framework_path}")
168
+
169
+ # Post-install: Generate header trampolines for all MCU variants
170
+ # This pre-generates trampoline caches to avoid Windows command-line length issues
171
+ self._post_install_generate_trampolines()
172
+
173
+ return self.framework_path
174
+
175
+ except (DownloadError, ExtractionError) as e:
176
+ raise FrameworkErrorESP32(f"Failed to install ESP32 framework: {e}")
177
+ except KeyboardInterrupt as ke:
178
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
179
+
180
+ handle_keyboard_interrupt_properly(ke)
181
+ raise # Never reached, but satisfies type checker
182
+ except Exception as e:
183
+ raise FrameworkErrorESP32(f"Unexpected error installing framework: {e}")
184
+
185
+ def _post_install_generate_trampolines(self) -> None:
186
+ """Generate header trampolines for all MCU variants after framework installation.
187
+
188
+ This post-install step pre-generates trampoline caches for all ESP32 MCU variants
189
+ found in the SDK. This ensures that Windows builds with sccache don't hit the
190
+ 32K command-line length limit due to excessive -I arguments.
191
+
192
+ The trampolines are generated once at framework install time rather than at
193
+ every compile time, improving build performance.
194
+ """
195
+ import platform as platform_module
196
+
197
+ # Only generate trampolines on Windows
198
+ if platform_module.system() != "Windows":
199
+ if self.show_progress:
200
+ print("[trampolines] Skipping post-install generation (not Windows)")
201
+ return
202
+
203
+ try:
204
+ if self.show_progress:
205
+ print("[trampolines] Post-install: Generating header trampoline caches...")
206
+
207
+ # Find all MCU variants in the SDK
208
+ sdk_dir = self.get_sdk_dir()
209
+ if not sdk_dir.exists():
210
+ if self.show_progress:
211
+ print("[trampolines] Warning: SDK directory not found, skipping")
212
+ return
213
+
214
+ # Look for MCU-specific directories
215
+ mcu_variants = []
216
+ for item in sdk_dir.iterdir():
217
+ if item.is_dir() and item.name.startswith("esp32"):
218
+ mcu_variants.append(item.name)
219
+
220
+ if not mcu_variants:
221
+ if self.show_progress:
222
+ print("[trampolines] Warning: No MCU variants found in SDK")
223
+ return
224
+
225
+ if self.show_progress:
226
+ print(f"[trampolines] Found {len(mcu_variants)} MCU variant(s): {', '.join(mcu_variants)}")
227
+
228
+ # Generate trampolines for each MCU variant
229
+ trampoline_cache = HeaderTrampolineCache(show_progress=self.show_progress)
230
+
231
+ # Exclude ESP-IDF headers that use relative paths or #include_next that break trampolines
232
+ exclude_patterns = [
233
+ "newlib/platform_include", # Uses #include_next which breaks trampolines
234
+ "newlib\\platform_include", # Windows path variant
235
+ "/bt/", # Bluetooth SDK uses relative paths between bt/include and bt/controller
236
+ "\\bt\\", # Windows path variant
237
+ ]
238
+
239
+ for mcu in mcu_variants:
240
+ try:
241
+ # Get SDK include paths for this MCU
242
+ include_paths = self.get_sdk_includes(mcu)
243
+
244
+ if self.show_progress:
245
+ print(f"[trampolines] Generating cache for {mcu} ({len(include_paths)} include paths)...")
246
+
247
+ # Generate trampolines with exclusions (this will create C:/inc/NNN directories)
248
+ trampoline_paths = trampoline_cache.generate_trampolines(include_paths, exclude_patterns=exclude_patterns)
249
+
250
+ if self.show_progress:
251
+ print(f"[trampolines] Generated {len(trampoline_paths)} trampoline directories for {mcu}")
252
+
253
+ except KeyboardInterrupt:
254
+ _thread.interrupt_main()
255
+ raise
256
+ except Exception as e:
257
+ if self.show_progress:
258
+ print(f"[trampolines] Warning: Failed to generate trampolines for {mcu}: {e}")
259
+ continue
260
+
261
+ if self.show_progress:
262
+ print("[trampolines] Post-install generation complete")
263
+
264
+ except KeyboardInterrupt:
265
+ _thread.interrupt_main()
266
+ raise
267
+ except Exception as e:
268
+ # Don't fail the entire installation if trampoline generation fails
269
+ if self.show_progress:
270
+ print(f"[trampolines] Warning: Post-install generation failed: {e}")
271
+
272
+ def is_installed(self) -> bool:
273
+ """Check if framework is already installed.
274
+
275
+ Returns:
276
+ True if framework directory exists with key files
277
+ """
278
+ if not self.framework_path.exists():
279
+ return False
280
+
281
+ # Verify essential framework directories exist
282
+ required_paths = [
283
+ self.framework_path / "cores",
284
+ self.framework_path / "variants",
285
+ self.framework_path / "tools" / "sdk",
286
+ ]
287
+
288
+ return all(p.exists() for p in required_paths)
289
+
290
+ def get_cores_dir(self) -> Path:
291
+ """Get path to cores directory.
292
+
293
+ Returns:
294
+ Path to cores directory containing Arduino core implementation
295
+ """
296
+ return self.framework_path / "cores"
297
+
298
+ def get_core_dir(self, core_name: str = "esp32") -> Path:
299
+ """Get path to specific core directory.
300
+
301
+ Args:
302
+ core_name: Core name (default: "esp32")
303
+
304
+ Returns:
305
+ Path to the core directory
306
+
307
+ Raises:
308
+ FrameworkErrorESP32: If core directory doesn't exist
309
+ """
310
+ core_path = self.get_cores_dir() / core_name
311
+ if not core_path.exists():
312
+ raise FrameworkErrorESP32(f"Core '{core_name}' not found at {core_path}")
313
+ return core_path
314
+
315
+ def get_variants_dir(self) -> Path:
316
+ """Get path to variants directory.
317
+
318
+ Returns:
319
+ Path to variants directory containing board-specific configurations
320
+ """
321
+ return self.framework_path / "variants"
322
+
323
+ def get_variant_dir(self, variant_name: str) -> Path:
324
+ """Get path to specific variant directory.
325
+
326
+ Args:
327
+ variant_name: Variant name (e.g., "esp32c6")
328
+
329
+ Returns:
330
+ Path to the variant directory
331
+
332
+ Raises:
333
+ FrameworkErrorESP32: If variant directory doesn't exist
334
+ """
335
+ variant_path = self.get_variants_dir() / variant_name
336
+ if not variant_path.exists():
337
+ raise FrameworkErrorESP32(f"Variant '{variant_name}' not found at {variant_path}")
338
+ return variant_path
339
+
340
+ def get_libraries_dir(self) -> Path:
341
+ """Get path to built-in libraries directory.
342
+
343
+ Returns:
344
+ Path to libraries directory
345
+ """
346
+ return self.framework_path / "libraries"
347
+
348
+ def list_libraries(self) -> List[str]:
349
+ """List all available built-in libraries.
350
+
351
+ Returns:
352
+ List of library names
353
+ """
354
+ libs_dir = self.get_libraries_dir()
355
+ if not libs_dir.exists():
356
+ return []
357
+
358
+ return [d.name for d in libs_dir.iterdir() if d.is_dir() and not d.name.startswith(".")]
359
+
360
+ def get_library_dir(self, library_name: str) -> Optional[Path]:
361
+ """Get path to a specific library.
362
+
363
+ Args:
364
+ library_name: Library name (e.g., "WiFi", "Wire")
365
+
366
+ Returns:
367
+ Path to library directory or None if not found
368
+ """
369
+ lib_path = self.get_libraries_dir() / library_name
370
+ return lib_path if lib_path.exists() else None
371
+
372
+ def get_sdk_dir(self) -> Path:
373
+ """Get path to ESP-IDF SDK directory.
374
+
375
+ Returns:
376
+ Path to SDK directory containing ESP-IDF headers and libraries
377
+ """
378
+ return self.framework_path / "tools" / "sdk"
379
+
380
+ def get_sdk_includes(self, mcu: str) -> List[Path]:
381
+ """Get list of ESP-IDF include directories for a specific MCU.
382
+
383
+ This method reads the SDK's own includes file which lists the exact
384
+ include paths used by ESP-IDF, avoiding C++ stdlib conflicts that
385
+ occur when recursively discovering paths.
386
+
387
+ Args:
388
+ mcu: MCU type (e.g., "esp32c6", "esp32s3")
389
+
390
+ Returns:
391
+ List of include directory paths (305 paths for esp32c6)
392
+ """
393
+ sdk_resolver = SDKPathResolver(self.get_sdk_dir(), self.show_progress)
394
+ return sdk_resolver.get_sdk_includes(mcu)
395
+
396
+ def get_sdk_libs(self, mcu: str, flash_mode: str = "qio") -> List[Path]:
397
+ """Get list of ESP-IDF precompiled libraries for a specific MCU.
398
+
399
+ Args:
400
+ mcu: MCU type (e.g., "esp32c6", "esp32s3")
401
+ flash_mode: Flash mode (e.g., "qio", "dio") - determines flash library variant
402
+
403
+ Returns:
404
+ List of .a library file paths
405
+ """
406
+ sdk_resolver = SDKPathResolver(self.get_sdk_dir(), self.show_progress)
407
+ return sdk_resolver.get_sdk_libs(mcu, flash_mode)
408
+
409
+ def get_sdk_flags_dir(self, mcu: str) -> Path:
410
+ """Get path to SDK flags directory for a specific MCU.
411
+
412
+ Args:
413
+ mcu: MCU type (e.g., "esp32c6", "esp32s3")
414
+
415
+ Returns:
416
+ Path to flags directory
417
+ """
418
+ sdk_resolver = SDKPathResolver(self.get_sdk_dir(), self.show_progress)
419
+ return sdk_resolver.get_sdk_flags_dir(mcu)
420
+
421
+ def get_tools_dir(self) -> Path:
422
+ """Get path to tools directory.
423
+
424
+ Returns:
425
+ Path to tools directory
426
+ """
427
+ return self.framework_path / "tools"
428
+
429
+ def get_package_json(self) -> Dict[str, Any]:
430
+ """Load and parse package.json metadata.
431
+
432
+ Returns:
433
+ Dictionary containing package metadata
434
+
435
+ Raises:
436
+ FrameworkErrorESP32: If package.json doesn't exist or is invalid
437
+ """
438
+ package_json_path = self.framework_path / "package.json"
439
+
440
+ if not package_json_path.exists():
441
+ raise FrameworkErrorESP32(f"package.json not found at {package_json_path}. " + "Ensure framework is downloaded first.")
442
+
443
+ try:
444
+ with open(package_json_path, "r", encoding="utf-8") as f:
445
+ return json.load(f)
446
+ except json.JSONDecodeError as e:
447
+ raise FrameworkErrorESP32(f"Failed to parse package.json: {e}")
448
+ except KeyboardInterrupt as ke:
449
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
450
+
451
+ handle_keyboard_interrupt_properly(ke)
452
+ raise # Never reached, but satisfies type checker
453
+ except Exception as e:
454
+ raise FrameworkErrorESP32(f"Failed to read package.json: {e}")
455
+
456
+ def list_variants(self) -> List[str]:
457
+ """List all available board variants.
458
+
459
+ Returns:
460
+ List of variant names
461
+ """
462
+ variants_dir = self.get_variants_dir()
463
+ if not variants_dir.exists():
464
+ return []
465
+
466
+ return [d.name for d in variants_dir.iterdir() if d.is_dir() and not d.name.startswith(".")]
467
+
468
+ def list_cores(self) -> List[str]:
469
+ """List all available cores.
470
+
471
+ Returns:
472
+ List of core names
473
+ """
474
+ cores_dir = self.get_cores_dir()
475
+ if not cores_dir.exists():
476
+ return []
477
+
478
+ return [d.name for d in cores_dir.iterdir() if d.is_dir() and not d.name.startswith(".")]
479
+
480
+ def get_core_sources(self, core_name: str = "esp32") -> List[Path]:
481
+ """Get all source files in a core.
482
+
483
+ Args:
484
+ core_name: Core name (default: "esp32")
485
+
486
+ Returns:
487
+ List of .c and .cpp source file paths
488
+ """
489
+ core_dir = self.get_core_dir(core_name)
490
+ sources = []
491
+ sources.extend(core_dir.glob("*.c"))
492
+ sources.extend(core_dir.glob("*.cpp"))
493
+ # Also search in subdirectories
494
+ sources.extend(core_dir.glob("**/*.c"))
495
+ sources.extend(core_dir.glob("**/*.cpp"))
496
+ # Remove duplicates
497
+ return list(set(sources))
498
+
499
+ def get_package_info(self) -> Dict[str, Any]:
500
+ """Get information about the installed framework.
501
+
502
+ Returns:
503
+ Dictionary with framework information
504
+ """
505
+ return self.get_framework_info()
506
+
507
+ def get_framework_info(self) -> Dict[str, Any]:
508
+ """Get information about the installed framework.
509
+
510
+ Returns:
511
+ Dictionary with framework information
512
+ """
513
+ info = {
514
+ "version": self.version,
515
+ "path": str(self.framework_path),
516
+ "framework_url": self.framework_url,
517
+ "libs_url": self.libs_url,
518
+ "installed": self.is_installed(),
519
+ }
520
+
521
+ if self.is_installed():
522
+ info["cores_dir"] = str(self.get_cores_dir())
523
+ info["variants_dir"] = str(self.get_variants_dir())
524
+ info["libraries_dir"] = str(self.get_libraries_dir())
525
+ info["sdk_dir"] = str(self.get_sdk_dir())
526
+ info["available_cores"] = self.list_cores()
527
+ info["available_variants"] = self.list_variants()
528
+ info["available_libraries"] = self.list_libraries()
529
+
530
+ # Get package information
531
+ try:
532
+ package_json = self.get_package_json()
533
+ info["package_version"] = package_json.get("version")
534
+ info["package_name"] = package_json.get("name")
535
+ except FrameworkErrorESP32:
536
+ pass
537
+
538
+ return info