fbuild 1.2.8__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.
Files changed (121) hide show
  1. fbuild/__init__.py +390 -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_info_generator.py +624 -0
  8. fbuild/build/build_state.py +325 -0
  9. fbuild/build/build_utils.py +93 -0
  10. fbuild/build/compilation_executor.py +422 -0
  11. fbuild/build/compiler.py +165 -0
  12. fbuild/build/compiler_avr.py +574 -0
  13. fbuild/build/configurable_compiler.py +664 -0
  14. fbuild/build/configurable_linker.py +637 -0
  15. fbuild/build/flag_builder.py +214 -0
  16. fbuild/build/library_dependency_processor.py +185 -0
  17. fbuild/build/linker.py +708 -0
  18. fbuild/build/orchestrator.py +67 -0
  19. fbuild/build/orchestrator_avr.py +651 -0
  20. fbuild/build/orchestrator_esp32.py +878 -0
  21. fbuild/build/orchestrator_rp2040.py +719 -0
  22. fbuild/build/orchestrator_stm32.py +696 -0
  23. fbuild/build/orchestrator_teensy.py +580 -0
  24. fbuild/build/source_compilation_orchestrator.py +218 -0
  25. fbuild/build/source_scanner.py +516 -0
  26. fbuild/cli.py +717 -0
  27. fbuild/cli_utils.py +314 -0
  28. fbuild/config/__init__.py +16 -0
  29. fbuild/config/board_config.py +542 -0
  30. fbuild/config/board_loader.py +92 -0
  31. fbuild/config/ini_parser.py +369 -0
  32. fbuild/config/mcu_specs.py +88 -0
  33. fbuild/daemon/__init__.py +42 -0
  34. fbuild/daemon/async_client.py +531 -0
  35. fbuild/daemon/client.py +1505 -0
  36. fbuild/daemon/compilation_queue.py +293 -0
  37. fbuild/daemon/configuration_lock.py +865 -0
  38. fbuild/daemon/daemon.py +585 -0
  39. fbuild/daemon/daemon_context.py +293 -0
  40. fbuild/daemon/error_collector.py +263 -0
  41. fbuild/daemon/file_cache.py +332 -0
  42. fbuild/daemon/firmware_ledger.py +546 -0
  43. fbuild/daemon/lock_manager.py +508 -0
  44. fbuild/daemon/logging_utils.py +149 -0
  45. fbuild/daemon/messages.py +957 -0
  46. fbuild/daemon/operation_registry.py +288 -0
  47. fbuild/daemon/port_state_manager.py +249 -0
  48. fbuild/daemon/process_tracker.py +366 -0
  49. fbuild/daemon/processors/__init__.py +18 -0
  50. fbuild/daemon/processors/build_processor.py +248 -0
  51. fbuild/daemon/processors/deploy_processor.py +664 -0
  52. fbuild/daemon/processors/install_deps_processor.py +431 -0
  53. fbuild/daemon/processors/locking_processor.py +777 -0
  54. fbuild/daemon/processors/monitor_processor.py +285 -0
  55. fbuild/daemon/request_processor.py +457 -0
  56. fbuild/daemon/shared_serial.py +819 -0
  57. fbuild/daemon/status_manager.py +238 -0
  58. fbuild/daemon/subprocess_manager.py +316 -0
  59. fbuild/deploy/__init__.py +21 -0
  60. fbuild/deploy/deployer.py +67 -0
  61. fbuild/deploy/deployer_esp32.py +310 -0
  62. fbuild/deploy/docker_utils.py +315 -0
  63. fbuild/deploy/monitor.py +519 -0
  64. fbuild/deploy/qemu_runner.py +603 -0
  65. fbuild/interrupt_utils.py +34 -0
  66. fbuild/ledger/__init__.py +52 -0
  67. fbuild/ledger/board_ledger.py +560 -0
  68. fbuild/output.py +352 -0
  69. fbuild/packages/__init__.py +66 -0
  70. fbuild/packages/archive_utils.py +1098 -0
  71. fbuild/packages/arduino_core.py +412 -0
  72. fbuild/packages/cache.py +256 -0
  73. fbuild/packages/concurrent_manager.py +510 -0
  74. fbuild/packages/downloader.py +518 -0
  75. fbuild/packages/fingerprint.py +423 -0
  76. fbuild/packages/framework_esp32.py +538 -0
  77. fbuild/packages/framework_rp2040.py +349 -0
  78. fbuild/packages/framework_stm32.py +459 -0
  79. fbuild/packages/framework_teensy.py +346 -0
  80. fbuild/packages/github_utils.py +96 -0
  81. fbuild/packages/header_trampoline_cache.py +394 -0
  82. fbuild/packages/library_compiler.py +203 -0
  83. fbuild/packages/library_manager.py +549 -0
  84. fbuild/packages/library_manager_esp32.py +725 -0
  85. fbuild/packages/package.py +163 -0
  86. fbuild/packages/platform_esp32.py +383 -0
  87. fbuild/packages/platform_rp2040.py +400 -0
  88. fbuild/packages/platform_stm32.py +581 -0
  89. fbuild/packages/platform_teensy.py +312 -0
  90. fbuild/packages/platform_utils.py +131 -0
  91. fbuild/packages/platformio_registry.py +369 -0
  92. fbuild/packages/sdk_utils.py +231 -0
  93. fbuild/packages/toolchain.py +436 -0
  94. fbuild/packages/toolchain_binaries.py +196 -0
  95. fbuild/packages/toolchain_esp32.py +489 -0
  96. fbuild/packages/toolchain_metadata.py +185 -0
  97. fbuild/packages/toolchain_rp2040.py +436 -0
  98. fbuild/packages/toolchain_stm32.py +417 -0
  99. fbuild/packages/toolchain_teensy.py +404 -0
  100. fbuild/platform_configs/esp32.json +150 -0
  101. fbuild/platform_configs/esp32c2.json +144 -0
  102. fbuild/platform_configs/esp32c3.json +143 -0
  103. fbuild/platform_configs/esp32c5.json +151 -0
  104. fbuild/platform_configs/esp32c6.json +151 -0
  105. fbuild/platform_configs/esp32p4.json +149 -0
  106. fbuild/platform_configs/esp32s3.json +151 -0
  107. fbuild/platform_configs/imxrt1062.json +56 -0
  108. fbuild/platform_configs/rp2040.json +70 -0
  109. fbuild/platform_configs/rp2350.json +76 -0
  110. fbuild/platform_configs/stm32f1.json +59 -0
  111. fbuild/platform_configs/stm32f4.json +63 -0
  112. fbuild/py.typed +0 -0
  113. fbuild-1.2.8.dist-info/METADATA +468 -0
  114. fbuild-1.2.8.dist-info/RECORD +121 -0
  115. fbuild-1.2.8.dist-info/WHEEL +5 -0
  116. fbuild-1.2.8.dist-info/entry_points.txt +5 -0
  117. fbuild-1.2.8.dist-info/licenses/LICENSE +21 -0
  118. fbuild-1.2.8.dist-info/top_level.txt +2 -0
  119. fbuild_lint/__init__.py +0 -0
  120. fbuild_lint/ruff_plugins/__init__.py +0 -0
  121. fbuild_lint/ruff_plugins/keyboard_interrupt_checker.py +158 -0
@@ -0,0 +1,542 @@
1
+ """
2
+ Board configuration loader for Arduino boards.
3
+
4
+ This module provides functionality to load and parse board-specific configurations
5
+ from boards.txt files, extracting MCU type, frequency, and other build parameters.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Dict, Optional
10
+
11
+
12
+ class BoardConfigError(Exception):
13
+ """Exception raised for board configuration errors."""
14
+
15
+ pass
16
+
17
+
18
+ class BoardConfig:
19
+ """
20
+ Loader and parser for Arduino boards.txt configuration.
21
+
22
+ boards.txt files contain board-specific definitions like MCU type,
23
+ frequency, fuse settings, and other build parameters.
24
+
25
+ Example boards.txt entry:
26
+ uno.name=Arduino Uno
27
+ uno.build.mcu=atmega328p
28
+ uno.build.f_cpu=16000000L
29
+ uno.build.board=AVR_UNO
30
+ uno.build.core=arduino
31
+ uno.build.variant=standard
32
+
33
+ Usage:
34
+ # Load from boards.txt file
35
+ config = BoardConfig.from_boards_txt(Path("boards.txt"), "uno")
36
+
37
+ # Or create directly with known values
38
+ config = BoardConfig(
39
+ name="uno",
40
+ mcu="atmega328p",
41
+ f_cpu="16000000L",
42
+ board="AVR_UNO",
43
+ core="arduino",
44
+ variant="standard"
45
+ )
46
+ """
47
+
48
+ # Default Arduino Uno configuration (used as fallback)
49
+ UNO_DEFAULTS = {
50
+ "name": "Arduino Uno",
51
+ "mcu": "atmega328p",
52
+ "f_cpu": "16000000L",
53
+ "board": "AVR_UNO",
54
+ "core": "arduino",
55
+ "variant": "standard",
56
+ "vid": "0x2341",
57
+ "pid": "0x0043",
58
+ }
59
+
60
+ # Other common boards with defaults
61
+ BOARD_DEFAULTS = {
62
+ "uno": UNO_DEFAULTS,
63
+ "mega": {
64
+ "name": "Arduino Mega",
65
+ "mcu": "atmega2560",
66
+ "f_cpu": "16000000L",
67
+ "board": "AVR_MEGA2560",
68
+ "core": "arduino",
69
+ "variant": "mega",
70
+ },
71
+ "nano": {
72
+ "name": "Arduino Nano",
73
+ "mcu": "atmega328p",
74
+ "f_cpu": "16000000L",
75
+ "board": "AVR_NANO",
76
+ "core": "arduino",
77
+ "variant": "eightanaloginputs",
78
+ },
79
+ "leonardo": {
80
+ "name": "Arduino Leonardo",
81
+ "mcu": "atmega32u4",
82
+ "f_cpu": "16000000L",
83
+ "board": "AVR_LEONARDO",
84
+ "core": "arduino",
85
+ "variant": "leonardo",
86
+ },
87
+ "esp32-c3-devkitm-1": {
88
+ "name": "ESP32-C3 DevKit M-1",
89
+ "mcu": "esp32c3",
90
+ "f_cpu": "160000000L",
91
+ "board": "ESP32_DEV",
92
+ "core": "esp32",
93
+ "variant": "esp32c3",
94
+ },
95
+ "esp32-c6-devkitc-1": {
96
+ "name": "ESP32-C6 DevKit C-1",
97
+ "mcu": "esp32c6",
98
+ "f_cpu": "160000000L",
99
+ "board": "ESP32_DEV",
100
+ "core": "esp32",
101
+ "variant": "esp32c6",
102
+ },
103
+ "esp32-c6-devkitm-1": {
104
+ "name": "ESP32-C6 DevKit M-1",
105
+ "mcu": "esp32c6",
106
+ "f_cpu": "160000000L",
107
+ "board": "ESP32_DEV",
108
+ "core": "esp32",
109
+ "variant": "esp32c6",
110
+ },
111
+ "esp32-c5-devkitc-1": {
112
+ "name": "ESP32-C5 DevKit C-1",
113
+ "mcu": "esp32c5",
114
+ "f_cpu": "160000000L",
115
+ "board": "ESP32_DEV",
116
+ "core": "esp32",
117
+ "variant": "esp32c5",
118
+ },
119
+ "teensy40": {
120
+ "name": "Teensy 4.0",
121
+ "mcu": "imxrt1062",
122
+ "f_cpu": "600000000L",
123
+ "board": "TEENSY40",
124
+ "core": "teensy4",
125
+ "variant": "teensy40",
126
+ },
127
+ "teensy41": {
128
+ "name": "Teensy 4.1",
129
+ "mcu": "imxrt1062",
130
+ "f_cpu": "600000000L",
131
+ "board": "TEENSY41",
132
+ "core": "teensy4",
133
+ "variant": "teensy41",
134
+ },
135
+ "rpipico": {
136
+ "name": "Raspberry Pi Pico",
137
+ "mcu": "rp2040",
138
+ "f_cpu": "133000000L",
139
+ "board": "RASPBERRY_PI_PICO",
140
+ "core": "rp2040",
141
+ "variant": "rpipico",
142
+ },
143
+ "rpipicow": {
144
+ "name": "Raspberry Pi Pico W",
145
+ "mcu": "rp2040",
146
+ "f_cpu": "133000000L",
147
+ "board": "RASPBERRY_PI_PICO_W",
148
+ "core": "rp2040",
149
+ "variant": "rpipicow",
150
+ },
151
+ "rpipico2": {
152
+ "name": "Raspberry Pi Pico 2",
153
+ "mcu": "rp2350",
154
+ "f_cpu": "150000000L",
155
+ "board": "RASPBERRY_PI_PICO_2",
156
+ "core": "rp2040",
157
+ "variant": "rpipico2",
158
+ },
159
+ "rpipico2w": {
160
+ "name": "Raspberry Pi Pico 2 W",
161
+ "mcu": "rp2350",
162
+ "f_cpu": "150000000L",
163
+ "board": "RASPBERRY_PI_PICO_2_W",
164
+ "core": "rp2040",
165
+ "variant": "rpipico2w",
166
+ },
167
+ # STM32 boards
168
+ "nucleo_f446re": {
169
+ "name": "ST Nucleo F446RE",
170
+ "mcu": "stm32f446ret6",
171
+ "f_cpu": "180000000L",
172
+ "board": "NUCLEO_F446RE",
173
+ "core": "arduino",
174
+ "variant": "STM32F4xx/F446R(C-E)T_NUCLEO",
175
+ },
176
+ "nucleo_f411re": {
177
+ "name": "ST Nucleo F411RE",
178
+ "mcu": "stm32f411ret6",
179
+ "f_cpu": "100000000L",
180
+ "board": "NUCLEO_F411RE",
181
+ "core": "arduino",
182
+ "variant": "STM32F4xx/F411R(C-E)T",
183
+ },
184
+ "nucleo_f103rb": {
185
+ "name": "ST Nucleo F103RB",
186
+ "mcu": "stm32f103rbt6",
187
+ "f_cpu": "72000000L",
188
+ "board": "NUCLEO_F103RB",
189
+ "core": "arduino",
190
+ "variant": "STM32F1xx/F103R(8-B)T",
191
+ },
192
+ "nucleo_l476rg": {
193
+ "name": "ST Nucleo L476RG",
194
+ "mcu": "stm32l476rgt6",
195
+ "f_cpu": "80000000L",
196
+ "board": "NUCLEO_L476RG",
197
+ "core": "arduino",
198
+ "variant": "STM32L4xx/L476R(C-E-G)T",
199
+ },
200
+ "bluepill_f103c8": {
201
+ "name": "Blue Pill F103C8",
202
+ "mcu": "stm32f103c8t6",
203
+ "f_cpu": "72000000L",
204
+ "board": "BLUEPILL_F103C8",
205
+ "core": "arduino",
206
+ "variant": "STM32F1xx/F103C8T_BLUEPILL",
207
+ },
208
+ "blackpill_f411ce": {
209
+ "name": "Black Pill F411CE",
210
+ "mcu": "stm32f411ceu6",
211
+ "f_cpu": "100000000L",
212
+ "board": "BLACKPILL_F411CE",
213
+ "core": "arduino",
214
+ "variant": "STM32F4xx/F411C(C-E)(U-Y)",
215
+ },
216
+ }
217
+
218
+ def __init__(
219
+ self,
220
+ name: str,
221
+ mcu: str,
222
+ f_cpu: str,
223
+ board: str,
224
+ core: str = "arduino",
225
+ variant: str = "standard",
226
+ vid: Optional[str] = None,
227
+ pid: Optional[str] = None,
228
+ extra_flags: Optional[Dict[str, str]] = None,
229
+ ):
230
+ """
231
+ Initialize board configuration.
232
+
233
+ Args:
234
+ name: Human-readable board name (e.g., "Arduino Uno")
235
+ mcu: Microcontroller type (e.g., "atmega328p")
236
+ f_cpu: CPU frequency (e.g., "16000000L")
237
+ board: Board identifier for defines (e.g., "AVR_UNO")
238
+ core: Core library name (default: "arduino")
239
+ variant: Variant directory name (default: "standard")
240
+ vid: USB Vendor ID (optional)
241
+ pid: USB Product ID (optional)
242
+ extra_flags: Additional board-specific flags (optional)
243
+ """
244
+ self.name = name
245
+ self.mcu = mcu
246
+ self.f_cpu = f_cpu
247
+ self.board = board
248
+ self.core = core
249
+ self.variant = variant
250
+ self.vid = vid
251
+ self.pid = pid
252
+ self.extra_flags = extra_flags or {}
253
+
254
+ @property
255
+ def platform(self) -> str:
256
+ """
257
+ Detect platform type from MCU.
258
+
259
+ Returns:
260
+ Platform identifier: "avr", "esp32", "teensy", "raspberrypi", or "ststm32"
261
+ """
262
+ if self.mcu.startswith("atmega"):
263
+ return "avr"
264
+ elif self.mcu.startswith("esp32"):
265
+ return "esp32"
266
+ elif self.mcu.startswith("imxrt"):
267
+ return "teensy"
268
+ elif self.mcu.startswith("rp20"):
269
+ return "raspberrypi"
270
+ elif self.mcu.startswith("stm32"):
271
+ return "ststm32"
272
+ else:
273
+ # Default to AVR for unknown
274
+ return "avr"
275
+
276
+ @classmethod
277
+ def from_boards_txt(
278
+ cls,
279
+ boards_txt_path: Path,
280
+ board_id: str,
281
+ overrides: Optional[Dict[str, str]] = None,
282
+ ) -> "BoardConfig":
283
+ """
284
+ Load board configuration from a boards.txt file.
285
+
286
+ Args:
287
+ boards_txt_path: Path to boards.txt file
288
+ board_id: Board identifier (e.g., "uno", "mega")
289
+ overrides: Optional overrides from platformio.ini
290
+ (e.g., board_build.mcu, board_build.f_cpu)
291
+
292
+ Returns:
293
+ BoardConfig instance
294
+
295
+ Raises:
296
+ BoardConfigError: If file not found or board not defined
297
+
298
+ Example:
299
+ config = BoardConfig.from_boards_txt(
300
+ Path("hardware/arduino/avr/boards.txt"),
301
+ "uno"
302
+ )
303
+ """
304
+ if not boards_txt_path.exists():
305
+ raise BoardConfigError(f"boards.txt not found: {boards_txt_path}")
306
+
307
+ # Parse boards.txt
308
+ board_data = cls._parse_boards_txt(boards_txt_path, board_id)
309
+
310
+ if not board_data:
311
+ raise BoardConfigError(f"Board '{board_id}' not found in {boards_txt_path}")
312
+
313
+ # Apply overrides from platformio.ini (board_build.*)
314
+ if overrides:
315
+ board_data.update(overrides)
316
+
317
+ # Extract required fields with validation
318
+ try:
319
+ return cls(
320
+ name=board_data.get("name", board_id),
321
+ mcu=board_data["mcu"],
322
+ f_cpu=board_data["f_cpu"],
323
+ board=board_data["board"],
324
+ core=board_data.get("core", "arduino"),
325
+ variant=board_data.get("variant", "standard"),
326
+ vid=board_data.get("vid"),
327
+ pid=board_data.get("pid"),
328
+ extra_flags={
329
+ k: v
330
+ for k, v in board_data.items()
331
+ if k
332
+ not in {
333
+ "name",
334
+ "mcu",
335
+ "f_cpu",
336
+ "board",
337
+ "core",
338
+ "variant",
339
+ "vid",
340
+ "pid",
341
+ }
342
+ },
343
+ )
344
+ except KeyError as e:
345
+ raise BoardConfigError(f"Board '{board_id}' is missing required field: {e}") from e
346
+
347
+ @classmethod
348
+ def from_board_id(cls, board_id: str, overrides: Optional[Dict[str, str]] = None) -> "BoardConfig":
349
+ """
350
+ Load board configuration using built-in defaults.
351
+
352
+ This is a fallback when boards.txt is not available. It uses
353
+ hardcoded configurations for common Arduino boards.
354
+
355
+ Args:
356
+ board_id: Board identifier (e.g., "uno", "mega")
357
+ overrides: Optional overrides (e.g., from platformio.ini)
358
+
359
+ Returns:
360
+ BoardConfig instance
361
+
362
+ Raises:
363
+ BoardConfigError: If board_id not recognized
364
+
365
+ Example:
366
+ config = BoardConfig.from_board_id("uno")
367
+ """
368
+ # Normalize board_id (handle common variations)
369
+ board_id = board_id.lower()
370
+ if board_id == "megaatmega2560":
371
+ board_id = "mega"
372
+
373
+ if board_id not in cls.BOARD_DEFAULTS:
374
+ raise BoardConfigError(f"Unknown board: {board_id}. " + f"Supported boards: {', '.join(cls.BOARD_DEFAULTS.keys())}")
375
+
376
+ board_data = cls.BOARD_DEFAULTS[board_id].copy()
377
+
378
+ # Apply overrides
379
+ if overrides:
380
+ board_data.update(overrides)
381
+
382
+ return cls(
383
+ name=board_data["name"],
384
+ mcu=board_data["mcu"],
385
+ f_cpu=board_data["f_cpu"],
386
+ board=board_data["board"],
387
+ core=board_data.get("core", "arduino"),
388
+ variant=board_data.get("variant", "standard"),
389
+ vid=board_data.get("vid"),
390
+ pid=board_data.get("pid"),
391
+ )
392
+
393
+ @staticmethod
394
+ def _parse_boards_txt(boards_txt_path: Path, board_id: str) -> Dict[str, str]:
395
+ """
396
+ Parse boards.txt file and extract configuration for a specific board.
397
+
398
+ Args:
399
+ boards_txt_path: Path to boards.txt
400
+ board_id: Board identifier to extract
401
+
402
+ Returns:
403
+ Dictionary of board configuration keys and values
404
+
405
+ Example boards.txt format:
406
+ uno.name=Arduino Uno
407
+ uno.build.mcu=atmega328p
408
+ uno.build.f_cpu=16000000L
409
+ """
410
+ board_data = {}
411
+ prefix = f"{board_id}."
412
+
413
+ try:
414
+ with open(boards_txt_path, "r", encoding="utf-8") as f:
415
+ for line in f:
416
+ line = line.strip()
417
+
418
+ # Skip comments and empty lines
419
+ if not line or line.startswith("#"):
420
+ continue
421
+
422
+ # Parse key=value pairs
423
+ if "=" not in line:
424
+ continue
425
+
426
+ key, value = line.split("=", 1)
427
+ key = key.strip()
428
+ value = value.strip()
429
+
430
+ # Only process lines for our board
431
+ if not key.startswith(prefix):
432
+ continue
433
+
434
+ # Remove board prefix and extract the field name
435
+ # uno.build.mcu -> mcu (from build.*)
436
+ # uno.name -> name
437
+ field = key[len(prefix) :]
438
+
439
+ # Handle build.* fields
440
+ if field.startswith("build."):
441
+ field = field[6:] # Remove 'build.' prefix
442
+
443
+ # Handle upload.* fields (vid, pid)
444
+ elif field.startswith("upload."):
445
+ field = field[7:] # Remove 'upload.' prefix
446
+
447
+ board_data[field] = value
448
+
449
+ except KeyboardInterrupt as ke:
450
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
451
+
452
+ handle_keyboard_interrupt_properly(ke)
453
+ except Exception as e:
454
+ raise BoardConfigError(f"Failed to parse {boards_txt_path}: {e}") from e
455
+
456
+ return board_data
457
+
458
+ def get_defines(self) -> Dict[str, str]:
459
+ """
460
+ Get preprocessor defines for this board configuration.
461
+
462
+ Returns:
463
+ Dictionary of define names to values
464
+
465
+ Example:
466
+ {
467
+ 'PLATFORMIO': '60118',
468
+ 'F_CPU': '16000000L',
469
+ 'ARDUINO': '10819',
470
+ 'ARDUINO_AVR_UNO': '',
471
+ 'ARDUINO_ARCH_AVR': '',
472
+ '__AVR_ATmega328P__': ''
473
+ }
474
+ """
475
+ defines = {
476
+ "PLATFORMIO": "60118", # PlatformIO version identifier
477
+ "F_CPU": self.f_cpu,
478
+ "ARDUINO": "10819", # Arduino version (1.8.19)
479
+ f"ARDUINO_{self.board}": "",
480
+ "ARDUINO_ARCH_AVR": "",
481
+ }
482
+
483
+ # Add MCU-specific define (e.g., __AVR_ATmega328P__)
484
+ mcu_define = f"__AVR_{self.mcu.upper()}__"
485
+ defines[mcu_define] = ""
486
+
487
+ # Add any extra defines from board configuration
488
+ for key, value in self.extra_flags.items():
489
+ if key.startswith("define_"):
490
+ define_name = key[7:].upper()
491
+ defines[define_name] = value
492
+
493
+ return defines
494
+
495
+ def get_include_paths(self, core_path: Path) -> list[Path]:
496
+ """
497
+ Get include paths for compilation.
498
+
499
+ Args:
500
+ core_path: Path to Arduino core installation
501
+
502
+ Returns:
503
+ List of include directory paths
504
+
505
+ Example:
506
+ [
507
+ Path('/path/to/cores/arduino'),
508
+ Path('/path/to/variants/standard')
509
+ ]
510
+ """
511
+ return [
512
+ core_path / "cores" / self.core,
513
+ core_path / "variants" / self.variant,
514
+ ]
515
+
516
+ def get_core_sources_dir(self, core_path: Path) -> Path:
517
+ """
518
+ Get path to core source files directory.
519
+
520
+ Args:
521
+ core_path: Path to Arduino core installation
522
+
523
+ Returns:
524
+ Path to core sources (e.g., cores/arduino)
525
+ """
526
+ return core_path / "cores" / self.core
527
+
528
+ def get_variant_dir(self, core_path: Path) -> Path:
529
+ """
530
+ Get path to board variant directory.
531
+
532
+ Args:
533
+ core_path: Path to Arduino core installation
534
+
535
+ Returns:
536
+ Path to variant directory (e.g., variants/standard)
537
+ """
538
+ return core_path / "variants" / self.variant
539
+
540
+ def __repr__(self) -> str:
541
+ """String representation of board configuration."""
542
+ return f"BoardConfig(name='{self.name}', mcu='{self.mcu}', f_cpu='{self.f_cpu}', board='{self.board}')"
@@ -0,0 +1,92 @@
1
+ """Board configuration loading utilities.
2
+
3
+ This module provides utilities for loading board configurations with support
4
+ for both built-in boards and platform-specific boards (ESP32, etc.).
5
+ """
6
+
7
+ from typing import Dict
8
+
9
+ from .board_config import BoardConfig, BoardConfigError
10
+
11
+
12
+ class BoardConfigLoader:
13
+ """Utility class for loading board configurations with platform detection."""
14
+
15
+ @staticmethod
16
+ def load_board_config(board_id: str, env_config: Dict) -> BoardConfig:
17
+ """
18
+ Load board configuration with platform-specific handling.
19
+
20
+ This method first attempts to load the board from built-in defaults.
21
+ If that fails and the platform is ESP32, it creates a minimal BoardConfig
22
+ with MCU detection based on the board ID.
23
+
24
+ Args:
25
+ board_id: Board identifier (e.g., 'uno', 'esp32dev')
26
+ env_config: Environment configuration from platformio.ini
27
+
28
+ Returns:
29
+ BoardConfig instance
30
+
31
+ Raises:
32
+ BoardConfigError: If board configuration cannot be loaded
33
+ """
34
+ # Extract overrides from env_config (board_build.*)
35
+ overrides = {}
36
+ for key, value in env_config.items():
37
+ if key.startswith("board_build."):
38
+ override_key = key[12:] # Remove 'board_build.' prefix
39
+ overrides[override_key] = value
40
+
41
+ try:
42
+ # Try to load from built-in defaults first
43
+ return BoardConfig.from_board_id(board_id, overrides)
44
+ except BoardConfigError:
45
+ # Check if this is an ESP32 board by looking at platform URL
46
+ platform_url = env_config.get("platform", "")
47
+ if "espressif32" in platform_url or "esp32" in platform_url.lower():
48
+ # For ESP32 boards, create a minimal BoardConfig
49
+ # The actual board JSON will be loaded by ESP32Platform
50
+ mcu = BoardConfigLoader._detect_esp32_mcu(board_id)
51
+
52
+ return BoardConfig(
53
+ name=board_id,
54
+ mcu=mcu,
55
+ f_cpu="240000000L", # Default ESP32 frequency
56
+ board="ESP32_DEV",
57
+ core="esp32",
58
+ variant="esp32",
59
+ )
60
+
61
+ # If not ESP32, re-raise original error
62
+ raise BoardConfigError(f"Unknown board: {board_id}\n" + "Supported AVR boards: uno, mega, nano, leonardo\n" + "For ESP32 boards, ensure platform URL contains 'espressif32'")
63
+
64
+ @staticmethod
65
+ def _detect_esp32_mcu(board_id: str) -> str:
66
+ """
67
+ Detect ESP32 MCU variant from board ID.
68
+
69
+ Args:
70
+ board_id: Board identifier
71
+
72
+ Returns:
73
+ MCU identifier (esp32, esp32s3, esp32c3, etc.)
74
+ """
75
+ board_id_lower = board_id.lower()
76
+
77
+ # Check for specific ESP32 variants
78
+ if "esp32s3" in board_id_lower or "esp32-s3" in board_id_lower:
79
+ return "esp32s3"
80
+ elif "esp32s2" in board_id_lower or "esp32-s2" in board_id_lower:
81
+ return "esp32s2"
82
+ elif "esp32c3" in board_id_lower or "esp32-c3" in board_id_lower:
83
+ return "esp32c3"
84
+ elif "esp32c6" in board_id_lower or "esp32-c6" in board_id_lower:
85
+ return "esp32c6"
86
+ elif "esp32c2" in board_id_lower or "esp32-c2" in board_id_lower:
87
+ return "esp32c2"
88
+ elif "esp32h2" in board_id_lower or "esp32-h2" in board_id_lower:
89
+ return "esp32h2"
90
+ else:
91
+ # Default to esp32
92
+ return "esp32"