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
fbuild/output.py ADDED
@@ -0,0 +1,352 @@
1
+ """
2
+ Centralized logging and output module for fbuild.
3
+
4
+ This module provides timestamped output from program launch to help audit
5
+ where time is spent during builds. All output is prefixed with elapsed time
6
+ in MM:SS.cc format (minutes:seconds.centiseconds).
7
+
8
+ Example output:
9
+ 00:00.12 fbuild Build System v1.2.4
10
+ 00:00.15 Building environment: uno...
11
+ 00:01.23 [1/9] Parsing platformio.ini...
12
+ 00:01.45 Board: Arduino Uno
13
+ 00:02.67 MCU: atmega328p
14
+
15
+ Usage:
16
+ from fbuild.output import log, log_phase, log_detail, init_timer
17
+
18
+ # Initialize at program start (done automatically on first use)
19
+ init_timer()
20
+
21
+ # Log a message with timestamp
22
+ log("Building environment: uno...")
23
+
24
+ # Log a build phase
25
+ log_phase(1, 9, "Parsing platformio.ini...")
26
+
27
+ # Log a detail (indented)
28
+ log_detail("Board: Arduino Uno")
29
+ """
30
+
31
+ import sys
32
+ import time
33
+ from pathlib import Path
34
+ from types import TracebackType
35
+ from typing import Optional, TextIO
36
+
37
+ # Global state for the timer
38
+ _start_time: Optional[float] = None
39
+ _output_stream: TextIO = sys.stdout
40
+ _verbose: bool = True
41
+
42
+
43
+ def init_timer(output_stream: Optional[TextIO] = None) -> None:
44
+ """
45
+ Initialize the program timer.
46
+
47
+ Call this at program startup to set the reference time for all timestamps.
48
+ If not called explicitly, it will be called automatically on first log.
49
+
50
+ Args:
51
+ output_stream: Optional output stream (defaults to sys.stdout)
52
+ """
53
+ global _start_time, _output_stream
54
+ _start_time = time.time()
55
+ if output_stream is not None:
56
+ _output_stream = output_stream
57
+
58
+
59
+ def reset_timer() -> None:
60
+ """
61
+ Reset the timer to current time.
62
+
63
+ Useful for resetting the epoch at the start of a new build phase.
64
+ """
65
+ global _start_time
66
+ _start_time = time.time()
67
+
68
+
69
+ def set_verbose(verbose: bool) -> None:
70
+ """
71
+ Set verbose mode for logging.
72
+
73
+ Args:
74
+ verbose: If True, all messages are printed. If False, only non-verbose messages.
75
+ """
76
+ global _verbose
77
+ _verbose = verbose
78
+
79
+
80
+ def get_elapsed() -> float:
81
+ """
82
+ Get elapsed time since timer initialization.
83
+
84
+ Returns:
85
+ Elapsed time in seconds
86
+ """
87
+ global _start_time
88
+ if _start_time is None:
89
+ init_timer()
90
+ return time.time() - _start_time # type: ignore
91
+
92
+
93
+ def format_timestamp() -> str:
94
+ """
95
+ Format the current elapsed time as MM:SS.cc.
96
+
97
+ Returns:
98
+ Formatted timestamp string
99
+ """
100
+ elapsed = get_elapsed()
101
+ minutes = int(elapsed // 60)
102
+ seconds = elapsed % 60
103
+ return f"{minutes:02d}:{seconds:05.2f}"
104
+
105
+
106
+ def _print(message: str, end: str = "\n") -> None:
107
+ """
108
+ Internal print function with timestamp.
109
+
110
+ Args:
111
+ message: Message to print
112
+ end: End character (default newline)
113
+ """
114
+ timestamp = format_timestamp()
115
+ _output_stream.write(f"{timestamp} {message}{end}")
116
+ _output_stream.flush()
117
+
118
+
119
+ def log(message: str, verbose_only: bool = False) -> None:
120
+ """
121
+ Log a message with timestamp.
122
+
123
+ Args:
124
+ message: Message to log
125
+ verbose_only: If True, only print if verbose mode is enabled
126
+ """
127
+ global _verbose
128
+ if verbose_only and not _verbose:
129
+ return
130
+ _print(message)
131
+
132
+
133
+ def log_phase(phase: int, total: int, message: str, verbose_only: bool = False) -> None:
134
+ """
135
+ Log a build phase message.
136
+
137
+ Format: [N/M] message
138
+
139
+ Args:
140
+ phase: Current phase number
141
+ total: Total number of phases
142
+ message: Phase description
143
+ verbose_only: If True, only print if verbose mode is enabled
144
+ """
145
+ global _verbose
146
+ if verbose_only and not _verbose:
147
+ return
148
+ _print(f"[{phase}/{total}] {message}")
149
+
150
+
151
+ def log_detail(message: str, indent: int = 6, verbose_only: bool = False) -> None:
152
+ """
153
+ Log a detail message (indented).
154
+
155
+ Args:
156
+ message: Detail message
157
+ indent: Number of spaces to indent (default 6)
158
+ verbose_only: If True, only print if verbose mode is enabled
159
+ """
160
+ global _verbose
161
+ if verbose_only and not _verbose:
162
+ return
163
+ _print(f"{' ' * indent}{message}")
164
+
165
+
166
+ def log_file(source_type: str, filename: str, cached: bool = False, verbose_only: bool = True) -> None:
167
+ """
168
+ Log a file compilation message.
169
+
170
+ Format: [source_type] filename (cached)
171
+
172
+ Args:
173
+ source_type: Type of source (e.g., 'sketch', 'core', 'variant')
174
+ filename: Name of the file
175
+ cached: If True, append "(cached)" to message
176
+ verbose_only: If True, only print if verbose mode is enabled
177
+ """
178
+ global _verbose
179
+ if verbose_only and not _verbose:
180
+ return
181
+ suffix = " (cached)" if cached else ""
182
+ _print(f" [{source_type}] {filename}{suffix}")
183
+
184
+
185
+ def log_header(title: str, version: str) -> None:
186
+ """
187
+ Log a header message (e.g., program startup).
188
+
189
+ Args:
190
+ title: Program title
191
+ version: Version string
192
+ """
193
+ _print(f"{title} v{version}")
194
+ _print("")
195
+
196
+
197
+ def log_size_info(
198
+ program_bytes: int,
199
+ program_percent: Optional[float],
200
+ max_flash: Optional[int],
201
+ data_bytes: int,
202
+ bss_bytes: int,
203
+ ram_bytes: int,
204
+ ram_percent: Optional[float],
205
+ max_ram: Optional[int],
206
+ verbose_only: bool = False,
207
+ ) -> None:
208
+ """
209
+ Log firmware size information.
210
+
211
+ Args:
212
+ program_bytes: Program flash usage in bytes
213
+ program_percent: Percentage of flash used (or None)
214
+ max_flash: Maximum flash size (or None)
215
+ data_bytes: Data section size in bytes
216
+ bss_bytes: BSS section size in bytes
217
+ ram_bytes: Total RAM usage in bytes
218
+ ram_percent: Percentage of RAM used (or None)
219
+ max_ram: Maximum RAM size (or None)
220
+ verbose_only: If True, only print if verbose mode is enabled
221
+ """
222
+ global _verbose
223
+ if verbose_only and not _verbose:
224
+ return
225
+
226
+ _print("Firmware Size:")
227
+
228
+ if program_percent is not None and max_flash is not None:
229
+ _print(f" Program: {program_bytes:6d} bytes ({program_percent:5.1f}% of {max_flash} bytes)")
230
+ else:
231
+ _print(f" Program: {program_bytes:6d} bytes")
232
+
233
+ _print(f" Data: {data_bytes:6d} bytes")
234
+ _print(f" BSS: {bss_bytes:6d} bytes")
235
+
236
+ if ram_percent is not None and max_ram is not None:
237
+ _print(f" RAM: {ram_bytes:6d} bytes ({ram_percent:5.1f}% of {max_ram} bytes)")
238
+ else:
239
+ _print(f" RAM: {ram_bytes:6d} bytes")
240
+
241
+
242
+ def log_build_complete(build_time: float, verbose_only: bool = False) -> None:
243
+ """
244
+ Log build completion message.
245
+
246
+ Args:
247
+ build_time: Total build time in seconds
248
+ verbose_only: If True, only print if verbose mode is enabled
249
+ """
250
+ global _verbose
251
+ if verbose_only and not _verbose:
252
+ return
253
+ _print("")
254
+ _print(f"Build time: {build_time:.2f}s")
255
+
256
+
257
+ def log_error(message: str) -> None:
258
+ """
259
+ Log an error message.
260
+
261
+ Args:
262
+ message: Error message
263
+ """
264
+ _print(f"ERROR: {message}")
265
+
266
+
267
+ def log_warning(message: str) -> None:
268
+ """
269
+ Log a warning message.
270
+
271
+ Args:
272
+ message: Warning message
273
+ """
274
+ _print(f"WARNING: {message}")
275
+
276
+
277
+ def log_success(message: str) -> None:
278
+ """
279
+ Log a success message.
280
+
281
+ Args:
282
+ message: Success message
283
+ """
284
+ _print(message)
285
+
286
+
287
+ def log_firmware_path(path: Path, verbose_only: bool = False) -> None:
288
+ """
289
+ Log firmware output path.
290
+
291
+ Args:
292
+ path: Path to firmware file
293
+ verbose_only: If True, only print if verbose mode is enabled
294
+ """
295
+ global _verbose
296
+ if verbose_only and not _verbose:
297
+ return
298
+ log_detail(f"Firmware: {path}")
299
+
300
+
301
+ class TimedLogger:
302
+ """
303
+ Context manager for logging with elapsed time tracking.
304
+
305
+ Usage:
306
+ with TimedLogger("Compiling sources") as logger:
307
+ # Do compilation
308
+ logger.detail("Compiled 10 files")
309
+ # Automatically logs completion time
310
+ """
311
+
312
+ def __init__(self, operation: str, phase: Optional[tuple[int, int]] = None, verbose_only: bool = False):
313
+ """
314
+ Initialize timed logger.
315
+
316
+ Args:
317
+ operation: Description of the operation
318
+ phase: Optional (current, total) phase numbers
319
+ verbose_only: If True, only print if verbose mode is enabled
320
+ """
321
+ self.operation = operation
322
+ self.phase = phase
323
+ self.verbose_only = verbose_only
324
+ self.start_time = 0.0
325
+
326
+ def __enter__(self) -> "TimedLogger":
327
+ self.start_time = time.time()
328
+ if self.phase:
329
+ log_phase(self.phase[0], self.phase[1], f"{self.operation}...", self.verbose_only)
330
+ else:
331
+ log(f"{self.operation}...", self.verbose_only)
332
+ return self
333
+
334
+ def __exit__(
335
+ self,
336
+ exc_type: Optional[type[BaseException]],
337
+ exc_val: Optional[BaseException],
338
+ exc_tb: Optional[TracebackType],
339
+ ) -> None:
340
+ del exc_val, exc_tb # Unused
341
+ elapsed = time.time() - self.start_time
342
+ if exc_type is None:
343
+ log_detail(f"Done ({elapsed:.2f}s)", verbose_only=self.verbose_only)
344
+ return None
345
+
346
+ def detail(self, message: str) -> None:
347
+ """Log a detail message within this operation."""
348
+ log_detail(message, verbose_only=self.verbose_only)
349
+
350
+ def log(self, message: str) -> None:
351
+ """Log a message within this operation."""
352
+ log(message, self.verbose_only)
@@ -0,0 +1,66 @@
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 .concurrent_manager import (
11
+ ConcurrentPackageManager,
12
+ PackageLockError,
13
+ PackageResult,
14
+ PackageSpec,
15
+ )
16
+ from .downloader import ChecksumError, DownloadError, ExtractionError, PackageDownloader
17
+ from .fingerprint import FingerprintRegistry, PackageFingerprint
18
+ from .github_utils import GitHubURLOptimizer
19
+ from .library_compiler import LibraryCompilationError, LibraryCompiler
20
+ from .package import IFramework, IPackage
21
+ from .package import IToolchain as BaseToolchain
22
+ from .package import PackageError
23
+ from .platform_esp32 import PlatformErrorESP32, PlatformESP32
24
+ from .platform_utils import PlatformDetector, PlatformError
25
+ from .sdk_utils import SDKPathResolver
26
+ from .toolchain import ToolchainAVR as Toolchain
27
+ from .toolchain import ToolchainError
28
+ from .toolchain_binaries import BinaryNotFoundError, ToolchainBinaryFinder
29
+ from .toolchain_metadata import MetadataParseError, ToolchainMetadataParser
30
+
31
+ __all__ = [
32
+ "IPackage",
33
+ "BaseToolchain",
34
+ "IFramework",
35
+ "PackageError",
36
+ "Cache",
37
+ "PackageDownloader",
38
+ "DownloadError",
39
+ "ChecksumError",
40
+ "ExtractionError",
41
+ "Toolchain",
42
+ "ToolchainError",
43
+ "ArduinoCore",
44
+ "ArduinoCoreError",
45
+ "PlatformESP32",
46
+ "PlatformErrorESP32",
47
+ "GitHubURLOptimizer",
48
+ "LibraryCompiler",
49
+ "LibraryCompilationError",
50
+ "ArchiveExtractor",
51
+ "ArchiveExtractionError",
52
+ "URLVersionExtractor",
53
+ "SDKPathResolver",
54
+ "PlatformDetector",
55
+ "PlatformError",
56
+ "ToolchainBinaryFinder",
57
+ "BinaryNotFoundError",
58
+ "ToolchainMetadataParser",
59
+ "MetadataParseError",
60
+ "PackageFingerprint",
61
+ "FingerprintRegistry",
62
+ "ConcurrentPackageManager",
63
+ "PackageSpec",
64
+ "PackageResult",
65
+ "PackageLockError",
66
+ ]