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.
- fbuild/__init__.py +390 -0
- fbuild/assets/example.txt +1 -0
- fbuild/build/__init__.py +117 -0
- fbuild/build/archive_creator.py +186 -0
- fbuild/build/binary_generator.py +444 -0
- fbuild/build/build_component_factory.py +131 -0
- fbuild/build/build_info_generator.py +624 -0
- fbuild/build/build_state.py +325 -0
- fbuild/build/build_utils.py +93 -0
- fbuild/build/compilation_executor.py +422 -0
- fbuild/build/compiler.py +165 -0
- fbuild/build/compiler_avr.py +574 -0
- fbuild/build/configurable_compiler.py +664 -0
- fbuild/build/configurable_linker.py +637 -0
- fbuild/build/flag_builder.py +214 -0
- fbuild/build/library_dependency_processor.py +185 -0
- fbuild/build/linker.py +708 -0
- fbuild/build/orchestrator.py +67 -0
- fbuild/build/orchestrator_avr.py +651 -0
- fbuild/build/orchestrator_esp32.py +878 -0
- fbuild/build/orchestrator_rp2040.py +719 -0
- fbuild/build/orchestrator_stm32.py +696 -0
- fbuild/build/orchestrator_teensy.py +580 -0
- fbuild/build/source_compilation_orchestrator.py +218 -0
- fbuild/build/source_scanner.py +516 -0
- fbuild/cli.py +717 -0
- fbuild/cli_utils.py +314 -0
- fbuild/config/__init__.py +16 -0
- fbuild/config/board_config.py +542 -0
- fbuild/config/board_loader.py +92 -0
- fbuild/config/ini_parser.py +369 -0
- fbuild/config/mcu_specs.py +88 -0
- fbuild/daemon/__init__.py +42 -0
- fbuild/daemon/async_client.py +531 -0
- fbuild/daemon/client.py +1505 -0
- fbuild/daemon/compilation_queue.py +293 -0
- fbuild/daemon/configuration_lock.py +865 -0
- fbuild/daemon/daemon.py +585 -0
- fbuild/daemon/daemon_context.py +293 -0
- fbuild/daemon/error_collector.py +263 -0
- fbuild/daemon/file_cache.py +332 -0
- fbuild/daemon/firmware_ledger.py +546 -0
- fbuild/daemon/lock_manager.py +508 -0
- fbuild/daemon/logging_utils.py +149 -0
- fbuild/daemon/messages.py +957 -0
- fbuild/daemon/operation_registry.py +288 -0
- fbuild/daemon/port_state_manager.py +249 -0
- fbuild/daemon/process_tracker.py +366 -0
- fbuild/daemon/processors/__init__.py +18 -0
- fbuild/daemon/processors/build_processor.py +248 -0
- fbuild/daemon/processors/deploy_processor.py +664 -0
- fbuild/daemon/processors/install_deps_processor.py +431 -0
- fbuild/daemon/processors/locking_processor.py +777 -0
- fbuild/daemon/processors/monitor_processor.py +285 -0
- fbuild/daemon/request_processor.py +457 -0
- fbuild/daemon/shared_serial.py +819 -0
- fbuild/daemon/status_manager.py +238 -0
- fbuild/daemon/subprocess_manager.py +316 -0
- fbuild/deploy/__init__.py +21 -0
- fbuild/deploy/deployer.py +67 -0
- fbuild/deploy/deployer_esp32.py +310 -0
- fbuild/deploy/docker_utils.py +315 -0
- fbuild/deploy/monitor.py +519 -0
- fbuild/deploy/qemu_runner.py +603 -0
- fbuild/interrupt_utils.py +34 -0
- fbuild/ledger/__init__.py +52 -0
- fbuild/ledger/board_ledger.py +560 -0
- fbuild/output.py +352 -0
- fbuild/packages/__init__.py +66 -0
- fbuild/packages/archive_utils.py +1098 -0
- fbuild/packages/arduino_core.py +412 -0
- fbuild/packages/cache.py +256 -0
- fbuild/packages/concurrent_manager.py +510 -0
- fbuild/packages/downloader.py +518 -0
- fbuild/packages/fingerprint.py +423 -0
- fbuild/packages/framework_esp32.py +538 -0
- fbuild/packages/framework_rp2040.py +349 -0
- fbuild/packages/framework_stm32.py +459 -0
- fbuild/packages/framework_teensy.py +346 -0
- fbuild/packages/github_utils.py +96 -0
- fbuild/packages/header_trampoline_cache.py +394 -0
- fbuild/packages/library_compiler.py +203 -0
- fbuild/packages/library_manager.py +549 -0
- fbuild/packages/library_manager_esp32.py +725 -0
- fbuild/packages/package.py +163 -0
- fbuild/packages/platform_esp32.py +383 -0
- fbuild/packages/platform_rp2040.py +400 -0
- fbuild/packages/platform_stm32.py +581 -0
- fbuild/packages/platform_teensy.py +312 -0
- fbuild/packages/platform_utils.py +131 -0
- fbuild/packages/platformio_registry.py +369 -0
- fbuild/packages/sdk_utils.py +231 -0
- fbuild/packages/toolchain.py +436 -0
- fbuild/packages/toolchain_binaries.py +196 -0
- fbuild/packages/toolchain_esp32.py +489 -0
- fbuild/packages/toolchain_metadata.py +185 -0
- fbuild/packages/toolchain_rp2040.py +436 -0
- fbuild/packages/toolchain_stm32.py +417 -0
- fbuild/packages/toolchain_teensy.py +404 -0
- fbuild/platform_configs/esp32.json +150 -0
- fbuild/platform_configs/esp32c2.json +144 -0
- fbuild/platform_configs/esp32c3.json +143 -0
- fbuild/platform_configs/esp32c5.json +151 -0
- fbuild/platform_configs/esp32c6.json +151 -0
- fbuild/platform_configs/esp32p4.json +149 -0
- fbuild/platform_configs/esp32s3.json +151 -0
- fbuild/platform_configs/imxrt1062.json +56 -0
- fbuild/platform_configs/rp2040.json +70 -0
- fbuild/platform_configs/rp2350.json +76 -0
- fbuild/platform_configs/stm32f1.json +59 -0
- fbuild/platform_configs/stm32f4.json +63 -0
- fbuild/py.typed +0 -0
- fbuild-1.2.8.dist-info/METADATA +468 -0
- fbuild-1.2.8.dist-info/RECORD +121 -0
- fbuild-1.2.8.dist-info/WHEEL +5 -0
- fbuild-1.2.8.dist-info/entry_points.txt +5 -0
- fbuild-1.2.8.dist-info/licenses/LICENSE +21 -0
- fbuild-1.2.8.dist-info/top_level.txt +2 -0
- fbuild_lint/__init__.py +0 -0
- fbuild_lint/ruff_plugins/__init__.py +0 -0
- fbuild_lint/ruff_plugins/keyboard_interrupt_checker.py +158 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
"""Build info generator for creating build_info.json after successful builds.
|
|
2
|
+
|
|
3
|
+
This module generates build metadata similar to PlatformIO's idedata.json,
|
|
4
|
+
providing information about the build configuration, firmware paths, memory
|
|
5
|
+
usage, and toolchain information.
|
|
6
|
+
|
|
7
|
+
Design:
|
|
8
|
+
- Stores build info in .fbuild/build/{env_name}/build_info.json
|
|
9
|
+
- Generated automatically after each successful build
|
|
10
|
+
- Contains all information needed for IDE integration and debugging
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class BoardInfo:
|
|
22
|
+
"""Board configuration information."""
|
|
23
|
+
|
|
24
|
+
id: str
|
|
25
|
+
name: str
|
|
26
|
+
mcu: str
|
|
27
|
+
platform: str
|
|
28
|
+
f_cpu: Optional[int] = None
|
|
29
|
+
|
|
30
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
31
|
+
"""Convert to dictionary for JSON serialization."""
|
|
32
|
+
return {
|
|
33
|
+
"id": self.id,
|
|
34
|
+
"name": self.name,
|
|
35
|
+
"mcu": self.mcu,
|
|
36
|
+
"platform": self.platform,
|
|
37
|
+
"f_cpu": self.f_cpu,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class FirmwareInfo:
|
|
43
|
+
"""Firmware file paths and sizes."""
|
|
44
|
+
|
|
45
|
+
elf_path: Optional[str] = None
|
|
46
|
+
hex_path: Optional[str] = None
|
|
47
|
+
bin_path: Optional[str] = None
|
|
48
|
+
elf_size_bytes: Optional[int] = None
|
|
49
|
+
hex_size_bytes: Optional[int] = None
|
|
50
|
+
bin_size_bytes: Optional[int] = None
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
53
|
+
"""Convert to dictionary for JSON serialization."""
|
|
54
|
+
return {
|
|
55
|
+
"elf_path": self.elf_path,
|
|
56
|
+
"hex_path": self.hex_path,
|
|
57
|
+
"bin_path": self.bin_path,
|
|
58
|
+
"elf_size_bytes": self.elf_size_bytes,
|
|
59
|
+
"hex_size_bytes": self.hex_size_bytes,
|
|
60
|
+
"bin_size_bytes": self.bin_size_bytes,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class MemoryUsage:
|
|
66
|
+
"""Memory usage for a specific memory type (flash or RAM)."""
|
|
67
|
+
|
|
68
|
+
used_bytes: int
|
|
69
|
+
max_bytes: Optional[int] = None
|
|
70
|
+
percent: Optional[float] = None
|
|
71
|
+
|
|
72
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
73
|
+
"""Convert to dictionary for JSON serialization."""
|
|
74
|
+
return {
|
|
75
|
+
"used_bytes": self.used_bytes,
|
|
76
|
+
"max_bytes": self.max_bytes,
|
|
77
|
+
"percent": self.percent,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class MemoryInfo:
|
|
83
|
+
"""Combined memory usage information."""
|
|
84
|
+
|
|
85
|
+
flash: Optional[MemoryUsage] = None
|
|
86
|
+
ram: Optional[MemoryUsage] = None
|
|
87
|
+
|
|
88
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
89
|
+
"""Convert to dictionary for JSON serialization."""
|
|
90
|
+
return {
|
|
91
|
+
"flash": self.flash.to_dict() if self.flash else None,
|
|
92
|
+
"ram": self.ram.to_dict() if self.ram else None,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class ToolchainInfo:
|
|
98
|
+
"""Toolchain version and path information."""
|
|
99
|
+
|
|
100
|
+
version: Optional[str] = None
|
|
101
|
+
cc_path: Optional[str] = None
|
|
102
|
+
cxx_path: Optional[str] = None
|
|
103
|
+
ar_path: Optional[str] = None
|
|
104
|
+
objcopy_path: Optional[str] = None
|
|
105
|
+
size_path: Optional[str] = None
|
|
106
|
+
|
|
107
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
108
|
+
"""Convert to dictionary for JSON serialization."""
|
|
109
|
+
return {
|
|
110
|
+
"version": self.version,
|
|
111
|
+
"cc_path": self.cc_path,
|
|
112
|
+
"cxx_path": self.cxx_path,
|
|
113
|
+
"ar_path": self.ar_path,
|
|
114
|
+
"objcopy_path": self.objcopy_path,
|
|
115
|
+
"size_path": self.size_path,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class FrameworkInfo:
|
|
121
|
+
"""Framework version and path information."""
|
|
122
|
+
|
|
123
|
+
name: str
|
|
124
|
+
version: Optional[str] = None
|
|
125
|
+
path: Optional[str] = None
|
|
126
|
+
|
|
127
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
128
|
+
"""Convert to dictionary for JSON serialization."""
|
|
129
|
+
return {
|
|
130
|
+
"name": self.name,
|
|
131
|
+
"version": self.version,
|
|
132
|
+
"path": self.path,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class ESP32SpecificInfo:
|
|
138
|
+
"""ESP32-specific build information."""
|
|
139
|
+
|
|
140
|
+
bootloader_path: Optional[str] = None
|
|
141
|
+
partitions_path: Optional[str] = None
|
|
142
|
+
application_offset: Optional[str] = None
|
|
143
|
+
flash_mode: Optional[str] = None
|
|
144
|
+
flash_size: Optional[str] = None
|
|
145
|
+
|
|
146
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
147
|
+
"""Convert to dictionary for JSON serialization."""
|
|
148
|
+
return {
|
|
149
|
+
"bootloader_path": self.bootloader_path,
|
|
150
|
+
"partitions_path": self.partitions_path,
|
|
151
|
+
"application_offset": self.application_offset,
|
|
152
|
+
"flash_mode": self.flash_mode,
|
|
153
|
+
"flash_size": self.flash_size,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass
|
|
158
|
+
class BuildInfo:
|
|
159
|
+
"""Complete build information."""
|
|
160
|
+
|
|
161
|
+
version: str = "1.0"
|
|
162
|
+
build_timestamp: str = ""
|
|
163
|
+
build_time_seconds: float = 0.0
|
|
164
|
+
environment: str = ""
|
|
165
|
+
board: Optional[BoardInfo] = None
|
|
166
|
+
firmware: Optional[FirmwareInfo] = None
|
|
167
|
+
memory: Optional[MemoryInfo] = None
|
|
168
|
+
build_flags: List[str] = field(default_factory=list)
|
|
169
|
+
lib_deps: List[str] = field(default_factory=list)
|
|
170
|
+
toolchain: Optional[ToolchainInfo] = None
|
|
171
|
+
framework: Optional[FrameworkInfo] = None
|
|
172
|
+
esp32_specific: Optional[ESP32SpecificInfo] = None
|
|
173
|
+
|
|
174
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
175
|
+
"""Convert to dictionary for JSON serialization."""
|
|
176
|
+
return {
|
|
177
|
+
"version": self.version,
|
|
178
|
+
"build_timestamp": self.build_timestamp,
|
|
179
|
+
"build_time_seconds": self.build_time_seconds,
|
|
180
|
+
"environment": self.environment,
|
|
181
|
+
"board": self.board.to_dict() if self.board else None,
|
|
182
|
+
"firmware": self.firmware.to_dict() if self.firmware else None,
|
|
183
|
+
"memory": self.memory.to_dict() if self.memory else None,
|
|
184
|
+
"build_flags": self.build_flags,
|
|
185
|
+
"lib_deps": self.lib_deps,
|
|
186
|
+
"toolchain": self.toolchain.to_dict() if self.toolchain else None,
|
|
187
|
+
"framework": self.framework.to_dict() if self.framework else None,
|
|
188
|
+
"esp32_specific": self.esp32_specific.to_dict() if self.esp32_specific else None,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class BuildInfoGenerator:
|
|
193
|
+
"""Generates and saves build_info.json after successful builds."""
|
|
194
|
+
|
|
195
|
+
BUILD_INFO_FILENAME = "build_info.json"
|
|
196
|
+
SCHEMA_VERSION = "1.0"
|
|
197
|
+
|
|
198
|
+
def __init__(self, build_dir: Path):
|
|
199
|
+
"""Initialize the build info generator.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
build_dir: Build directory (.fbuild/build/{env_name})
|
|
203
|
+
"""
|
|
204
|
+
self.build_dir = Path(build_dir)
|
|
205
|
+
self.build_info_path = self.build_dir / self.BUILD_INFO_FILENAME
|
|
206
|
+
|
|
207
|
+
def generate_avr(
|
|
208
|
+
self,
|
|
209
|
+
env_name: str,
|
|
210
|
+
board_id: str,
|
|
211
|
+
board_name: str,
|
|
212
|
+
mcu: str,
|
|
213
|
+
f_cpu: int,
|
|
214
|
+
build_time: float,
|
|
215
|
+
elf_path: Optional[Path],
|
|
216
|
+
hex_path: Optional[Path],
|
|
217
|
+
size_info: Optional[Any],
|
|
218
|
+
build_flags: List[str],
|
|
219
|
+
lib_deps: List[str],
|
|
220
|
+
toolchain_version: str,
|
|
221
|
+
toolchain_paths: Dict[str, Path],
|
|
222
|
+
framework_version: str,
|
|
223
|
+
core_path: Optional[Path] = None,
|
|
224
|
+
) -> BuildInfo:
|
|
225
|
+
"""Generate build info for AVR platform.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
env_name: Environment name (e.g., 'uno')
|
|
229
|
+
board_id: Board ID (e.g., 'uno')
|
|
230
|
+
board_name: Human-readable board name (e.g., 'Arduino Uno')
|
|
231
|
+
mcu: MCU type (e.g., 'atmega328p')
|
|
232
|
+
f_cpu: CPU frequency in Hz
|
|
233
|
+
build_time: Build duration in seconds
|
|
234
|
+
elf_path: Path to generated .elf file
|
|
235
|
+
hex_path: Path to generated .hex file
|
|
236
|
+
size_info: SizeInfo object from linker
|
|
237
|
+
build_flags: List of build flags
|
|
238
|
+
lib_deps: List of library dependencies
|
|
239
|
+
toolchain_version: Toolchain version string
|
|
240
|
+
toolchain_paths: Dict of toolchain binary paths
|
|
241
|
+
framework_version: Arduino core version
|
|
242
|
+
core_path: Path to Arduino core
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
BuildInfo object with all metadata
|
|
246
|
+
"""
|
|
247
|
+
# Board info
|
|
248
|
+
board = BoardInfo(
|
|
249
|
+
id=board_id,
|
|
250
|
+
name=board_name,
|
|
251
|
+
mcu=mcu,
|
|
252
|
+
platform="atmelavr",
|
|
253
|
+
f_cpu=f_cpu,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Firmware info
|
|
257
|
+
firmware = FirmwareInfo()
|
|
258
|
+
if elf_path and elf_path.exists():
|
|
259
|
+
firmware.elf_path = str(elf_path.relative_to(self.build_dir.parent.parent.parent))
|
|
260
|
+
firmware.elf_size_bytes = elf_path.stat().st_size
|
|
261
|
+
if hex_path and hex_path.exists():
|
|
262
|
+
firmware.hex_path = str(hex_path.relative_to(self.build_dir.parent.parent.parent))
|
|
263
|
+
firmware.hex_size_bytes = hex_path.stat().st_size
|
|
264
|
+
|
|
265
|
+
# Memory info from size_info
|
|
266
|
+
memory = None
|
|
267
|
+
if size_info:
|
|
268
|
+
flash_usage = MemoryUsage(
|
|
269
|
+
used_bytes=size_info.total_flash,
|
|
270
|
+
max_bytes=size_info.max_flash,
|
|
271
|
+
percent=size_info.flash_percent,
|
|
272
|
+
)
|
|
273
|
+
ram_usage = MemoryUsage(
|
|
274
|
+
used_bytes=size_info.total_ram,
|
|
275
|
+
max_bytes=size_info.max_ram,
|
|
276
|
+
percent=size_info.ram_percent,
|
|
277
|
+
)
|
|
278
|
+
memory = MemoryInfo(flash=flash_usage, ram=ram_usage)
|
|
279
|
+
|
|
280
|
+
# Toolchain info
|
|
281
|
+
toolchain = ToolchainInfo(
|
|
282
|
+
version=toolchain_version,
|
|
283
|
+
cc_path=str(toolchain_paths.get("gcc")) if toolchain_paths.get("gcc") else None,
|
|
284
|
+
cxx_path=str(toolchain_paths.get("gxx")) if toolchain_paths.get("gxx") else None,
|
|
285
|
+
ar_path=str(toolchain_paths.get("ar")) if toolchain_paths.get("ar") else None,
|
|
286
|
+
objcopy_path=str(toolchain_paths.get("objcopy")) if toolchain_paths.get("objcopy") else None,
|
|
287
|
+
size_path=str(toolchain_paths.get("size")) if toolchain_paths.get("size") else None,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Framework info
|
|
291
|
+
framework = FrameworkInfo(
|
|
292
|
+
name="arduino",
|
|
293
|
+
version=framework_version,
|
|
294
|
+
path=str(core_path) if core_path else None,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
return BuildInfo(
|
|
298
|
+
version=self.SCHEMA_VERSION,
|
|
299
|
+
build_timestamp=datetime.now(timezone.utc).isoformat(),
|
|
300
|
+
build_time_seconds=round(build_time, 3),
|
|
301
|
+
environment=env_name,
|
|
302
|
+
board=board,
|
|
303
|
+
firmware=firmware,
|
|
304
|
+
memory=memory,
|
|
305
|
+
build_flags=build_flags or [],
|
|
306
|
+
lib_deps=lib_deps or [],
|
|
307
|
+
toolchain=toolchain,
|
|
308
|
+
framework=framework,
|
|
309
|
+
esp32_specific=None,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
def generate_esp32(
|
|
313
|
+
self,
|
|
314
|
+
env_name: str,
|
|
315
|
+
board_id: str,
|
|
316
|
+
board_name: str,
|
|
317
|
+
mcu: str,
|
|
318
|
+
f_cpu: int,
|
|
319
|
+
build_time: float,
|
|
320
|
+
elf_path: Optional[Path],
|
|
321
|
+
bin_path: Optional[Path],
|
|
322
|
+
size_info: Optional[Any],
|
|
323
|
+
build_flags: List[str],
|
|
324
|
+
lib_deps: List[str],
|
|
325
|
+
toolchain_version: str,
|
|
326
|
+
toolchain_paths: Dict[str, Path],
|
|
327
|
+
framework_version: str,
|
|
328
|
+
core_path: Optional[Path] = None,
|
|
329
|
+
bootloader_path: Optional[Path] = None,
|
|
330
|
+
partitions_path: Optional[Path] = None,
|
|
331
|
+
application_offset: Optional[str] = None,
|
|
332
|
+
flash_mode: Optional[str] = None,
|
|
333
|
+
flash_size: Optional[str] = None,
|
|
334
|
+
) -> BuildInfo:
|
|
335
|
+
"""Generate build info for ESP32 platform.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
env_name: Environment name
|
|
339
|
+
board_id: Board ID
|
|
340
|
+
board_name: Human-readable board name
|
|
341
|
+
mcu: MCU type (e.g., 'esp32c6')
|
|
342
|
+
f_cpu: CPU frequency in Hz
|
|
343
|
+
build_time: Build duration in seconds
|
|
344
|
+
elf_path: Path to generated .elf file
|
|
345
|
+
bin_path: Path to generated .bin file
|
|
346
|
+
size_info: SizeInfo object from linker
|
|
347
|
+
build_flags: List of build flags
|
|
348
|
+
lib_deps: List of library dependencies
|
|
349
|
+
toolchain_version: Toolchain version string
|
|
350
|
+
toolchain_paths: Dict of toolchain binary paths
|
|
351
|
+
framework_version: ESP32 Arduino framework version
|
|
352
|
+
core_path: Path to framework core
|
|
353
|
+
bootloader_path: Path to bootloader.bin
|
|
354
|
+
partitions_path: Path to partitions.bin
|
|
355
|
+
application_offset: Application offset (e.g., '0x10000')
|
|
356
|
+
flash_mode: Flash mode (e.g., 'dio', 'qio')
|
|
357
|
+
flash_size: Flash size (e.g., '4MB')
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
BuildInfo object with all metadata
|
|
361
|
+
"""
|
|
362
|
+
# Board info
|
|
363
|
+
board = BoardInfo(
|
|
364
|
+
id=board_id,
|
|
365
|
+
name=board_name,
|
|
366
|
+
mcu=mcu,
|
|
367
|
+
platform="espressif32",
|
|
368
|
+
f_cpu=f_cpu,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# Firmware info
|
|
372
|
+
firmware = FirmwareInfo()
|
|
373
|
+
try:
|
|
374
|
+
if elf_path and elf_path.exists():
|
|
375
|
+
firmware.elf_path = str(elf_path.relative_to(self.build_dir.parent.parent.parent))
|
|
376
|
+
firmware.elf_size_bytes = elf_path.stat().st_size
|
|
377
|
+
if bin_path and bin_path.exists():
|
|
378
|
+
firmware.bin_path = str(bin_path.relative_to(self.build_dir.parent.parent.parent))
|
|
379
|
+
firmware.bin_size_bytes = bin_path.stat().st_size
|
|
380
|
+
except ValueError:
|
|
381
|
+
# If relative_to fails, use absolute paths
|
|
382
|
+
if elf_path and elf_path.exists():
|
|
383
|
+
firmware.elf_path = str(elf_path)
|
|
384
|
+
firmware.elf_size_bytes = elf_path.stat().st_size
|
|
385
|
+
if bin_path and bin_path.exists():
|
|
386
|
+
firmware.bin_path = str(bin_path)
|
|
387
|
+
firmware.bin_size_bytes = bin_path.stat().st_size
|
|
388
|
+
|
|
389
|
+
# Memory info from size_info
|
|
390
|
+
memory = None
|
|
391
|
+
if size_info:
|
|
392
|
+
flash_usage = MemoryUsage(
|
|
393
|
+
used_bytes=getattr(size_info, 'total_flash', 0),
|
|
394
|
+
max_bytes=getattr(size_info, 'max_flash', None),
|
|
395
|
+
percent=getattr(size_info, 'flash_percent', None),
|
|
396
|
+
)
|
|
397
|
+
ram_usage = MemoryUsage(
|
|
398
|
+
used_bytes=getattr(size_info, 'total_ram', 0),
|
|
399
|
+
max_bytes=getattr(size_info, 'max_ram', None),
|
|
400
|
+
percent=getattr(size_info, 'ram_percent', None),
|
|
401
|
+
)
|
|
402
|
+
memory = MemoryInfo(flash=flash_usage, ram=ram_usage)
|
|
403
|
+
|
|
404
|
+
# Toolchain info
|
|
405
|
+
toolchain = ToolchainInfo(
|
|
406
|
+
version=toolchain_version,
|
|
407
|
+
cc_path=str(toolchain_paths.get("gcc")) if toolchain_paths.get("gcc") else None,
|
|
408
|
+
cxx_path=str(toolchain_paths.get("gxx")) if toolchain_paths.get("gxx") else None,
|
|
409
|
+
ar_path=str(toolchain_paths.get("ar")) if toolchain_paths.get("ar") else None,
|
|
410
|
+
objcopy_path=str(toolchain_paths.get("objcopy")) if toolchain_paths.get("objcopy") else None,
|
|
411
|
+
size_path=str(toolchain_paths.get("size")) if toolchain_paths.get("size") else None,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Framework info
|
|
415
|
+
framework = FrameworkInfo(
|
|
416
|
+
name="arduino",
|
|
417
|
+
version=framework_version,
|
|
418
|
+
path=str(core_path) if core_path else None,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# ESP32-specific info
|
|
422
|
+
esp32_specific = None
|
|
423
|
+
if bootloader_path or partitions_path or application_offset:
|
|
424
|
+
esp32_specific = ESP32SpecificInfo(
|
|
425
|
+
bootloader_path=str(bootloader_path) if bootloader_path else None,
|
|
426
|
+
partitions_path=str(partitions_path) if partitions_path else None,
|
|
427
|
+
application_offset=application_offset,
|
|
428
|
+
flash_mode=flash_mode,
|
|
429
|
+
flash_size=flash_size,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
return BuildInfo(
|
|
433
|
+
version=self.SCHEMA_VERSION,
|
|
434
|
+
build_timestamp=datetime.now(timezone.utc).isoformat(),
|
|
435
|
+
build_time_seconds=round(build_time, 3),
|
|
436
|
+
environment=env_name,
|
|
437
|
+
board=board,
|
|
438
|
+
firmware=firmware,
|
|
439
|
+
memory=memory,
|
|
440
|
+
build_flags=build_flags or [],
|
|
441
|
+
lib_deps=lib_deps or [],
|
|
442
|
+
toolchain=toolchain,
|
|
443
|
+
framework=framework,
|
|
444
|
+
esp32_specific=esp32_specific,
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def generate_generic(
|
|
448
|
+
self,
|
|
449
|
+
env_name: str,
|
|
450
|
+
board_id: str,
|
|
451
|
+
board_name: str,
|
|
452
|
+
mcu: str,
|
|
453
|
+
platform: str,
|
|
454
|
+
f_cpu: int,
|
|
455
|
+
build_time: float,
|
|
456
|
+
elf_path: Optional[Path],
|
|
457
|
+
hex_path: Optional[Path] = None,
|
|
458
|
+
bin_path: Optional[Path] = None,
|
|
459
|
+
size_info: Optional[Any] = None,
|
|
460
|
+
build_flags: Optional[List[str]] = None,
|
|
461
|
+
lib_deps: Optional[List[str]] = None,
|
|
462
|
+
toolchain_version: Optional[str] = None,
|
|
463
|
+
toolchain_paths: Optional[Dict[str, Path]] = None,
|
|
464
|
+
framework_name: str = "arduino",
|
|
465
|
+
framework_version: Optional[str] = None,
|
|
466
|
+
core_path: Optional[Path] = None,
|
|
467
|
+
) -> BuildInfo:
|
|
468
|
+
"""Generate build info for any platform (generic method).
|
|
469
|
+
|
|
470
|
+
This is a generic method that can be used for platforms that don't
|
|
471
|
+
have specific generate methods (Teensy, RP2040, STM32).
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
env_name: Environment name
|
|
475
|
+
board_id: Board ID
|
|
476
|
+
board_name: Human-readable board name
|
|
477
|
+
mcu: MCU type
|
|
478
|
+
platform: Platform name
|
|
479
|
+
f_cpu: CPU frequency in Hz
|
|
480
|
+
build_time: Build duration in seconds
|
|
481
|
+
elf_path: Path to generated .elf file
|
|
482
|
+
hex_path: Path to generated .hex file (optional)
|
|
483
|
+
bin_path: Path to generated .bin file (optional)
|
|
484
|
+
size_info: SizeInfo object from linker
|
|
485
|
+
build_flags: List of build flags
|
|
486
|
+
lib_deps: List of library dependencies
|
|
487
|
+
toolchain_version: Toolchain version string
|
|
488
|
+
toolchain_paths: Dict of toolchain binary paths
|
|
489
|
+
framework_name: Framework name (default: 'arduino')
|
|
490
|
+
framework_version: Framework version
|
|
491
|
+
core_path: Path to framework core
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
BuildInfo object with all metadata
|
|
495
|
+
"""
|
|
496
|
+
# Board info
|
|
497
|
+
board = BoardInfo(
|
|
498
|
+
id=board_id,
|
|
499
|
+
name=board_name,
|
|
500
|
+
mcu=mcu,
|
|
501
|
+
platform=platform,
|
|
502
|
+
f_cpu=f_cpu,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Firmware info
|
|
506
|
+
firmware = FirmwareInfo()
|
|
507
|
+
try:
|
|
508
|
+
base_dir = self.build_dir.parent.parent.parent
|
|
509
|
+
if elf_path and elf_path.exists():
|
|
510
|
+
try:
|
|
511
|
+
firmware.elf_path = str(elf_path.relative_to(base_dir))
|
|
512
|
+
except ValueError:
|
|
513
|
+
firmware.elf_path = str(elf_path)
|
|
514
|
+
firmware.elf_size_bytes = elf_path.stat().st_size
|
|
515
|
+
if hex_path and hex_path.exists():
|
|
516
|
+
try:
|
|
517
|
+
firmware.hex_path = str(hex_path.relative_to(base_dir))
|
|
518
|
+
except ValueError:
|
|
519
|
+
firmware.hex_path = str(hex_path)
|
|
520
|
+
firmware.hex_size_bytes = hex_path.stat().st_size
|
|
521
|
+
if bin_path and bin_path.exists():
|
|
522
|
+
try:
|
|
523
|
+
firmware.bin_path = str(bin_path.relative_to(base_dir))
|
|
524
|
+
except ValueError:
|
|
525
|
+
firmware.bin_path = str(bin_path)
|
|
526
|
+
firmware.bin_size_bytes = bin_path.stat().st_size
|
|
527
|
+
except KeyboardInterrupt as ke:
|
|
528
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
529
|
+
|
|
530
|
+
handle_keyboard_interrupt_properly(ke)
|
|
531
|
+
except Exception:
|
|
532
|
+
pass # Silently ignore path resolution errors
|
|
533
|
+
|
|
534
|
+
# Memory info from size_info
|
|
535
|
+
memory = None
|
|
536
|
+
if size_info:
|
|
537
|
+
flash_usage = MemoryUsage(
|
|
538
|
+
used_bytes=getattr(size_info, 'total_flash', 0),
|
|
539
|
+
max_bytes=getattr(size_info, 'max_flash', None),
|
|
540
|
+
percent=getattr(size_info, 'flash_percent', None),
|
|
541
|
+
)
|
|
542
|
+
ram_usage = MemoryUsage(
|
|
543
|
+
used_bytes=getattr(size_info, 'total_ram', 0),
|
|
544
|
+
max_bytes=getattr(size_info, 'max_ram', None),
|
|
545
|
+
percent=getattr(size_info, 'ram_percent', None),
|
|
546
|
+
)
|
|
547
|
+
memory = MemoryInfo(flash=flash_usage, ram=ram_usage)
|
|
548
|
+
|
|
549
|
+
# Toolchain info
|
|
550
|
+
toolchain = None
|
|
551
|
+
if toolchain_paths:
|
|
552
|
+
toolchain = ToolchainInfo(
|
|
553
|
+
version=toolchain_version,
|
|
554
|
+
cc_path=str(toolchain_paths.get("gcc")) if toolchain_paths.get("gcc") else None,
|
|
555
|
+
cxx_path=str(toolchain_paths.get("gxx")) if toolchain_paths.get("gxx") else None,
|
|
556
|
+
ar_path=str(toolchain_paths.get("ar")) if toolchain_paths.get("ar") else None,
|
|
557
|
+
objcopy_path=str(toolchain_paths.get("objcopy")) if toolchain_paths.get("objcopy") else None,
|
|
558
|
+
size_path=str(toolchain_paths.get("size")) if toolchain_paths.get("size") else None,
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
# Framework info
|
|
562
|
+
framework = FrameworkInfo(
|
|
563
|
+
name=framework_name,
|
|
564
|
+
version=framework_version,
|
|
565
|
+
path=str(core_path) if core_path else None,
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
return BuildInfo(
|
|
569
|
+
version=self.SCHEMA_VERSION,
|
|
570
|
+
build_timestamp=datetime.now(timezone.utc).isoformat(),
|
|
571
|
+
build_time_seconds=round(build_time, 3),
|
|
572
|
+
environment=env_name,
|
|
573
|
+
board=board,
|
|
574
|
+
firmware=firmware,
|
|
575
|
+
memory=memory,
|
|
576
|
+
build_flags=build_flags or [],
|
|
577
|
+
lib_deps=lib_deps or [],
|
|
578
|
+
toolchain=toolchain,
|
|
579
|
+
framework=framework,
|
|
580
|
+
esp32_specific=None,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
def save(self, build_info: BuildInfo) -> Path:
|
|
584
|
+
"""Save build info to JSON file.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
build_info: BuildInfo object to save
|
|
588
|
+
|
|
589
|
+
Returns:
|
|
590
|
+
Path to the saved build_info.json file
|
|
591
|
+
"""
|
|
592
|
+
self.build_dir.mkdir(parents=True, exist_ok=True)
|
|
593
|
+
|
|
594
|
+
with open(self.build_info_path, "w", encoding="utf-8") as f:
|
|
595
|
+
json.dump(build_info.to_dict(), f, indent=2)
|
|
596
|
+
|
|
597
|
+
return self.build_info_path
|
|
598
|
+
|
|
599
|
+
def load(self) -> Optional[BuildInfo]:
|
|
600
|
+
"""Load build info from JSON file.
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
BuildInfo object or None if file doesn't exist or is corrupted
|
|
604
|
+
"""
|
|
605
|
+
if not self.build_info_path.exists():
|
|
606
|
+
return None
|
|
607
|
+
|
|
608
|
+
try:
|
|
609
|
+
with open(self.build_info_path, "r", encoding="utf-8") as f:
|
|
610
|
+
data = json.load(f)
|
|
611
|
+
|
|
612
|
+
# Reconstruct BuildInfo from dict
|
|
613
|
+
# This is a simplified reconstruction - for full fidelity,
|
|
614
|
+
# we'd need from_dict methods on all dataclasses
|
|
615
|
+
return BuildInfo(
|
|
616
|
+
version=data.get("version", "1.0"),
|
|
617
|
+
build_timestamp=data.get("build_timestamp", ""),
|
|
618
|
+
build_time_seconds=data.get("build_time_seconds", 0.0),
|
|
619
|
+
environment=data.get("environment", ""),
|
|
620
|
+
build_flags=data.get("build_flags", []),
|
|
621
|
+
lib_deps=data.get("lib_deps", []),
|
|
622
|
+
)
|
|
623
|
+
except (json.JSONDecodeError, KeyError):
|
|
624
|
+
return None
|