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,495 @@
1
+ """
2
+ Serial monitor module for embedded devices.
3
+
4
+ This module provides serial monitoring capabilities with optional halt conditions.
5
+ """
6
+
7
+ import _thread
8
+ import json
9
+ import re
10
+ import sys
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ from fbuild.cli_utils import safe_print
16
+ from fbuild.config import PlatformIOConfig
17
+
18
+
19
+ class MonitorError(Exception):
20
+ """Raised when monitor operations fail."""
21
+
22
+ pass
23
+
24
+
25
+ class SerialMonitor:
26
+ """Serial monitor for embedded devices."""
27
+
28
+ def __init__(self, verbose: bool = False):
29
+ """Initialize serial monitor.
30
+
31
+ Args:
32
+ verbose: Whether to show verbose output
33
+ """
34
+ self.verbose = verbose
35
+
36
+ def _write_summary(
37
+ self,
38
+ summary_file: Optional[Path],
39
+ expect: Optional[str],
40
+ expect_found: bool,
41
+ halt_on_error: Optional[str],
42
+ halt_on_error_found: bool,
43
+ halt_on_success: Optional[str],
44
+ halt_on_success_found: bool,
45
+ lines_processed: int,
46
+ elapsed_time: float,
47
+ exit_reason: str,
48
+ ) -> None:
49
+ """Write monitoring summary to JSON file.
50
+
51
+ Args:
52
+ summary_file: Path to write summary JSON
53
+ expect: Expected pattern (or None)
54
+ expect_found: Whether expect pattern was found
55
+ halt_on_error: Error pattern (or None)
56
+ halt_on_error_found: Whether error pattern was found
57
+ halt_on_success: Success pattern (or None)
58
+ halt_on_success_found: Whether success pattern was found
59
+ lines_processed: Total lines read from serial
60
+ elapsed_time: Time elapsed in seconds
61
+ exit_reason: Reason for exit (timeout/expect_found/halt_error/halt_success/interrupted/error)
62
+ """
63
+ if not summary_file:
64
+ return
65
+
66
+ summary = {
67
+ "expect_pattern": expect,
68
+ "expect_found": expect_found,
69
+ "halt_on_error_pattern": halt_on_error,
70
+ "halt_on_error_found": halt_on_error_found,
71
+ "halt_on_success_pattern": halt_on_success,
72
+ "halt_on_success_found": halt_on_success_found,
73
+ "lines_processed": lines_processed,
74
+ "elapsed_time": round(elapsed_time, 2),
75
+ "exit_reason": exit_reason,
76
+ }
77
+
78
+ try:
79
+ summary_file.parent.mkdir(parents=True, exist_ok=True)
80
+ with open(summary_file, "w", encoding="utf-8") as f:
81
+ json.dump(summary, f, indent=2)
82
+ except KeyboardInterrupt:
83
+ raise
84
+ except Exception as e:
85
+ # Silently fail - don't disrupt the monitor operation
86
+ if self.verbose:
87
+ print(f"Warning: Could not write summary file: {e}")
88
+
89
+ def monitor(
90
+ self,
91
+ project_dir: Path,
92
+ env_name: str,
93
+ port: Optional[str] = None,
94
+ baud: int = 115200,
95
+ timeout: Optional[int] = None,
96
+ halt_on_error: Optional[str] = None,
97
+ halt_on_success: Optional[str] = None,
98
+ expect: Optional[str] = None,
99
+ output_file: Optional[Path] = None,
100
+ summary_file: Optional[Path] = None,
101
+ ) -> int:
102
+ """Monitor serial output from device.
103
+
104
+ Args:
105
+ project_dir: Path to project directory
106
+ env_name: Environment name
107
+ port: Serial port to use (auto-detect if None)
108
+ baud: Baud rate (default: 115200)
109
+ timeout: Timeout in seconds (None for infinite)
110
+ halt_on_error: String pattern that triggers error exit
111
+ halt_on_success: String pattern that triggers success exit
112
+ expect: Expected pattern - checked at timeout/success for exit code
113
+ output_file: Optional file to write serial output to (for client streaming)
114
+ summary_file: Optional file to write summary JSON to (for client display)
115
+
116
+ Returns:
117
+ Exit code (0 for success, 1 for error)
118
+ """
119
+ try:
120
+ import serial
121
+ except ImportError:
122
+ print("Error: pyserial not installed. Install with: pip install pyserial")
123
+ return 1
124
+
125
+ # Load platformio.ini to get board config
126
+ ini_path = project_dir / "platformio.ini"
127
+ if not ini_path.exists():
128
+ print(f"Error: platformio.ini not found in {project_dir}")
129
+ return 1
130
+
131
+ config = PlatformIOConfig(ini_path)
132
+
133
+ try:
134
+ env_config = config.get_env_config(env_name)
135
+ except KeyboardInterrupt as ke:
136
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
137
+
138
+ handle_keyboard_interrupt_properly(ke)
139
+ except Exception as e:
140
+ print(f"Error: {e}")
141
+ return 1
142
+
143
+ # Get monitor baud rate from config if specified
144
+ monitor_speed = env_config.get("monitor_speed")
145
+ if monitor_speed:
146
+ try:
147
+ baud = int(monitor_speed)
148
+ except ValueError:
149
+ pass
150
+
151
+ # Auto-detect port if not specified
152
+ if not port:
153
+ port = self._detect_serial_port()
154
+ if not port:
155
+ print("Error: No serial port specified and auto-detection failed. " + "Use --port to specify a port.")
156
+ return 1
157
+
158
+ print(f"Opening serial port {port} at {baud} baud...")
159
+
160
+ ser = None
161
+ output_fp = None
162
+ try:
163
+ # Open serial port
164
+ ser = serial.Serial(
165
+ port,
166
+ baud,
167
+ timeout=0.1, # Short timeout for readline
168
+ )
169
+
170
+ # Reset the device to ensure we catch all output from the start
171
+ # This is necessary because the device may have already booted
172
+ # between esptool finishing and the monitor starting
173
+ ser.setDTR(False) # type: ignore[attr-defined]
174
+ ser.setRTS(True) # type: ignore[attr-defined]
175
+ time.sleep(0.1)
176
+ ser.setRTS(False) # type: ignore[attr-defined]
177
+ time.sleep(0.1)
178
+ ser.setDTR(True) # type: ignore[attr-defined]
179
+
180
+ print(f"Connected to {port}")
181
+ print("--- Serial Monitor (Ctrl+C to exit) ---")
182
+ print()
183
+
184
+ # Give device a moment to start booting after reset
185
+ time.sleep(0.2)
186
+
187
+ # Open output file for streaming (if specified)
188
+ if output_file:
189
+ try:
190
+ output_file.parent.mkdir(parents=True, exist_ok=True)
191
+ output_fp = open(output_file, "w", encoding="utf-8", errors="replace")
192
+ except KeyboardInterrupt:
193
+ raise
194
+ except Exception as e:
195
+ print(f"Warning: Could not open output file {output_file}: {e}")
196
+
197
+ start_time = time.time()
198
+
199
+ # Track statistics
200
+ expect_found = False
201
+ halt_on_error_found = False
202
+ halt_on_success_found = False
203
+ lines_processed = 0
204
+
205
+ while True:
206
+ # Check timeout
207
+ if timeout and (time.time() - start_time) > timeout:
208
+ elapsed_time = time.time() - start_time
209
+ print()
210
+ print(f"--- Monitor timeout after {timeout} seconds ---")
211
+
212
+ # Print statistics
213
+ if expect or halt_on_error or halt_on_success:
214
+ safe_print("\n--- Test Results ---")
215
+ if expect:
216
+ if expect_found:
217
+ safe_print(f"✓ Expected pattern found: '{expect}'")
218
+ else:
219
+ safe_print(f"✗ Expected pattern NOT found: '{expect}'")
220
+ if halt_on_error:
221
+ if halt_on_error_found:
222
+ safe_print(f"✗ Error pattern found: '{halt_on_error}'")
223
+ else:
224
+ safe_print(f"✓ Error pattern not found: '{halt_on_error}'")
225
+ if halt_on_success:
226
+ if halt_on_success_found:
227
+ safe_print(f"✓ Success pattern found: '{halt_on_success}'")
228
+ else:
229
+ safe_print(f"✗ Success pattern NOT found: '{halt_on_success}'")
230
+
231
+ ser.close()
232
+ if output_fp:
233
+ output_fp.close()
234
+
235
+ # Write summary
236
+ self._write_summary(
237
+ summary_file,
238
+ expect,
239
+ expect_found,
240
+ halt_on_error,
241
+ halt_on_error_found,
242
+ halt_on_success,
243
+ halt_on_success_found,
244
+ lines_processed,
245
+ elapsed_time,
246
+ "timeout",
247
+ )
248
+
249
+ # Check expect keyword for exit code
250
+ if expect:
251
+ return 0 if expect_found else 1
252
+ else:
253
+ # Legacy behavior when no expect is specified
254
+ if halt_on_error or halt_on_success:
255
+ return 1 # Error: pattern was expected but not found
256
+ else:
257
+ return 0 # Success: just a timed monitoring session
258
+
259
+ # Read line from serial
260
+ try:
261
+ if ser.in_waiting:
262
+ line = ser.readline()
263
+ try:
264
+ text = line.decode("utf-8", errors="replace").rstrip()
265
+ except KeyboardInterrupt as ke:
266
+ from fbuild.interrupt_utils import (
267
+ handle_keyboard_interrupt_properly,
268
+ )
269
+
270
+ handle_keyboard_interrupt_properly(ke)
271
+ except Exception:
272
+ text = str(line)
273
+
274
+ # Print the line
275
+ safe_print(text)
276
+ sys.stdout.flush()
277
+
278
+ # Write to output file if specified
279
+ if output_fp:
280
+ try:
281
+ output_fp.write(text + "\n")
282
+ output_fp.flush()
283
+ except KeyboardInterrupt:
284
+ raise
285
+ except Exception:
286
+ pass # Ignore write errors
287
+
288
+ # Increment line counter
289
+ lines_processed += 1
290
+
291
+ # Check for expect pattern (track but don't halt)
292
+ if expect and re.search(expect, text, re.IGNORECASE):
293
+ expect_found = True
294
+
295
+ # Check halt conditions
296
+ if halt_on_error and re.search(halt_on_error, text, re.IGNORECASE):
297
+ halt_on_error_found = True
298
+ elapsed_time = time.time() - start_time
299
+ print()
300
+ print(f"--- Found error pattern: '{halt_on_error}' ---")
301
+
302
+ # Print statistics
303
+ if expect or halt_on_success:
304
+ safe_print("\n--- Test Results ---")
305
+ if expect:
306
+ if expect_found:
307
+ safe_print(f"✓ Expected pattern found: '{expect}'")
308
+ else:
309
+ safe_print(f"✗ Expected pattern NOT found: '{expect}'")
310
+ if halt_on_success:
311
+ if halt_on_success_found:
312
+ safe_print(f"✓ Success pattern found: '{halt_on_success}'")
313
+ else:
314
+ safe_print(f"✗ Success pattern NOT found: '{halt_on_success}'")
315
+ safe_print(f"✗ Error pattern found: '{halt_on_error}'")
316
+
317
+ ser.close()
318
+ if output_fp:
319
+ output_fp.close()
320
+
321
+ # Write summary
322
+ self._write_summary(
323
+ summary_file,
324
+ expect,
325
+ expect_found,
326
+ halt_on_error,
327
+ halt_on_error_found,
328
+ halt_on_success,
329
+ halt_on_success_found,
330
+ lines_processed,
331
+ elapsed_time,
332
+ "halt_error",
333
+ )
334
+
335
+ return 1
336
+
337
+ if halt_on_success and re.search(halt_on_success, text, re.IGNORECASE):
338
+ halt_on_success_found = True
339
+ elapsed_time = time.time() - start_time
340
+ print()
341
+ print(f"--- Found success pattern: '{halt_on_success}' ---")
342
+
343
+ # Print statistics
344
+ if expect or halt_on_error:
345
+ safe_print("\n--- Test Results ---")
346
+ if expect:
347
+ if expect_found:
348
+ safe_print(f"✓ Expected pattern found: '{expect}'")
349
+ else:
350
+ safe_print(f"✗ Expected pattern NOT found: '{expect}'")
351
+ safe_print(f"✓ Success pattern found: '{halt_on_success}'")
352
+ if halt_on_error:
353
+ if halt_on_error_found:
354
+ safe_print(f"✗ Error pattern found: '{halt_on_error}'")
355
+ else:
356
+ safe_print(f"✓ Error pattern not found: '{halt_on_error}'")
357
+
358
+ ser.close()
359
+ if output_fp:
360
+ output_fp.close()
361
+
362
+ # Write summary
363
+ exit_reason = "expect_found" if (expect and expect_found) else "halt_success"
364
+ self._write_summary(
365
+ summary_file,
366
+ expect,
367
+ expect_found,
368
+ halt_on_error,
369
+ halt_on_error_found,
370
+ halt_on_success,
371
+ halt_on_success_found,
372
+ lines_processed,
373
+ elapsed_time,
374
+ exit_reason,
375
+ )
376
+
377
+ # Check expect keyword for exit code
378
+ if expect:
379
+ return 0 if expect_found else 1
380
+ else:
381
+ return 0
382
+ else:
383
+ time.sleep(0.01)
384
+
385
+ except serial.SerialException as e:
386
+ elapsed_time = time.time() - start_time
387
+ print(f"\nError reading from serial port: {e}")
388
+ ser.close()
389
+ if output_fp:
390
+ output_fp.close()
391
+
392
+ # Write summary
393
+ self._write_summary(
394
+ summary_file,
395
+ expect,
396
+ expect_found,
397
+ halt_on_error,
398
+ halt_on_error_found,
399
+ halt_on_success,
400
+ halt_on_success_found,
401
+ lines_processed,
402
+ elapsed_time,
403
+ "error",
404
+ )
405
+
406
+ return 1
407
+
408
+ except serial.SerialException as e:
409
+ print(f"Error opening serial port {port}: {e}")
410
+ if output_fp:
411
+ output_fp.close()
412
+
413
+ # Write summary (minimal - couldn't even start monitoring)
414
+ self._write_summary(
415
+ summary_file,
416
+ expect,
417
+ False,
418
+ halt_on_error,
419
+ False,
420
+ halt_on_success,
421
+ False,
422
+ 0,
423
+ 0.0,
424
+ "error",
425
+ )
426
+
427
+ return 1
428
+ except KeyboardInterrupt:
429
+ # Interrupt other threads
430
+ _thread.interrupt_main()
431
+
432
+ elapsed_time = time.time() - start_time if "start_time" in locals() else 0.0
433
+ lines = lines_processed if "lines_processed" in locals() else 0
434
+ exp_found = expect_found if "expect_found" in locals() else False
435
+ halt_err_found = halt_on_error_found if "halt_on_error_found" in locals() else False
436
+ halt_succ_found = halt_on_success_found if "halt_on_success_found" in locals() else False
437
+
438
+ print()
439
+ print("--- Monitor interrupted ---")
440
+ if ser is not None:
441
+ ser.close()
442
+ if output_fp:
443
+ output_fp.close()
444
+
445
+ # Write summary
446
+ self._write_summary(
447
+ summary_file,
448
+ expect,
449
+ exp_found,
450
+ halt_on_error,
451
+ halt_err_found,
452
+ halt_on_success,
453
+ halt_succ_found,
454
+ lines,
455
+ elapsed_time,
456
+ "interrupted",
457
+ )
458
+
459
+ return 0
460
+
461
+ def _detect_serial_port(self) -> Optional[str]:
462
+ """Auto-detect serial port for device.
463
+
464
+ Returns:
465
+ Serial port name or None if not found
466
+ """
467
+ try:
468
+ import serial.tools.list_ports
469
+
470
+ ports = list(serial.tools.list_ports.comports())
471
+
472
+ # Look for ESP32 or USB-SERIAL devices
473
+ for port in ports:
474
+ description = (port.description or "").lower()
475
+ manufacturer = (port.manufacturer or "").lower()
476
+
477
+ if any(x in description or x in manufacturer for x in ["cp210", "ch340", "usb-serial", "uart", "esp32"]):
478
+ return port.device
479
+
480
+ # If no specific match, return first port
481
+ if ports:
482
+ return ports[0].device
483
+
484
+ except ImportError:
485
+ if self.verbose:
486
+ print("pyserial not installed. Cannot auto-detect port.")
487
+ except KeyboardInterrupt as ke:
488
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
489
+
490
+ handle_keyboard_interrupt_properly(ke)
491
+ except Exception as e:
492
+ if self.verbose:
493
+ print(f"Port detection failed: {e}")
494
+
495
+ return None
@@ -0,0 +1,34 @@
1
+ """Utilities for handling KeyboardInterrupt in try-except blocks.
2
+
3
+ This module provides utilities to ensure KeyboardInterrupt is properly
4
+ propagated to the main thread when caught in exception handlers.
5
+ """
6
+
7
+ import _thread
8
+ from typing import NoReturn
9
+
10
+
11
+ def handle_keyboard_interrupt_properly(ke: KeyboardInterrupt) -> NoReturn:
12
+ """Handle KeyboardInterrupt by propagating it to the main thread.
13
+
14
+ This utility ensures that KeyboardInterrupt is properly handled in try-except
15
+ blocks by calling _thread.interrupt_main() before re-raising the exception.
16
+
17
+ Usage:
18
+ try:
19
+ # Some code that might be interrupted
20
+ pass
21
+ except KeyboardInterrupt as ke:
22
+ handle_keyboard_interrupt_properly(ke)
23
+ except Exception:
24
+ # Handle other exceptions
25
+ pass
26
+
27
+ Args:
28
+ ke: The KeyboardInterrupt exception to handle
29
+
30
+ Raises:
31
+ KeyboardInterrupt: Always re-raises the exception after handling
32
+ """
33
+ _thread.interrupt_main()
34
+ raise ke
@@ -0,0 +1,53 @@
1
+ """Package management for fbuild.
2
+
3
+ This module handles downloading, caching, and managing external packages
4
+ including toolchains, platforms, and libraries.
5
+ """
6
+
7
+ from .archive_utils import ArchiveExtractionError, ArchiveExtractor, URLVersionExtractor
8
+ from .arduino_core import ArduinoCore, ArduinoCoreError
9
+ from .cache import Cache
10
+ from .downloader import ChecksumError, DownloadError, ExtractionError, PackageDownloader
11
+ from .github_utils import GitHubURLOptimizer
12
+ from .library_compiler import LibraryCompilationError, LibraryCompiler
13
+ from .package import IFramework, IPackage
14
+ from .package import IToolchain as BaseToolchain
15
+ from .package import PackageError
16
+ from .platform_esp32 import PlatformErrorESP32, PlatformESP32
17
+ from .platform_utils import PlatformDetector, PlatformError
18
+ from .sdk_utils import SDKPathResolver
19
+ from .toolchain import ToolchainAVR as Toolchain
20
+ from .toolchain import ToolchainError
21
+ from .toolchain_binaries import BinaryNotFoundError, ToolchainBinaryFinder
22
+ from .toolchain_metadata import MetadataParseError, ToolchainMetadataParser
23
+
24
+ __all__ = [
25
+ "IPackage",
26
+ "BaseToolchain",
27
+ "IFramework",
28
+ "PackageError",
29
+ "Cache",
30
+ "PackageDownloader",
31
+ "DownloadError",
32
+ "ChecksumError",
33
+ "ExtractionError",
34
+ "Toolchain",
35
+ "ToolchainError",
36
+ "ArduinoCore",
37
+ "ArduinoCoreError",
38
+ "PlatformESP32",
39
+ "PlatformErrorESP32",
40
+ "GitHubURLOptimizer",
41
+ "LibraryCompiler",
42
+ "LibraryCompilationError",
43
+ "ArchiveExtractor",
44
+ "ArchiveExtractionError",
45
+ "URLVersionExtractor",
46
+ "SDKPathResolver",
47
+ "PlatformDetector",
48
+ "PlatformError",
49
+ "ToolchainBinaryFinder",
50
+ "BinaryNotFoundError",
51
+ "ToolchainMetadataParser",
52
+ "MetadataParseError",
53
+ ]