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,459 @@
1
+ """STM32 Framework Management.
2
+
3
+ This module handles downloading, extracting, and managing the STM32duino Arduino framework
4
+ needed for STM32 Arduino builds.
5
+
6
+ Framework Download Process:
7
+ 1. Download Arduino_Core_STM32 from GitHub (stm32duino/Arduino_Core_STM32)
8
+ 2. Extract to cache directory
9
+ 3. Provide access to cores, variants, and libraries
10
+
11
+ Framework Structure (after extraction):
12
+ cores/
13
+ └── arduino/ # Arduino core for STM32
14
+ ├── Arduino.h
15
+ ├── main.cpp
16
+ ├── wiring.c
17
+ └── ...
18
+ variants/
19
+ ├── STM32F1xx/ # STM32F1 variants
20
+ ├── STM32F4xx/ # STM32F4 variants
21
+ └── ...
22
+
23
+ Supported MCU Families:
24
+ - STM32F0, STM32F1, STM32F2, STM32F3, STM32F4, STM32F7
25
+ - STM32G0, STM32G4
26
+ - STM32H7
27
+ - STM32L0, STM32L1, STM32L4, STM32L5
28
+ - STM32U5
29
+ - STM32WB, STM32WL
30
+ """
31
+
32
+ from pathlib import Path
33
+ from typing import Any, Dict, List, Optional
34
+
35
+ from .cache import Cache
36
+ from .downloader import DownloadError, ExtractionError, PackageDownloader
37
+ from .package import IFramework, PackageError
38
+
39
+
40
+ class FrameworkErrorSTM32(PackageError):
41
+ """Raised when STM32 framework operations fail."""
42
+
43
+ pass
44
+
45
+
46
+ class FrameworkSTM32(IFramework):
47
+ """Manages STM32 framework download, extraction, and access.
48
+
49
+ This class handles the stm32duino Arduino_Core_STM32 framework which includes:
50
+ - Arduino core for STM32 (cores/arduino/)
51
+ - Board variants (variants/)
52
+ - Built-in libraries
53
+ - CMSIS and HAL drivers
54
+ """
55
+
56
+ # STM32duino repository URL
57
+ FRAMEWORK_REPO_URL = "https://github.com/stm32duino/Arduino_Core_STM32"
58
+ # Using tagged release for stability
59
+ FRAMEWORK_VERSION = "2.12.0"
60
+ FRAMEWORK_ARCHIVE_URL = f"https://github.com/stm32duino/Arduino_Core_STM32/archive/refs/tags/{FRAMEWORK_VERSION}.zip"
61
+
62
+ def __init__(
63
+ self,
64
+ cache: Cache,
65
+ show_progress: bool = True,
66
+ ):
67
+ """Initialize STM32 framework manager.
68
+
69
+ Args:
70
+ cache: Cache manager instance
71
+ show_progress: Whether to show download/extraction progress
72
+ """
73
+ self.cache = cache
74
+ self.show_progress = show_progress
75
+ self.downloader = PackageDownloader()
76
+
77
+ # Use tagged version
78
+ self.version = self.FRAMEWORK_VERSION
79
+ self.framework_url = self.FRAMEWORK_ARCHIVE_URL
80
+
81
+ # Get framework path from cache
82
+ self.framework_path = cache.get_platform_path(self.framework_url, self.version)
83
+
84
+ def ensure_framework(self) -> Path:
85
+ """Ensure framework is downloaded and extracted.
86
+
87
+ Returns:
88
+ Path to the extracted framework directory
89
+
90
+ Raises:
91
+ FrameworkErrorSTM32: If download or extraction fails
92
+ """
93
+ if self.is_installed():
94
+ if self.show_progress:
95
+ print(f"Using cached Arduino_Core_STM32 {self.version}")
96
+ return self.framework_path
97
+
98
+ try:
99
+ if self.show_progress:
100
+ print(f"Downloading Arduino_Core_STM32 {self.version}...")
101
+
102
+ # Download and extract framework package
103
+ self.cache.ensure_directories()
104
+
105
+ # Use downloader to handle download and extraction
106
+ archive_name = f"Arduino_Core_STM32-{self.version}.zip"
107
+ archive_path = self.framework_path.parent / archive_name
108
+
109
+ # Download if not cached
110
+ if not archive_path.exists():
111
+ archive_path.parent.mkdir(parents=True, exist_ok=True)
112
+ self.downloader.download(self.framework_url, archive_path, show_progress=self.show_progress)
113
+ else:
114
+ if self.show_progress:
115
+ print("Using cached Arduino_Core_STM32 archive")
116
+
117
+ # Extract to framework directory
118
+ if self.show_progress:
119
+ print("Extracting Arduino_Core_STM32...")
120
+
121
+ # Create temp extraction directory
122
+ temp_extract = self.framework_path.parent / "temp_extract"
123
+ temp_extract.mkdir(parents=True, exist_ok=True)
124
+
125
+ self.downloader.extract_archive(archive_path, temp_extract, show_progress=self.show_progress)
126
+
127
+ # Find the Arduino_Core_STM32 directory in the extracted content
128
+ # Usually it's a subdirectory like "Arduino_Core_STM32-2.12.0/"
129
+ extracted_dirs = list(temp_extract.glob("Arduino_Core_STM32-*"))
130
+ if not extracted_dirs:
131
+ # Maybe it extracted directly
132
+ extracted_dirs = [temp_extract]
133
+
134
+ source_dir = extracted_dirs[0]
135
+
136
+ # Move to final location
137
+ if self.framework_path.exists():
138
+ import shutil
139
+
140
+ shutil.rmtree(self.framework_path)
141
+
142
+ source_dir.rename(self.framework_path)
143
+
144
+ # Clean up temp directory
145
+ if temp_extract.exists() and temp_extract != self.framework_path:
146
+ import shutil
147
+
148
+ shutil.rmtree(temp_extract, ignore_errors=True)
149
+
150
+ if self.show_progress:
151
+ print(f"Arduino_Core_STM32 installed to {self.framework_path}")
152
+
153
+ return self.framework_path
154
+
155
+ except (DownloadError, ExtractionError) as e:
156
+ raise FrameworkErrorSTM32(f"Failed to install Arduino_Core_STM32: {e}")
157
+ except KeyboardInterrupt as ke:
158
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
159
+
160
+ handle_keyboard_interrupt_properly(ke)
161
+ raise # Never reached, but satisfies type checker
162
+ except Exception as e:
163
+ raise FrameworkErrorSTM32(f"Unexpected error installing framework: {e}")
164
+
165
+ def is_installed(self) -> bool:
166
+ """Check if framework is already installed.
167
+
168
+ Returns:
169
+ True if framework directory exists with key files
170
+ """
171
+ if not self.framework_path.exists():
172
+ return False
173
+
174
+ # Verify arduino core directory exists
175
+ arduino_path = self.framework_path / "cores" / "arduino"
176
+ if not arduino_path.exists():
177
+ return False
178
+
179
+ # Verify essential files exist
180
+ required_files = [
181
+ arduino_path / "Arduino.h",
182
+ arduino_path / "main.cpp",
183
+ ]
184
+
185
+ return all(f.exists() for f in required_files)
186
+
187
+ def get_core_dir(self, core_name: str = "arduino") -> Path:
188
+ """Get path to specific core directory.
189
+
190
+ Args:
191
+ core_name: Core name (default: "arduino")
192
+
193
+ Returns:
194
+ Path to the core directory
195
+
196
+ Raises:
197
+ FrameworkErrorSTM32: If core directory doesn't exist
198
+ """
199
+ core_path = self.framework_path / "cores" / core_name
200
+ if not core_path.exists():
201
+ raise FrameworkErrorSTM32(f"Core '{core_name}' not found at {core_path}")
202
+ return core_path
203
+
204
+ def get_core_sources(self, core_name: str = "arduino") -> List[Path]:
205
+ """Get all source files in a core.
206
+
207
+ Args:
208
+ core_name: Core name (default: "arduino")
209
+
210
+ Returns:
211
+ List of .c and .cpp source file paths
212
+ """
213
+ core_dir = self.get_core_dir(core_name)
214
+ sources: List[Path] = []
215
+
216
+ # Get all .c and .cpp files in the core directory (recursively)
217
+ sources.extend(core_dir.rglob("*.c"))
218
+ sources.extend(core_dir.rglob("*.cpp"))
219
+
220
+ # Remove duplicates and sort
221
+ return sorted(set(sources))
222
+
223
+ def get_core_includes(self, core_name: str = "arduino") -> List[Path]:
224
+ """Get include directories for a core.
225
+
226
+ Args:
227
+ core_name: Core name (default: "arduino")
228
+
229
+ Returns:
230
+ List of include directory paths
231
+ """
232
+ core_dir = self.get_core_dir(core_name)
233
+ includes = [core_dir]
234
+
235
+ # Add api directory if it exists
236
+ api_dir = core_dir / "api"
237
+ if api_dir.exists():
238
+ includes.append(api_dir)
239
+
240
+ # Add stm32 directory if it exists
241
+ stm32_dir = core_dir / "stm32"
242
+ if stm32_dir.exists():
243
+ includes.append(stm32_dir)
244
+
245
+ # CRITICAL: Add SrcWrapper includes (clock.h, interrupt.h, PinNames.h, etc.)
246
+ srcwrapper_inc = self.framework_path / "libraries" / "SrcWrapper" / "inc"
247
+ if srcwrapper_inc.exists():
248
+ includes.append(srcwrapper_inc)
249
+
250
+ return includes
251
+
252
+ def get_stm32_system_includes(self, mcu_family: str) -> List[Path]:
253
+ """Get STM32 system include directories for CMSIS and HAL.
254
+
255
+ Args:
256
+ mcu_family: MCU family name (e.g., "STM32F4xx", "STM32F1xx")
257
+
258
+ Returns:
259
+ List of system include directory paths
260
+ """
261
+ includes = []
262
+
263
+ system_dir = self.framework_path / "system"
264
+ if not system_dir.exists():
265
+ return includes
266
+
267
+ # CMSIS Core includes
268
+ cmsis_core = system_dir / "Drivers" / "CMSIS" / "Core" / "Include"
269
+ if cmsis_core.exists():
270
+ includes.append(cmsis_core)
271
+
272
+ # CMSIS Device includes for specific MCU family
273
+ cmsis_device = system_dir / "Drivers" / "CMSIS" / "Device" / "ST" / mcu_family / "Include"
274
+ if cmsis_device.exists():
275
+ includes.append(cmsis_device)
276
+
277
+ # HAL Driver includes for specific MCU family
278
+ hal_driver = system_dir / "Drivers" / f"{mcu_family}_HAL_Driver" / "Inc"
279
+ if hal_driver.exists():
280
+ includes.append(hal_driver)
281
+
282
+ # Legacy HAL includes
283
+ hal_legacy = system_dir / "Drivers" / f"{mcu_family}_HAL_Driver" / "Inc" / "Legacy"
284
+ if hal_legacy.exists():
285
+ includes.append(hal_legacy)
286
+
287
+ # LL (Low-Layer) driver includes (same as HAL path)
288
+ # Already included above
289
+
290
+ # System directory itself
291
+ includes.append(system_dir)
292
+
293
+ return includes
294
+
295
+ def get_variant_dir(self, variant: str) -> Optional[Path]:
296
+ """Get variant directory for a specific board.
297
+
298
+ Args:
299
+ variant: Variant identifier (e.g., "STM32F4xx/F446R(C-E)T")
300
+
301
+ Returns:
302
+ Path to variant directory or None if not found
303
+ """
304
+ variants_dir = self.framework_path / "variants"
305
+ variant_path = variants_dir / variant
306
+
307
+ return variant_path if variant_path.exists() else None
308
+
309
+ def get_system_dir(self) -> Path:
310
+ """Get path to system directory containing CMSIS and HAL.
311
+
312
+ Returns:
313
+ Path to system directory
314
+
315
+ Raises:
316
+ FrameworkErrorSTM32: If system directory doesn't exist
317
+ """
318
+ system_dir = self.framework_path / "system"
319
+ if not system_dir.exists():
320
+ raise FrameworkErrorSTM32(f"System directory not found at {system_dir}")
321
+ return system_dir
322
+
323
+ def get_cmsis_dir(self) -> Optional[Path]:
324
+ """Get path to CMSIS directory.
325
+
326
+ Returns:
327
+ Path to CMSIS directory or None if not found
328
+ """
329
+ cmsis_dir = self.framework_path / "system" / "Drivers" / "CMSIS"
330
+ return cmsis_dir if cmsis_dir.exists() else None
331
+
332
+ def get_hal_dir(self, mcu_family: str) -> Optional[Path]:
333
+ """Get path to HAL drivers for a specific MCU family.
334
+
335
+ Args:
336
+ mcu_family: MCU family name (e.g., "STM32F4xx")
337
+
338
+ Returns:
339
+ Path to HAL directory or None if not found
340
+ """
341
+ hal_dir = self.framework_path / "system" / "Drivers" / f"{mcu_family}_HAL_Driver"
342
+ return hal_dir if hal_dir.exists() else None
343
+
344
+ def list_cores(self) -> List[str]:
345
+ """List all available cores.
346
+
347
+ Returns:
348
+ List of core names
349
+ """
350
+ cores_dir = self.framework_path / "cores"
351
+ if not cores_dir.exists():
352
+ return []
353
+
354
+ return [d.name for d in cores_dir.iterdir() if d.is_dir()]
355
+
356
+ def list_variants(self) -> List[str]:
357
+ """List all available variants.
358
+
359
+ Returns:
360
+ List of variant identifiers
361
+ """
362
+ variants_dir = self.framework_path / "variants"
363
+ if not variants_dir.exists():
364
+ return []
365
+
366
+ variants = []
367
+ # Variants are organized by MCU family
368
+ for family_dir in variants_dir.iterdir():
369
+ if family_dir.is_dir():
370
+ for variant_dir in family_dir.iterdir():
371
+ if variant_dir.is_dir():
372
+ variants.append(f"{family_dir.name}/{variant_dir.name}")
373
+
374
+ return sorted(variants)
375
+
376
+ def get_framework_info(self) -> Dict[str, Any]:
377
+ """Get information about the installed framework.
378
+
379
+ Returns:
380
+ Dictionary with framework information
381
+ """
382
+ info = {
383
+ "version": self.version,
384
+ "path": str(self.framework_path),
385
+ "url": self.framework_url,
386
+ "installed": self.is_installed(),
387
+ }
388
+
389
+ if self.is_installed():
390
+ info["available_cores"] = self.list_cores()
391
+ arduino_dir = self.framework_path / "cores" / "arduino"
392
+ if arduino_dir.exists():
393
+ info["arduino_path"] = str(arduino_dir)
394
+ info["arduino_sources"] = len(self.get_core_sources("arduino"))
395
+
396
+ return info
397
+
398
+ # Implement IFramework interface methods
399
+ def get_cores_dir(self) -> Path:
400
+ """Get path to cores directory.
401
+
402
+ Returns:
403
+ Path to cores directory containing Arduino core implementation
404
+
405
+ Raises:
406
+ FrameworkErrorSTM32: If cores directory doesn't exist
407
+ """
408
+ cores_dir = self.framework_path / "cores"
409
+ if not cores_dir.exists():
410
+ raise FrameworkErrorSTM32(f"Cores directory not found at {cores_dir}")
411
+ return cores_dir
412
+
413
+ def get_variants_dir(self) -> Path:
414
+ """Get path to variants directory.
415
+
416
+ Returns:
417
+ Path to variants directory
418
+
419
+ Raises:
420
+ FrameworkErrorSTM32: If variants directory doesn't exist
421
+ """
422
+ variants_dir = self.framework_path / "variants"
423
+ if not variants_dir.exists():
424
+ raise FrameworkErrorSTM32(f"Variants directory not found at {variants_dir}")
425
+ return variants_dir
426
+
427
+ def get_libraries_dir(self) -> Path:
428
+ """Get path to built-in libraries directory.
429
+
430
+ Returns:
431
+ Path to libraries directory
432
+
433
+ Raises:
434
+ FrameworkErrorSTM32: If libraries directory doesn't exist
435
+ """
436
+ libraries_dir = self.framework_path / "libraries"
437
+ if not libraries_dir.exists():
438
+ raise FrameworkErrorSTM32(f"Libraries directory not found at {libraries_dir}")
439
+ return libraries_dir
440
+
441
+ # Implement IPackage interface
442
+ def ensure_package(self) -> Path:
443
+ """Ensure package is downloaded and extracted.
444
+
445
+ Returns:
446
+ Path to the extracted package directory
447
+
448
+ Raises:
449
+ PackageError: If download or extraction fails
450
+ """
451
+ return self.ensure_framework()
452
+
453
+ def get_package_info(self) -> Dict[str, Any]:
454
+ """Get information about the package.
455
+
456
+ Returns:
457
+ Dictionary with package metadata (version, path, etc.)
458
+ """
459
+ return self.get_framework_info()