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,580 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Teensy-specific build orchestration for Fbuild projects.
|
|
3
|
+
|
|
4
|
+
This module handles Teensy platform builds separately from AVR/ESP32 builds,
|
|
5
|
+
providing cleaner separation of concerns and better maintainability.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import _thread
|
|
9
|
+
import time
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
from ..packages import Cache
|
|
15
|
+
from ..packages.platform_teensy import PlatformTeensy
|
|
16
|
+
from ..packages.toolchain_teensy import ToolchainTeensy
|
|
17
|
+
from ..packages.library_manager import LibraryManager, LibraryError
|
|
18
|
+
from ..config.board_config import BoardConfig
|
|
19
|
+
from ..cli_utils import BannerFormatter
|
|
20
|
+
from .configurable_compiler import ConfigurableCompiler
|
|
21
|
+
from .configurable_linker import ConfigurableLinker
|
|
22
|
+
from .linker import SizeInfo
|
|
23
|
+
from .orchestrator import IBuildOrchestrator, BuildResult
|
|
24
|
+
from .build_utils import safe_rmtree
|
|
25
|
+
from .build_state import BuildStateTracker
|
|
26
|
+
from .build_info_generator import BuildInfoGenerator
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class BuildResultTeensy:
|
|
31
|
+
"""Result of a Teensy build operation (internal use)."""
|
|
32
|
+
|
|
33
|
+
success: bool
|
|
34
|
+
firmware_hex: Optional[Path]
|
|
35
|
+
firmware_elf: Optional[Path]
|
|
36
|
+
size_info: Optional[SizeInfo]
|
|
37
|
+
build_time: float
|
|
38
|
+
message: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class OrchestratorTeensy(IBuildOrchestrator):
|
|
42
|
+
"""
|
|
43
|
+
Orchestrates Teensy-specific build process.
|
|
44
|
+
|
|
45
|
+
Handles platform initialization, toolchain setup, framework preparation,
|
|
46
|
+
and firmware generation for Teensy 4.x targets.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, cache: Cache, verbose: bool = False):
|
|
50
|
+
"""
|
|
51
|
+
Initialize Teensy orchestrator.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
cache: Cache instance for package management
|
|
55
|
+
verbose: Enable verbose output
|
|
56
|
+
"""
|
|
57
|
+
self.cache = cache
|
|
58
|
+
self.verbose = verbose
|
|
59
|
+
|
|
60
|
+
def build(
|
|
61
|
+
self,
|
|
62
|
+
project_dir: Path,
|
|
63
|
+
env_name: Optional[str] = None,
|
|
64
|
+
clean: bool = False,
|
|
65
|
+
verbose: Optional[bool] = None
|
|
66
|
+
) -> BuildResult:
|
|
67
|
+
"""Execute complete build process (IBuildOrchestrator interface).
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
project_dir: Project root directory containing platformio.ini
|
|
71
|
+
env_name: Environment name to build (defaults to first/default env)
|
|
72
|
+
clean: Clean build (remove all artifacts before building)
|
|
73
|
+
verbose: Override verbose setting
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
BuildResult with build status and output paths
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
BuildOrchestratorError: If build fails at any phase
|
|
80
|
+
"""
|
|
81
|
+
from ..config import PlatformIOConfig
|
|
82
|
+
|
|
83
|
+
verbose_mode = verbose if verbose is not None else self.verbose
|
|
84
|
+
|
|
85
|
+
# Parse platformio.ini to get environment configuration
|
|
86
|
+
ini_path = project_dir / "platformio.ini"
|
|
87
|
+
if not ini_path.exists():
|
|
88
|
+
return BuildResult(
|
|
89
|
+
success=False,
|
|
90
|
+
hex_path=None,
|
|
91
|
+
elf_path=None,
|
|
92
|
+
size_info=None,
|
|
93
|
+
build_time=0.0,
|
|
94
|
+
message=f"platformio.ini not found in {project_dir}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
config = PlatformIOConfig(ini_path)
|
|
99
|
+
|
|
100
|
+
# Determine environment to build
|
|
101
|
+
if env_name is None:
|
|
102
|
+
env_name = config.get_default_environment()
|
|
103
|
+
if env_name is None:
|
|
104
|
+
return BuildResult(
|
|
105
|
+
success=False,
|
|
106
|
+
hex_path=None,
|
|
107
|
+
elf_path=None,
|
|
108
|
+
size_info=None,
|
|
109
|
+
build_time=0.0,
|
|
110
|
+
message="No environment specified and no default found in platformio.ini"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
env_config = config.get_env_config(env_name)
|
|
114
|
+
board_id = env_config.get("board", "teensy41")
|
|
115
|
+
build_flags = config.get_build_flags(env_name)
|
|
116
|
+
lib_deps = config.get_lib_deps(env_name)
|
|
117
|
+
|
|
118
|
+
# Call internal build method
|
|
119
|
+
teensy_result = self._build_teensy(
|
|
120
|
+
project_dir, env_name, board_id, env_config, build_flags, lib_deps, clean, verbose_mode
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Convert BuildResultTeensy to BuildResult
|
|
124
|
+
return BuildResult(
|
|
125
|
+
success=teensy_result.success,
|
|
126
|
+
hex_path=teensy_result.firmware_hex,
|
|
127
|
+
elf_path=teensy_result.firmware_elf,
|
|
128
|
+
size_info=teensy_result.size_info,
|
|
129
|
+
build_time=teensy_result.build_time,
|
|
130
|
+
message=teensy_result.message
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
except KeyboardInterrupt:
|
|
134
|
+
_thread.interrupt_main()
|
|
135
|
+
raise
|
|
136
|
+
except Exception as e:
|
|
137
|
+
return BuildResult(
|
|
138
|
+
success=False,
|
|
139
|
+
hex_path=None,
|
|
140
|
+
elf_path=None,
|
|
141
|
+
size_info=None,
|
|
142
|
+
build_time=0.0,
|
|
143
|
+
message=f"Failed to parse configuration: {e}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def _build_teensy(
|
|
147
|
+
self,
|
|
148
|
+
project_dir: Path,
|
|
149
|
+
env_name: str,
|
|
150
|
+
board_id: str,
|
|
151
|
+
env_config: dict,
|
|
152
|
+
build_flags: List[str],
|
|
153
|
+
lib_deps: List[str],
|
|
154
|
+
clean: bool = False,
|
|
155
|
+
verbose: bool = False
|
|
156
|
+
) -> BuildResultTeensy:
|
|
157
|
+
"""
|
|
158
|
+
Execute complete Teensy build process (internal implementation).
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
project_dir: Project directory
|
|
162
|
+
env_name: Environment name
|
|
163
|
+
board_id: Board ID (e.g., teensy41)
|
|
164
|
+
env_config: Environment configuration dict
|
|
165
|
+
build_flags: User build flags from platformio.ini
|
|
166
|
+
lib_deps: Library dependencies from platformio.ini
|
|
167
|
+
clean: Whether to clean before build
|
|
168
|
+
verbose: Verbose output mode
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
BuildResultTeensy with build status and output paths
|
|
172
|
+
"""
|
|
173
|
+
start_time = time.time()
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
# Get board configuration
|
|
177
|
+
from ..config.board_config import BoardConfig
|
|
178
|
+
|
|
179
|
+
if verbose:
|
|
180
|
+
print("[2/7] Loading board configuration...")
|
|
181
|
+
|
|
182
|
+
board_config = BoardConfig.from_board_id(board_id)
|
|
183
|
+
|
|
184
|
+
# Initialize platform
|
|
185
|
+
if verbose:
|
|
186
|
+
print("[3/7] Initializing Teensy platform...")
|
|
187
|
+
|
|
188
|
+
platform = PlatformTeensy(
|
|
189
|
+
self.cache,
|
|
190
|
+
board_config.mcu,
|
|
191
|
+
show_progress=verbose
|
|
192
|
+
)
|
|
193
|
+
platform.ensure_package()
|
|
194
|
+
|
|
195
|
+
if verbose:
|
|
196
|
+
print(f" Board: {board_id}")
|
|
197
|
+
print(f" MCU: {board_config.mcu}")
|
|
198
|
+
print(f" CPU Frequency: {board_config.f_cpu}")
|
|
199
|
+
|
|
200
|
+
# Setup build directory
|
|
201
|
+
build_dir = self._setup_build_directory(env_name, clean, verbose)
|
|
202
|
+
|
|
203
|
+
# Check build state and invalidate cache if needed
|
|
204
|
+
if verbose:
|
|
205
|
+
print("[3.5/7] Checking build configuration state...")
|
|
206
|
+
|
|
207
|
+
state_tracker = BuildStateTracker(build_dir)
|
|
208
|
+
needs_rebuild, reasons, current_state = state_tracker.check_invalidation(
|
|
209
|
+
platformio_ini_path=project_dir / "platformio.ini",
|
|
210
|
+
platform="teensy",
|
|
211
|
+
board=board_id,
|
|
212
|
+
framework=env_config.get('framework', 'arduino'),
|
|
213
|
+
toolchain_version=platform.toolchain.version,
|
|
214
|
+
framework_version=platform.framework.version,
|
|
215
|
+
platform_version=f"teensy-{platform.framework.version}",
|
|
216
|
+
build_flags=build_flags,
|
|
217
|
+
lib_deps=lib_deps,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if needs_rebuild:
|
|
221
|
+
if verbose:
|
|
222
|
+
print(" Build cache invalidated:")
|
|
223
|
+
for reason in reasons:
|
|
224
|
+
print(f" - {reason}")
|
|
225
|
+
print(" Cleaning build artifacts...")
|
|
226
|
+
# Clean build artifacts to force rebuild
|
|
227
|
+
if build_dir.exists():
|
|
228
|
+
safe_rmtree(build_dir)
|
|
229
|
+
# Recreate build directory
|
|
230
|
+
build_dir.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
else:
|
|
232
|
+
if verbose:
|
|
233
|
+
print(" Build configuration unchanged, using cached artifacts")
|
|
234
|
+
|
|
235
|
+
# Initialize compiler
|
|
236
|
+
if verbose:
|
|
237
|
+
print("[4/7] Compiling Arduino core...")
|
|
238
|
+
|
|
239
|
+
compiler = ConfigurableCompiler(
|
|
240
|
+
platform,
|
|
241
|
+
platform.toolchain,
|
|
242
|
+
platform.framework,
|
|
243
|
+
board_id,
|
|
244
|
+
build_dir,
|
|
245
|
+
platform_config=None,
|
|
246
|
+
show_progress=verbose,
|
|
247
|
+
user_build_flags=build_flags
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Compile Arduino core
|
|
251
|
+
core_obj_files = compiler.compile_core()
|
|
252
|
+
core_archive = compiler.create_core_archive(core_obj_files)
|
|
253
|
+
|
|
254
|
+
if verbose:
|
|
255
|
+
print(f" Compiled {len(core_obj_files)} core source files")
|
|
256
|
+
|
|
257
|
+
# Handle library dependencies (if any)
|
|
258
|
+
library_archives, library_include_paths = self._process_libraries(
|
|
259
|
+
env_config, build_dir, compiler, platform.toolchain, board_config, verbose
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Add library include paths to compiler
|
|
263
|
+
if library_include_paths:
|
|
264
|
+
compiler.add_library_includes(library_include_paths)
|
|
265
|
+
|
|
266
|
+
# Find and compile sketch
|
|
267
|
+
sketch_obj_files = self._compile_sketch(project_dir, compiler, start_time, verbose)
|
|
268
|
+
if sketch_obj_files is None:
|
|
269
|
+
return self._error_result(
|
|
270
|
+
start_time,
|
|
271
|
+
f"No .ino sketch file found in {project_dir}"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Initialize linker
|
|
275
|
+
if verbose:
|
|
276
|
+
print("[6/7] Linking firmware...")
|
|
277
|
+
|
|
278
|
+
linker = ConfigurableLinker(
|
|
279
|
+
platform,
|
|
280
|
+
platform.toolchain,
|
|
281
|
+
platform.framework,
|
|
282
|
+
board_id,
|
|
283
|
+
build_dir,
|
|
284
|
+
platform_config=None,
|
|
285
|
+
show_progress=verbose
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Link firmware
|
|
289
|
+
firmware_elf = linker.link(sketch_obj_files, core_archive, library_archives=library_archives)
|
|
290
|
+
|
|
291
|
+
# Generate hex file
|
|
292
|
+
if verbose:
|
|
293
|
+
print("[7/7] Generating firmware hex...")
|
|
294
|
+
|
|
295
|
+
firmware_hex = linker.generate_hex(firmware_elf)
|
|
296
|
+
|
|
297
|
+
# Get size info
|
|
298
|
+
size_info = linker.get_size_info(firmware_elf)
|
|
299
|
+
|
|
300
|
+
build_time = time.time() - start_time
|
|
301
|
+
|
|
302
|
+
if verbose:
|
|
303
|
+
self._print_success(
|
|
304
|
+
build_time, firmware_elf, firmware_hex, size_info
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Save build state for future cache validation
|
|
308
|
+
if verbose:
|
|
309
|
+
print("[7.5/7] Saving build state...")
|
|
310
|
+
state_tracker.save_state(current_state)
|
|
311
|
+
|
|
312
|
+
# Generate build_info.json
|
|
313
|
+
build_info_generator = BuildInfoGenerator(build_dir)
|
|
314
|
+
# Parse f_cpu from string (e.g., "600000000L") to int
|
|
315
|
+
f_cpu_int = int(board_config.f_cpu.rstrip("L"))
|
|
316
|
+
# Build toolchain_paths dict, filtering out None values
|
|
317
|
+
toolchain_paths_raw = {
|
|
318
|
+
"gcc": platform.toolchain.get_gcc_path(),
|
|
319
|
+
"gxx": platform.toolchain.get_gxx_path(),
|
|
320
|
+
"ar": platform.toolchain.get_ar_path(),
|
|
321
|
+
"objcopy": platform.toolchain.get_objcopy_path(),
|
|
322
|
+
"size": platform.toolchain.get_size_path(),
|
|
323
|
+
}
|
|
324
|
+
toolchain_paths = {k: v for k, v in toolchain_paths_raw.items() if v is not None}
|
|
325
|
+
build_info = build_info_generator.generate_generic(
|
|
326
|
+
env_name=env_name,
|
|
327
|
+
board_id=board_id,
|
|
328
|
+
board_name=board_config.name,
|
|
329
|
+
mcu=board_config.mcu,
|
|
330
|
+
platform="teensy",
|
|
331
|
+
f_cpu=f_cpu_int,
|
|
332
|
+
build_time=build_time,
|
|
333
|
+
elf_path=firmware_elf,
|
|
334
|
+
hex_path=firmware_hex,
|
|
335
|
+
size_info=size_info,
|
|
336
|
+
build_flags=build_flags,
|
|
337
|
+
lib_deps=lib_deps,
|
|
338
|
+
toolchain_version=platform.toolchain.version,
|
|
339
|
+
toolchain_paths=toolchain_paths,
|
|
340
|
+
framework_name="arduino",
|
|
341
|
+
framework_version=platform.framework.version,
|
|
342
|
+
core_path=platform.framework.get_cores_dir(),
|
|
343
|
+
)
|
|
344
|
+
build_info_generator.save(build_info)
|
|
345
|
+
if verbose:
|
|
346
|
+
print(f" Build info saved to {build_info_generator.build_info_path}")
|
|
347
|
+
|
|
348
|
+
return BuildResultTeensy(
|
|
349
|
+
success=True,
|
|
350
|
+
firmware_hex=firmware_hex,
|
|
351
|
+
firmware_elf=firmware_elf,
|
|
352
|
+
size_info=size_info,
|
|
353
|
+
build_time=build_time,
|
|
354
|
+
message="Build successful (native Teensy build)"
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
except KeyboardInterrupt as ke:
|
|
358
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
359
|
+
handle_keyboard_interrupt_properly(ke)
|
|
360
|
+
raise # Never reached, but satisfies type checker
|
|
361
|
+
except Exception as e:
|
|
362
|
+
build_time = time.time() - start_time
|
|
363
|
+
import traceback
|
|
364
|
+
error_trace = traceback.format_exc()
|
|
365
|
+
return BuildResultTeensy(
|
|
366
|
+
success=False,
|
|
367
|
+
firmware_hex=None,
|
|
368
|
+
firmware_elf=None,
|
|
369
|
+
size_info=None,
|
|
370
|
+
build_time=build_time,
|
|
371
|
+
message=f"Teensy native build failed: {e}\n\n{error_trace}"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
def _setup_build_directory(self, env_name: str, clean: bool, verbose: bool) -> Path:
|
|
375
|
+
"""
|
|
376
|
+
Setup build directory with optional cleaning.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
env_name: Environment name
|
|
380
|
+
clean: Whether to clean before build
|
|
381
|
+
verbose: Verbose output mode
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Build directory path
|
|
385
|
+
"""
|
|
386
|
+
build_dir = self.cache.get_build_dir(env_name)
|
|
387
|
+
|
|
388
|
+
if clean and build_dir.exists():
|
|
389
|
+
if verbose:
|
|
390
|
+
print("[1/7] Cleaning build directory...")
|
|
391
|
+
safe_rmtree(build_dir)
|
|
392
|
+
|
|
393
|
+
build_dir.mkdir(parents=True, exist_ok=True)
|
|
394
|
+
return build_dir
|
|
395
|
+
|
|
396
|
+
def _process_libraries(
|
|
397
|
+
self,
|
|
398
|
+
env_config: dict,
|
|
399
|
+
build_dir: Path,
|
|
400
|
+
compiler: ConfigurableCompiler,
|
|
401
|
+
toolchain: ToolchainTeensy,
|
|
402
|
+
board_config: BoardConfig,
|
|
403
|
+
verbose: bool
|
|
404
|
+
) -> tuple[List[Path], List[Path]]:
|
|
405
|
+
"""
|
|
406
|
+
Process and compile library dependencies.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
env_config: Environment configuration
|
|
410
|
+
build_dir: Build directory
|
|
411
|
+
compiler: Configured compiler instance
|
|
412
|
+
toolchain: Teensy toolchain instance
|
|
413
|
+
board_config: Board configuration instance
|
|
414
|
+
verbose: Verbose output mode
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
Tuple of (library_archives, library_include_paths)
|
|
418
|
+
"""
|
|
419
|
+
lib_deps = env_config.get('lib_deps', '')
|
|
420
|
+
library_archives = []
|
|
421
|
+
library_include_paths = []
|
|
422
|
+
|
|
423
|
+
if not lib_deps:
|
|
424
|
+
return library_archives, library_include_paths
|
|
425
|
+
|
|
426
|
+
if verbose:
|
|
427
|
+
print("[4.5/7] Processing library dependencies...")
|
|
428
|
+
|
|
429
|
+
# Parse lib_deps (can be string or list)
|
|
430
|
+
if isinstance(lib_deps, str):
|
|
431
|
+
lib_specs = [dep.strip() for dep in lib_deps.split('\n') if dep.strip()]
|
|
432
|
+
else:
|
|
433
|
+
lib_specs = lib_deps
|
|
434
|
+
|
|
435
|
+
if not lib_specs:
|
|
436
|
+
return library_archives, library_include_paths
|
|
437
|
+
|
|
438
|
+
try:
|
|
439
|
+
# Initialize library manager
|
|
440
|
+
library_manager = LibraryManager(build_dir, mode="release")
|
|
441
|
+
|
|
442
|
+
# Prepare compilation parameters
|
|
443
|
+
lib_defines = []
|
|
444
|
+
defines_dict = board_config.get_defines()
|
|
445
|
+
for key, value in defines_dict.items():
|
|
446
|
+
if value:
|
|
447
|
+
lib_defines.append(f"{key}={value}")
|
|
448
|
+
else:
|
|
449
|
+
lib_defines.append(key)
|
|
450
|
+
|
|
451
|
+
# Get include paths from compiler configuration
|
|
452
|
+
lib_includes = compiler.get_include_paths()
|
|
453
|
+
|
|
454
|
+
# Get compiler path from toolchain (use C++ compiler for libraries)
|
|
455
|
+
compiler_path = toolchain.get_gxx_path()
|
|
456
|
+
if compiler_path is None:
|
|
457
|
+
raise LibraryError("C++ compiler not found in toolchain")
|
|
458
|
+
|
|
459
|
+
if verbose:
|
|
460
|
+
print(f" Found {len(lib_specs)} library dependencies")
|
|
461
|
+
print(f" Compiler path: {compiler_path}")
|
|
462
|
+
|
|
463
|
+
# Ensure all libraries are downloaded and compiled
|
|
464
|
+
libraries = library_manager.ensure_libraries(
|
|
465
|
+
lib_deps=lib_specs,
|
|
466
|
+
compiler_path=compiler_path,
|
|
467
|
+
mcu=board_config.mcu,
|
|
468
|
+
f_cpu=board_config.f_cpu,
|
|
469
|
+
defines=lib_defines,
|
|
470
|
+
include_paths=lib_includes,
|
|
471
|
+
extra_flags=[],
|
|
472
|
+
show_progress=verbose
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
# Get library artifacts
|
|
476
|
+
library_include_paths = library_manager.get_library_include_paths()
|
|
477
|
+
library_archives = library_manager.get_library_objects()
|
|
478
|
+
|
|
479
|
+
if verbose:
|
|
480
|
+
print(f" Compiled {len(libraries)} libraries")
|
|
481
|
+
print(f" Library objects: {len(library_archives)}")
|
|
482
|
+
|
|
483
|
+
except LibraryError as e:
|
|
484
|
+
print(f" Error processing libraries: {e}")
|
|
485
|
+
# Continue build without libraries
|
|
486
|
+
library_archives = []
|
|
487
|
+
library_include_paths = []
|
|
488
|
+
|
|
489
|
+
return library_archives, library_include_paths
|
|
490
|
+
|
|
491
|
+
def _compile_sketch(
|
|
492
|
+
self,
|
|
493
|
+
project_dir: Path,
|
|
494
|
+
compiler: ConfigurableCompiler,
|
|
495
|
+
start_time: float,
|
|
496
|
+
verbose: bool
|
|
497
|
+
) -> Optional[List[Path]]:
|
|
498
|
+
"""
|
|
499
|
+
Find and compile sketch files.
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
project_dir: Project directory
|
|
503
|
+
compiler: Configured compiler instance
|
|
504
|
+
start_time: Build start time for error reporting
|
|
505
|
+
verbose: Verbose output mode
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
List of compiled object files or None if no sketch found
|
|
509
|
+
"""
|
|
510
|
+
if verbose:
|
|
511
|
+
print("[5/7] Compiling sketch...")
|
|
512
|
+
|
|
513
|
+
# Look for .ino files in the project directory
|
|
514
|
+
sketch_files = list(project_dir.glob("*.ino"))
|
|
515
|
+
if not sketch_files:
|
|
516
|
+
# Also check src/ directory
|
|
517
|
+
src_dir = project_dir / "src"
|
|
518
|
+
if src_dir.exists():
|
|
519
|
+
sketch_files = list(src_dir.glob("*.ino"))
|
|
520
|
+
|
|
521
|
+
if not sketch_files:
|
|
522
|
+
return None
|
|
523
|
+
|
|
524
|
+
sketch_path = sketch_files[0]
|
|
525
|
+
sketch_obj_files = compiler.compile_sketch(sketch_path)
|
|
526
|
+
|
|
527
|
+
if verbose:
|
|
528
|
+
print(f" Compiled {len(sketch_obj_files)} sketch file(s)")
|
|
529
|
+
|
|
530
|
+
return sketch_obj_files
|
|
531
|
+
|
|
532
|
+
def _print_success(
|
|
533
|
+
self,
|
|
534
|
+
build_time: float,
|
|
535
|
+
firmware_elf: Path,
|
|
536
|
+
firmware_hex: Path,
|
|
537
|
+
size_info: Optional[SizeInfo]
|
|
538
|
+
) -> None:
|
|
539
|
+
"""
|
|
540
|
+
Print build success message.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
build_time: Total build time
|
|
544
|
+
firmware_elf: Path to firmware ELF
|
|
545
|
+
firmware_hex: Path to firmware hex
|
|
546
|
+
size_info: Size information
|
|
547
|
+
"""
|
|
548
|
+
# Build success message
|
|
549
|
+
message_lines = ["BUILD SUCCESSFUL!"]
|
|
550
|
+
message_lines.append(f"Build time: {build_time:.2f}s")
|
|
551
|
+
message_lines.append(f"Firmware ELF: {firmware_elf}")
|
|
552
|
+
message_lines.append(f"Firmware HEX: {firmware_hex}")
|
|
553
|
+
|
|
554
|
+
if size_info:
|
|
555
|
+
message_lines.append(
|
|
556
|
+
f"Program size: {size_info.text + size_info.data} bytes"
|
|
557
|
+
)
|
|
558
|
+
message_lines.append(f"Data size: {size_info.bss + size_info.data} bytes")
|
|
559
|
+
|
|
560
|
+
BannerFormatter.print_banner("\n".join(message_lines), width=60, center=False)
|
|
561
|
+
|
|
562
|
+
def _error_result(self, start_time: float, message: str) -> BuildResultTeensy:
|
|
563
|
+
"""
|
|
564
|
+
Create an error result.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
start_time: Build start time
|
|
568
|
+
message: Error message
|
|
569
|
+
|
|
570
|
+
Returns:
|
|
571
|
+
BuildResultTeensy indicating failure
|
|
572
|
+
"""
|
|
573
|
+
return BuildResultTeensy(
|
|
574
|
+
success=False,
|
|
575
|
+
firmware_hex=None,
|
|
576
|
+
firmware_elf=None,
|
|
577
|
+
size_info=None,
|
|
578
|
+
build_time=time.time() - start_time,
|
|
579
|
+
message=message
|
|
580
|
+
)
|