fbuild 1.1.0__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.
Potentially problematic release.
This version of fbuild might be problematic. Click here for more details.
- fbuild/__init__.py +0 -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_state.py +325 -0
- fbuild/build/build_utils.py +98 -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 +612 -0
- fbuild/build/configurable_linker.py +637 -0
- fbuild/build/flag_builder.py +186 -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 +656 -0
- fbuild/build/orchestrator_esp32.py +797 -0
- fbuild/build/orchestrator_teensy.py +543 -0
- fbuild/build/source_compilation_orchestrator.py +220 -0
- fbuild/build/source_scanner.py +516 -0
- fbuild/cli.py +566 -0
- fbuild/cli_utils.py +312 -0
- fbuild/config/__init__.py +16 -0
- fbuild/config/board_config.py +457 -0
- fbuild/config/board_loader.py +92 -0
- fbuild/config/ini_parser.py +209 -0
- fbuild/config/mcu_specs.py +88 -0
- fbuild/daemon/__init__.py +34 -0
- fbuild/daemon/client.py +929 -0
- fbuild/daemon/compilation_queue.py +293 -0
- fbuild/daemon/daemon.py +474 -0
- fbuild/daemon/daemon_context.py +196 -0
- fbuild/daemon/error_collector.py +263 -0
- fbuild/daemon/file_cache.py +332 -0
- fbuild/daemon/lock_manager.py +270 -0
- fbuild/daemon/logging_utils.py +149 -0
- fbuild/daemon/messages.py +301 -0
- fbuild/daemon/operation_registry.py +288 -0
- fbuild/daemon/process_tracker.py +366 -0
- fbuild/daemon/processors/__init__.py +12 -0
- fbuild/daemon/processors/build_processor.py +157 -0
- fbuild/daemon/processors/deploy_processor.py +327 -0
- fbuild/daemon/processors/monitor_processor.py +146 -0
- fbuild/daemon/request_processor.py +401 -0
- fbuild/daemon/status_manager.py +216 -0
- fbuild/daemon/subprocess_manager.py +316 -0
- fbuild/deploy/__init__.py +17 -0
- fbuild/deploy/deployer.py +67 -0
- fbuild/deploy/deployer_esp32.py +314 -0
- fbuild/deploy/monitor.py +495 -0
- fbuild/interrupt_utils.py +34 -0
- fbuild/packages/__init__.py +53 -0
- fbuild/packages/archive_utils.py +1098 -0
- fbuild/packages/arduino_core.py +412 -0
- fbuild/packages/cache.py +249 -0
- fbuild/packages/downloader.py +366 -0
- fbuild/packages/framework_esp32.py +538 -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 +413 -0
- fbuild/packages/package.py +163 -0
- fbuild/packages/platform_esp32.py +383 -0
- fbuild/packages/platform_teensy.py +312 -0
- fbuild/packages/platform_utils.py +131 -0
- fbuild/packages/platformio_registry.py +325 -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 +484 -0
- fbuild/packages/toolchain_metadata.py +185 -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-1.1.0.dist-info/METADATA +447 -0
- fbuild-1.1.0.dist-info/RECORD +93 -0
- fbuild-1.1.0.dist-info/WHEEL +5 -0
- fbuild-1.1.0.dist-info/entry_points.txt +5 -0
- fbuild-1.1.0.dist-info/licenses/LICENSE +21 -0
- fbuild-1.1.0.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,383 @@
|
|
|
1
|
+
"""ESP32 Platform Package Management.
|
|
2
|
+
|
|
3
|
+
This module handles downloading, extracting, and managing ESP32 platform packages
|
|
4
|
+
from GitHub releases. It provides access to the Arduino-ESP32 core, toolchains,
|
|
5
|
+
and platform-specific tools needed for ESP32 builds.
|
|
6
|
+
|
|
7
|
+
Platform Structure (after extraction):
|
|
8
|
+
platform-espressif32/
|
|
9
|
+
├── platform.json # Package metadata with download URLs
|
|
10
|
+
├── boards/ # Board definitions (JSON files)
|
|
11
|
+
│ └── esp32-c6-devkitm-1.json
|
|
12
|
+
├── builder/ # PlatformIO build scripts
|
|
13
|
+
│ └── frameworks/
|
|
14
|
+
│ └── arduino.py
|
|
15
|
+
└── ... # Other platform files
|
|
16
|
+
|
|
17
|
+
Key Packages (from platform.json):
|
|
18
|
+
- framework-arduinoespressif32: Arduino core (cores/, variants/)
|
|
19
|
+
- framework-arduinoespressif32-libs: Pre-built ESP-IDF libraries
|
|
20
|
+
- toolchain-riscv32-esp: RISC-V GCC (for C3, C6, H2)
|
|
21
|
+
- toolchain-xtensa-esp-elf: Xtensa GCC (for ESP32, S2, S3)
|
|
22
|
+
- tool-esptoolpy: Upload tool
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Dict, Optional
|
|
28
|
+
|
|
29
|
+
from .cache import Cache
|
|
30
|
+
from .downloader import DownloadError, ExtractionError, PackageDownloader
|
|
31
|
+
from .package import IPackage, PackageError
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PlatformErrorESP32(PackageError):
|
|
35
|
+
"""Raised when ESP32 platform operations fail."""
|
|
36
|
+
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class PlatformESP32(IPackage):
|
|
41
|
+
"""Manages ESP32 platform package download, extraction, and access.
|
|
42
|
+
|
|
43
|
+
This class handles the pioarduino/platform-espressif32 package which contains:
|
|
44
|
+
- Arduino core for ESP32 family (C3, C6, S2, S3, H2, etc.)
|
|
45
|
+
- Toolchains (riscv32-esp-elf-gcc, xtensa-esp-elf-gcc)
|
|
46
|
+
- Platform tools (esptool, mkspiffs, etc.)
|
|
47
|
+
- Board definitions and variants
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, cache: Cache, platform_url: str, show_progress: bool = True):
|
|
51
|
+
"""Initialize ESP32 platform manager.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
cache: Cache manager instance
|
|
55
|
+
platform_url: URL to platform package (e.g., GitHub release ZIP)
|
|
56
|
+
show_progress: Whether to show download/extraction progress
|
|
57
|
+
"""
|
|
58
|
+
self.cache = cache
|
|
59
|
+
self.platform_url = platform_url
|
|
60
|
+
self.show_progress = show_progress
|
|
61
|
+
self.downloader = PackageDownloader()
|
|
62
|
+
|
|
63
|
+
# Extract version from URL (e.g., "55.03.34" from release tag)
|
|
64
|
+
self.version = self._extract_version_from_url(platform_url)
|
|
65
|
+
|
|
66
|
+
# Get platform path from cache
|
|
67
|
+
self.platform_path = cache.get_platform_path(platform_url, self.version)
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def _extract_version_from_url(url: str) -> str:
|
|
71
|
+
"""Extract version string from platform URL.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
url: Platform URL (e.g., https://github.com/.../55.03.34/platform.zip)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Version string (e.g., "55.03.34")
|
|
78
|
+
"""
|
|
79
|
+
# URL format: .../releases/download/{version}/platform-espressif32.zip
|
|
80
|
+
parts = url.split("/")
|
|
81
|
+
for i, part in enumerate(parts):
|
|
82
|
+
if part == "download" and i + 1 < len(parts):
|
|
83
|
+
return parts[i + 1]
|
|
84
|
+
|
|
85
|
+
# Fallback: use URL hash if version extraction fails
|
|
86
|
+
from .cache import Cache
|
|
87
|
+
|
|
88
|
+
return Cache.hash_url(url)[:8]
|
|
89
|
+
|
|
90
|
+
def ensure_package(self) -> Path:
|
|
91
|
+
"""Ensure platform is downloaded and extracted.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Path to the extracted platform directory
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
PlatformErrorESP32: If download or extraction fails
|
|
98
|
+
"""
|
|
99
|
+
return self.ensure_platform()
|
|
100
|
+
|
|
101
|
+
def ensure_platform(self) -> Path:
|
|
102
|
+
"""Ensure platform is downloaded and extracted.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Path to the extracted platform directory
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
PlatformErrorESP32: If download or extraction fails
|
|
109
|
+
"""
|
|
110
|
+
if self.is_installed():
|
|
111
|
+
if self.show_progress:
|
|
112
|
+
print(f"Using cached ESP32 platform {self.version}")
|
|
113
|
+
return self.platform_path
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
if self.show_progress:
|
|
117
|
+
print(f"Downloading ESP32 platform {self.version}...")
|
|
118
|
+
|
|
119
|
+
# Download and extract platform package
|
|
120
|
+
self.cache.ensure_directories()
|
|
121
|
+
|
|
122
|
+
# Use downloader to handle download and extraction
|
|
123
|
+
archive_name = Path(self.platform_url).name
|
|
124
|
+
archive_path = self.platform_path.parent / archive_name
|
|
125
|
+
|
|
126
|
+
# Download if not cached
|
|
127
|
+
if not archive_path.exists():
|
|
128
|
+
archive_path.parent.mkdir(parents=True, exist_ok=True)
|
|
129
|
+
self.downloader.download(self.platform_url, archive_path, show_progress=self.show_progress)
|
|
130
|
+
else:
|
|
131
|
+
if self.show_progress:
|
|
132
|
+
print(f"Using cached archive {archive_name}")
|
|
133
|
+
|
|
134
|
+
# Extract to platform directory
|
|
135
|
+
if self.show_progress:
|
|
136
|
+
print(f"Extracting platform to {self.platform_path}...")
|
|
137
|
+
|
|
138
|
+
# Create temp extraction directory
|
|
139
|
+
temp_extract = self.platform_path.parent / "temp_extract"
|
|
140
|
+
temp_extract.mkdir(parents=True, exist_ok=True)
|
|
141
|
+
|
|
142
|
+
self.downloader.extract_archive(archive_path, temp_extract, show_progress=self.show_progress)
|
|
143
|
+
|
|
144
|
+
# Find the platform directory in the extracted content
|
|
145
|
+
# Usually it's a subdirectory like "platform-espressif32/"
|
|
146
|
+
extracted_dirs = list(temp_extract.glob("platform-*"))
|
|
147
|
+
if not extracted_dirs:
|
|
148
|
+
# Maybe it extracted directly
|
|
149
|
+
extracted_dirs = [temp_extract]
|
|
150
|
+
|
|
151
|
+
source_dir = extracted_dirs[0]
|
|
152
|
+
|
|
153
|
+
# Move to final location
|
|
154
|
+
if self.platform_path.exists():
|
|
155
|
+
import shutil
|
|
156
|
+
|
|
157
|
+
shutil.rmtree(self.platform_path)
|
|
158
|
+
|
|
159
|
+
source_dir.rename(self.platform_path)
|
|
160
|
+
|
|
161
|
+
# Clean up temp directory
|
|
162
|
+
if temp_extract.exists() and temp_extract != self.platform_path:
|
|
163
|
+
import shutil
|
|
164
|
+
|
|
165
|
+
shutil.rmtree(temp_extract, ignore_errors=True)
|
|
166
|
+
|
|
167
|
+
if self.show_progress:
|
|
168
|
+
print(f"ESP32 platform installed to {self.platform_path}")
|
|
169
|
+
|
|
170
|
+
return self.platform_path
|
|
171
|
+
|
|
172
|
+
except (DownloadError, ExtractionError) as e:
|
|
173
|
+
raise PlatformErrorESP32(f"Failed to install ESP32 platform: {e}")
|
|
174
|
+
except KeyboardInterrupt as ke:
|
|
175
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
176
|
+
|
|
177
|
+
handle_keyboard_interrupt_properly(ke)
|
|
178
|
+
raise # Never reached, but satisfies type checker
|
|
179
|
+
except Exception as e:
|
|
180
|
+
raise PlatformErrorESP32(f"Unexpected error installing platform: {e}")
|
|
181
|
+
|
|
182
|
+
def is_installed(self) -> bool:
|
|
183
|
+
"""Check if platform is already installed.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
True if platform directory exists with key files
|
|
187
|
+
"""
|
|
188
|
+
if not self.platform_path.exists():
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
# Verify essential platform files exist
|
|
192
|
+
required_files = [
|
|
193
|
+
self.platform_path / "platform.json",
|
|
194
|
+
self.platform_path / "boards",
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
return all(f.exists() for f in required_files)
|
|
198
|
+
|
|
199
|
+
def get_platform_json(self) -> Dict[str, Any]:
|
|
200
|
+
"""Load and parse platform.json metadata.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Dictionary containing platform metadata
|
|
204
|
+
|
|
205
|
+
Raises:
|
|
206
|
+
PlatformErrorESP32: If platform.json doesn't exist or is invalid
|
|
207
|
+
"""
|
|
208
|
+
platform_json_path = self.platform_path / "platform.json"
|
|
209
|
+
|
|
210
|
+
if not platform_json_path.exists():
|
|
211
|
+
raise PlatformErrorESP32(f"platform.json not found at {platform_json_path}. " + "Ensure platform is downloaded first.")
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
with open(platform_json_path, "r") as f:
|
|
215
|
+
return json.load(f)
|
|
216
|
+
except json.JSONDecodeError as e:
|
|
217
|
+
raise PlatformErrorESP32(f"Failed to parse platform.json: {e}")
|
|
218
|
+
except KeyboardInterrupt as ke:
|
|
219
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
220
|
+
|
|
221
|
+
handle_keyboard_interrupt_properly(ke)
|
|
222
|
+
raise # Never reached, but satisfies type checker
|
|
223
|
+
except Exception as e:
|
|
224
|
+
raise PlatformErrorESP32(f"Failed to read platform.json: {e}")
|
|
225
|
+
|
|
226
|
+
def get_package_url(self, package_name: str) -> Optional[str]:
|
|
227
|
+
"""Get download URL for a specific package.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
package_name: Name of the package (e.g., "toolchain-riscv32-esp")
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
URL string or None if package not found
|
|
234
|
+
"""
|
|
235
|
+
platform_json = self.get_platform_json()
|
|
236
|
+
packages = platform_json.get("packages", {})
|
|
237
|
+
|
|
238
|
+
if package_name not in packages:
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
package_info = packages[package_name]
|
|
242
|
+
return package_info.get("version") # "version" field contains URL
|
|
243
|
+
|
|
244
|
+
def get_required_packages(self, board_mcu: str) -> Dict[str, str]:
|
|
245
|
+
"""Get required packages for a specific MCU.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
board_mcu: MCU type (e.g., "esp32c6", "esp32s3", "esp32")
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Dictionary of package_name -> url for required packages
|
|
252
|
+
"""
|
|
253
|
+
packages = {}
|
|
254
|
+
|
|
255
|
+
# All ESP32 boards need the Arduino framework
|
|
256
|
+
framework_url = self.get_package_url("framework-arduinoespressif32")
|
|
257
|
+
if framework_url:
|
|
258
|
+
packages["framework-arduinoespressif32"] = framework_url
|
|
259
|
+
|
|
260
|
+
libs_url = self.get_package_url("framework-arduinoespressif32-libs")
|
|
261
|
+
if libs_url:
|
|
262
|
+
packages["framework-arduinoespressif32-libs"] = libs_url
|
|
263
|
+
|
|
264
|
+
# Check for MCU-specific skeleton libraries
|
|
265
|
+
# These are used when the main libs package is empty or incomplete
|
|
266
|
+
# The naming pattern is: framework-arduino-{mcu_suffix}-skeleton-lib
|
|
267
|
+
# where mcu_suffix is extracted from the MCU name (e.g., "esp32c2" -> "c2")
|
|
268
|
+
mcu_suffix = board_mcu.replace("esp32", "")
|
|
269
|
+
skeleton_lib_name = f"framework-arduino-{mcu_suffix}-skeleton-lib"
|
|
270
|
+
skeleton_lib_url = self.get_package_url(skeleton_lib_name)
|
|
271
|
+
if skeleton_lib_url:
|
|
272
|
+
packages[skeleton_lib_name] = skeleton_lib_url
|
|
273
|
+
|
|
274
|
+
# Determine which toolchain is needed based on MCU architecture
|
|
275
|
+
if board_mcu in [
|
|
276
|
+
"esp32c3",
|
|
277
|
+
"esp32c6",
|
|
278
|
+
"esp32h2",
|
|
279
|
+
"esp32c2",
|
|
280
|
+
"esp32c5",
|
|
281
|
+
"esp32p4",
|
|
282
|
+
]:
|
|
283
|
+
# RISC-V based ESP32s
|
|
284
|
+
toolchain_url = self.get_package_url("toolchain-riscv32-esp")
|
|
285
|
+
if toolchain_url:
|
|
286
|
+
packages["toolchain-riscv32-esp"] = toolchain_url
|
|
287
|
+
else:
|
|
288
|
+
# Xtensa based ESP32s (original ESP32, S2, S3)
|
|
289
|
+
toolchain_url = self.get_package_url("toolchain-xtensa-esp-elf")
|
|
290
|
+
if toolchain_url:
|
|
291
|
+
packages["toolchain-xtensa-esp-elf"] = toolchain_url
|
|
292
|
+
|
|
293
|
+
# Add esptool (needed for all ESP32 boards)
|
|
294
|
+
esptool_url = self.get_package_url("tool-esptoolpy")
|
|
295
|
+
if esptool_url:
|
|
296
|
+
packages["tool-esptoolpy"] = esptool_url
|
|
297
|
+
|
|
298
|
+
return packages
|
|
299
|
+
|
|
300
|
+
def get_boards_dir(self) -> Path:
|
|
301
|
+
"""Get path to boards directory.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Path to boards directory containing JSON board definitions
|
|
305
|
+
"""
|
|
306
|
+
return self.platform_path / "boards"
|
|
307
|
+
|
|
308
|
+
def get_board_json(self, board_id: str) -> Dict[str, Any]:
|
|
309
|
+
"""Load board configuration from JSON.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
board_id: Board identifier (e.g., "esp32-c6-devkitm-1")
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Dictionary containing board configuration
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
PlatformErrorESP32: If board JSON doesn't exist or is invalid
|
|
319
|
+
"""
|
|
320
|
+
board_json_path = self.get_boards_dir() / f"{board_id}.json"
|
|
321
|
+
|
|
322
|
+
if not board_json_path.exists():
|
|
323
|
+
raise PlatformErrorESP32(f"Board definition not found: {board_id} " + f"at {board_json_path}")
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
with open(board_json_path, "r") as f:
|
|
327
|
+
return json.load(f)
|
|
328
|
+
except json.JSONDecodeError as e:
|
|
329
|
+
raise PlatformErrorESP32(f"Failed to parse board JSON: {e}")
|
|
330
|
+
except KeyboardInterrupt as ke:
|
|
331
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
332
|
+
|
|
333
|
+
handle_keyboard_interrupt_properly(ke)
|
|
334
|
+
raise # Never reached, but satisfies type checker
|
|
335
|
+
except Exception as e:
|
|
336
|
+
raise PlatformErrorESP32(f"Failed to read board JSON: {e}")
|
|
337
|
+
|
|
338
|
+
def list_boards(self) -> list[str]:
|
|
339
|
+
"""List all available board IDs.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
List of board identifiers
|
|
343
|
+
"""
|
|
344
|
+
boards_dir = self.get_boards_dir()
|
|
345
|
+
if not boards_dir.exists():
|
|
346
|
+
return []
|
|
347
|
+
|
|
348
|
+
return [f.stem for f in boards_dir.glob("*.json") if f.is_file() and not f.name.endswith(".py")]
|
|
349
|
+
|
|
350
|
+
def get_package_info(self) -> Dict[str, Any]:
|
|
351
|
+
"""Get information about the installed platform.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Dictionary with platform information
|
|
355
|
+
"""
|
|
356
|
+
return self.get_platform_info()
|
|
357
|
+
|
|
358
|
+
def get_platform_info(self) -> Dict[str, Any]:
|
|
359
|
+
"""Get information about the installed platform.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Dictionary with platform information
|
|
363
|
+
"""
|
|
364
|
+
info = {
|
|
365
|
+
"version": self.version,
|
|
366
|
+
"path": str(self.platform_path),
|
|
367
|
+
"url": self.platform_url,
|
|
368
|
+
"installed": self.is_installed(),
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if self.is_installed():
|
|
372
|
+
info["boards_dir"] = str(self.get_boards_dir())
|
|
373
|
+
info["available_boards"] = len(self.list_boards())
|
|
374
|
+
|
|
375
|
+
# Get package information
|
|
376
|
+
try:
|
|
377
|
+
platform_json = self.get_platform_json()
|
|
378
|
+
info["platform_version"] = platform_json.get("version")
|
|
379
|
+
info["available_packages"] = list(platform_json.get("packages", {}).keys())
|
|
380
|
+
except PlatformErrorESP32:
|
|
381
|
+
pass
|
|
382
|
+
|
|
383
|
+
return info
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Teensy Platform Package Management.
|
|
2
|
+
|
|
3
|
+
This module coordinates Teensy platform components including toolchain and framework.
|
|
4
|
+
It provides a unified interface for managing Teensy 4.x platform builds.
|
|
5
|
+
|
|
6
|
+
Platform Components:
|
|
7
|
+
- ARM GCC Toolchain (arm-none-eabi-gcc)
|
|
8
|
+
- Teensy Cores Framework (Arduino core for Teensy 4.x)
|
|
9
|
+
|
|
10
|
+
Supported Boards:
|
|
11
|
+
- Teensy 4.1 (NXP i.MX RT1062, ARM Cortex-M7 @ 600MHz)
|
|
12
|
+
- Teensy 4.0 (NXP i.MX RT1062, ARM Cortex-M7 @ 600MHz)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List
|
|
17
|
+
|
|
18
|
+
from .cache import Cache
|
|
19
|
+
from .framework_teensy import FrameworkErrorTeensy, FrameworkTeensy
|
|
20
|
+
from .package import IPackage, PackageError
|
|
21
|
+
from .toolchain_teensy import ToolchainErrorTeensy, ToolchainTeensy
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PlatformErrorTeensy(PackageError):
|
|
25
|
+
"""Raised when Teensy platform operations fail."""
|
|
26
|
+
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PlatformTeensy(IPackage):
|
|
31
|
+
"""Manages Teensy platform components and configuration.
|
|
32
|
+
|
|
33
|
+
This class coordinates the Teensy toolchain and framework to provide
|
|
34
|
+
a complete build environment for Teensy 4.x boards.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, cache: Cache, board_mcu: str, show_progress: bool = True):
|
|
38
|
+
"""Initialize Teensy platform manager.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
cache: Cache manager instance
|
|
42
|
+
board_mcu: MCU type (e.g., "imxrt1062")
|
|
43
|
+
show_progress: Whether to show download/extraction progress
|
|
44
|
+
"""
|
|
45
|
+
self.cache = cache
|
|
46
|
+
self.board_mcu = board_mcu
|
|
47
|
+
self.show_progress = show_progress
|
|
48
|
+
|
|
49
|
+
# Initialize toolchain and framework
|
|
50
|
+
self.toolchain = ToolchainTeensy(cache, show_progress=show_progress)
|
|
51
|
+
self.framework = FrameworkTeensy(cache, show_progress=show_progress)
|
|
52
|
+
|
|
53
|
+
def ensure_package(self) -> Path:
|
|
54
|
+
"""Ensure platform components are downloaded and extracted.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Path to the framework directory (main platform directory)
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
PlatformErrorTeensy: If download or extraction fails
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
# Ensure toolchain is installed
|
|
64
|
+
self.toolchain.ensure_toolchain()
|
|
65
|
+
|
|
66
|
+
# Ensure framework is installed
|
|
67
|
+
framework_path = self.framework.ensure_framework()
|
|
68
|
+
|
|
69
|
+
return framework_path
|
|
70
|
+
|
|
71
|
+
except (ToolchainErrorTeensy, FrameworkErrorTeensy) as e:
|
|
72
|
+
raise PlatformErrorTeensy(f"Failed to install Teensy platform: {e}")
|
|
73
|
+
except KeyboardInterrupt as ke:
|
|
74
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
75
|
+
|
|
76
|
+
handle_keyboard_interrupt_properly(ke)
|
|
77
|
+
raise # Never reached, but satisfies type checker
|
|
78
|
+
except Exception as e:
|
|
79
|
+
raise PlatformErrorTeensy(f"Unexpected error installing platform: {e}")
|
|
80
|
+
|
|
81
|
+
def is_installed(self) -> bool:
|
|
82
|
+
"""Check if platform is already installed.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
True if both toolchain and framework are installed
|
|
86
|
+
"""
|
|
87
|
+
return self.toolchain.is_installed() and self.framework.is_installed()
|
|
88
|
+
|
|
89
|
+
def get_compiler_flags(self, board_config: Any) -> List[str]:
|
|
90
|
+
"""Get compiler flags for Teensy builds.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
board_config: Board configuration object
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
List of compiler flags
|
|
97
|
+
"""
|
|
98
|
+
flags = [
|
|
99
|
+
# CPU and architecture
|
|
100
|
+
"-mcpu=cortex-m7",
|
|
101
|
+
"-mthumb",
|
|
102
|
+
"-mfloat-abi=hard",
|
|
103
|
+
"-mfpu=fpv5-d16",
|
|
104
|
+
# Optimization
|
|
105
|
+
"-O2",
|
|
106
|
+
"-g",
|
|
107
|
+
# Warnings
|
|
108
|
+
"-Wall",
|
|
109
|
+
"-Wextra",
|
|
110
|
+
"-Wno-unused-parameter",
|
|
111
|
+
# Standards
|
|
112
|
+
"-std=gnu11", # For C files
|
|
113
|
+
# Board-specific defines
|
|
114
|
+
f"-DF_CPU={board_config.f_cpu}",
|
|
115
|
+
"-DARDUINO_TEENSY41",
|
|
116
|
+
"-D__IMXRT1062__",
|
|
117
|
+
"-DARDUINO=10819",
|
|
118
|
+
"-DTEENSYDUINO=159",
|
|
119
|
+
"-DUSB_SERIAL",
|
|
120
|
+
# Memory layout
|
|
121
|
+
"-DARDUINO_ARCH_TEENSY",
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
return flags
|
|
125
|
+
|
|
126
|
+
def get_compiler_flags_cpp(self, board_config: Any) -> List[str]:
|
|
127
|
+
"""Get C++ compiler flags for Teensy builds.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
board_config: Board configuration object
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
List of C++ compiler flags
|
|
134
|
+
"""
|
|
135
|
+
# Start with base C flags
|
|
136
|
+
flags = self.get_compiler_flags(board_config)
|
|
137
|
+
|
|
138
|
+
# Replace C standard with C++ standard
|
|
139
|
+
flags = [f for f in flags if not f.startswith("-std=gnu11")]
|
|
140
|
+
flags.extend(
|
|
141
|
+
[
|
|
142
|
+
"-std=gnu++14",
|
|
143
|
+
"-fno-exceptions",
|
|
144
|
+
"-fno-rtti",
|
|
145
|
+
"-felide-constructors",
|
|
146
|
+
"-fno-threadsafe-statics",
|
|
147
|
+
]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return flags
|
|
151
|
+
|
|
152
|
+
def get_linker_flags(self, board_config: Any, board_id: str = "teensy41") -> List[str]:
|
|
153
|
+
"""Get linker flags for Teensy builds.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
board_config: Board configuration object
|
|
157
|
+
board_id: Board identifier for linker script selection
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
List of linker flags
|
|
161
|
+
"""
|
|
162
|
+
# Get linker script
|
|
163
|
+
linker_script = self.framework.get_linker_script(board_id)
|
|
164
|
+
if not linker_script:
|
|
165
|
+
raise PlatformErrorTeensy(f"Linker script not found for board: {board_id}")
|
|
166
|
+
|
|
167
|
+
flags = [
|
|
168
|
+
# CPU and architecture
|
|
169
|
+
"-mcpu=cortex-m7",
|
|
170
|
+
"-mthumb",
|
|
171
|
+
"-mfloat-abi=hard",
|
|
172
|
+
"-mfpu=fpv5-d16",
|
|
173
|
+
# Optimization
|
|
174
|
+
"-O2",
|
|
175
|
+
# Linker script
|
|
176
|
+
f"-T{linker_script}",
|
|
177
|
+
# Linker options
|
|
178
|
+
"-Wl,--gc-sections",
|
|
179
|
+
"-Wl,--print-memory-usage",
|
|
180
|
+
# Math library
|
|
181
|
+
"-lm",
|
|
182
|
+
"-lstdc++",
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
return flags
|
|
186
|
+
|
|
187
|
+
def get_include_dirs(self, board_config: Any) -> List[Path]:
|
|
188
|
+
"""Get include directories for Teensy builds.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
board_config: Board configuration object
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
List of include directory paths
|
|
195
|
+
"""
|
|
196
|
+
includes = []
|
|
197
|
+
|
|
198
|
+
# Core includes
|
|
199
|
+
try:
|
|
200
|
+
core_includes = self.framework.get_core_includes("teensy4")
|
|
201
|
+
includes.extend(core_includes)
|
|
202
|
+
except FrameworkErrorTeensy:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
return includes
|
|
206
|
+
|
|
207
|
+
def get_core_sources(self) -> List[Path]:
|
|
208
|
+
"""Get core source files for Teensy builds.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
List of core source file paths
|
|
212
|
+
"""
|
|
213
|
+
try:
|
|
214
|
+
return self.framework.get_core_sources("teensy4")
|
|
215
|
+
except FrameworkErrorTeensy:
|
|
216
|
+
return []
|
|
217
|
+
|
|
218
|
+
def get_toolchain_binaries(self) -> Dict[str, Path]:
|
|
219
|
+
"""Get paths to toolchain binaries.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Dictionary mapping tool names to paths
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
PlatformErrorTeensy: If toolchain binaries are not found
|
|
226
|
+
"""
|
|
227
|
+
tools = self.toolchain.get_all_tool_paths()
|
|
228
|
+
|
|
229
|
+
# Verify all required tools exist
|
|
230
|
+
required_tools = ["gcc", "g++", "ar", "objcopy", "size"]
|
|
231
|
+
for tool_name in required_tools:
|
|
232
|
+
if tool_name not in tools or tools[tool_name] is None:
|
|
233
|
+
raise PlatformErrorTeensy(f"Required tool not found: {tool_name}")
|
|
234
|
+
|
|
235
|
+
# Filter out None values
|
|
236
|
+
return {name: path for name, path in tools.items() if path is not None}
|
|
237
|
+
|
|
238
|
+
def get_package_info(self) -> Dict[str, Any]:
|
|
239
|
+
"""Get information about the installed platform.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Dictionary with platform information
|
|
243
|
+
"""
|
|
244
|
+
return self.get_platform_info()
|
|
245
|
+
|
|
246
|
+
def get_board_json(self, board_id: str) -> Dict[str, Any]:
|
|
247
|
+
"""Get board configuration in JSON format.
|
|
248
|
+
|
|
249
|
+
This method returns board configuration compatible with the format
|
|
250
|
+
expected by ConfigurableCompiler and ConfigurableLinker.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
board_id: Board identifier (e.g., "teensy41")
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Dictionary containing board configuration
|
|
257
|
+
|
|
258
|
+
Raises:
|
|
259
|
+
PlatformErrorTeensy: If board is not supported
|
|
260
|
+
"""
|
|
261
|
+
# Map board IDs to their configurations
|
|
262
|
+
board_configs = {
|
|
263
|
+
"teensy41": {
|
|
264
|
+
"build": {
|
|
265
|
+
"mcu": "imxrt1062",
|
|
266
|
+
"f_cpu": "600000000L",
|
|
267
|
+
"core": "teensy4",
|
|
268
|
+
"variant": "teensy41",
|
|
269
|
+
"board": "TEENSY41",
|
|
270
|
+
},
|
|
271
|
+
"name": "Teensy 4.1",
|
|
272
|
+
"upload": {
|
|
273
|
+
"maximum_size": 8126464,
|
|
274
|
+
"maximum_ram_size": 524288,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
"teensy40": {
|
|
278
|
+
"build": {
|
|
279
|
+
"mcu": "imxrt1062",
|
|
280
|
+
"f_cpu": "600000000L",
|
|
281
|
+
"core": "teensy4",
|
|
282
|
+
"variant": "teensy40",
|
|
283
|
+
"board": "TEENSY40",
|
|
284
|
+
},
|
|
285
|
+
"name": "Teensy 4.0",
|
|
286
|
+
"upload": {
|
|
287
|
+
"maximum_size": 2031616,
|
|
288
|
+
"maximum_ram_size": 524288,
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if board_id not in board_configs:
|
|
294
|
+
raise PlatformErrorTeensy(f"Unsupported board: {board_id}. " + f"Supported boards: {', '.join(board_configs.keys())}")
|
|
295
|
+
|
|
296
|
+
return board_configs[board_id]
|
|
297
|
+
|
|
298
|
+
def get_platform_info(self) -> Dict[str, Any]:
|
|
299
|
+
"""Get information about the installed platform.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Dictionary with platform information
|
|
303
|
+
"""
|
|
304
|
+
info = {
|
|
305
|
+
"platform": "teensy",
|
|
306
|
+
"mcu": self.board_mcu,
|
|
307
|
+
"installed": self.is_installed(),
|
|
308
|
+
"toolchain": self.toolchain.get_toolchain_info(),
|
|
309
|
+
"framework": self.framework.get_framework_info(),
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return info
|