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,413 @@
|
|
|
1
|
+
"""ESP32 library dependency management.
|
|
2
|
+
|
|
3
|
+
This module handles downloading and compiling external libraries for ESP32 builds.
|
|
4
|
+
It uses the PlatformIO registry to resolve and download libraries, then compiles
|
|
5
|
+
them with the ESP32 toolchain.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
|
|
13
|
+
from fbuild.packages.platformio_registry import (
|
|
14
|
+
LibrarySpec,
|
|
15
|
+
PlatformIORegistry,
|
|
16
|
+
RegistryError,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LibraryErrorESP32(Exception):
|
|
21
|
+
"""Exception for ESP32 library management errors."""
|
|
22
|
+
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LibraryESP32:
|
|
27
|
+
"""Represents a downloaded and compiled ESP32 library."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, lib_dir: Path, name: str):
|
|
30
|
+
"""Initialize ESP32 library.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
lib_dir: Root directory for the library
|
|
34
|
+
name: Library name
|
|
35
|
+
"""
|
|
36
|
+
self.lib_dir = lib_dir
|
|
37
|
+
self.name = name
|
|
38
|
+
self.src_dir = lib_dir / "src"
|
|
39
|
+
self.info_file = lib_dir / "library.json"
|
|
40
|
+
self.archive_file = lib_dir / f"lib{name}.a"
|
|
41
|
+
self.build_info_file = lib_dir / "build_info.json"
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def exists(self) -> bool:
|
|
45
|
+
"""Check if library is downloaded and compiled."""
|
|
46
|
+
return self.lib_dir.exists() and self.src_dir.exists() and self.info_file.exists()
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def is_compiled(self) -> bool:
|
|
50
|
+
"""Check if library is compiled."""
|
|
51
|
+
return self.archive_file.exists() and self.build_info_file.exists()
|
|
52
|
+
|
|
53
|
+
def get_source_files(self) -> List[Path]:
|
|
54
|
+
"""Find all source files (.c, .cpp, .cc, .cxx) in the library.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
List of source file paths
|
|
58
|
+
"""
|
|
59
|
+
if not self.src_dir.exists():
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
sources = []
|
|
63
|
+
|
|
64
|
+
# Check for src/src/ structure (some libraries have this)
|
|
65
|
+
src_src = self.src_dir / "src"
|
|
66
|
+
search_dir = src_src if (src_src.exists() and src_src.is_dir()) else self.src_dir
|
|
67
|
+
|
|
68
|
+
# Find all source files recursively
|
|
69
|
+
for pattern in ["**/*.c", "**/*.cpp", "**/*.cc", "**/*.cxx"]:
|
|
70
|
+
for path in search_dir.glob(pattern):
|
|
71
|
+
# Skip examples and tests (check relative path only)
|
|
72
|
+
rel_path = str(path.relative_to(search_dir)).lower()
|
|
73
|
+
if "example" not in rel_path and "test" not in rel_path:
|
|
74
|
+
sources.append(path)
|
|
75
|
+
|
|
76
|
+
return sources
|
|
77
|
+
|
|
78
|
+
def get_include_dirs(self) -> List[Path]:
|
|
79
|
+
"""Get include directories for this library.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of include directory paths
|
|
83
|
+
"""
|
|
84
|
+
include_dirs = []
|
|
85
|
+
|
|
86
|
+
if not self.src_dir.exists():
|
|
87
|
+
return include_dirs
|
|
88
|
+
|
|
89
|
+
# Check for src/src/ structure
|
|
90
|
+
src_src = self.src_dir / "src"
|
|
91
|
+
if src_src.exists() and src_src.is_dir():
|
|
92
|
+
include_dirs.append(src_src)
|
|
93
|
+
else:
|
|
94
|
+
include_dirs.append(self.src_dir)
|
|
95
|
+
|
|
96
|
+
# Look for additional include directories
|
|
97
|
+
for name in ["include", "Include", "INCLUDE"]:
|
|
98
|
+
inc_dir = self.lib_dir / name
|
|
99
|
+
if inc_dir.exists():
|
|
100
|
+
include_dirs.append(inc_dir)
|
|
101
|
+
|
|
102
|
+
return include_dirs
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class LibraryManagerESP32:
|
|
106
|
+
"""Manages ESP32 library dependencies."""
|
|
107
|
+
|
|
108
|
+
def __init__(self, build_dir: Path, registry: Optional[PlatformIORegistry] = None):
|
|
109
|
+
"""Initialize library manager.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
build_dir: Build directory (.fbuild/build/{board})
|
|
113
|
+
registry: Optional registry client
|
|
114
|
+
"""
|
|
115
|
+
self.build_dir = Path(build_dir)
|
|
116
|
+
self.libs_dir = self.build_dir / "libs"
|
|
117
|
+
self.registry = registry or PlatformIORegistry()
|
|
118
|
+
|
|
119
|
+
# Ensure libs directory exists
|
|
120
|
+
self.libs_dir.mkdir(parents=True, exist_ok=True)
|
|
121
|
+
|
|
122
|
+
def _sanitize_name(self, name: str) -> str:
|
|
123
|
+
"""Sanitize library name for filesystem.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
name: Library name
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Sanitized name
|
|
130
|
+
"""
|
|
131
|
+
return name.lower().replace("/", "_").replace("@", "_")
|
|
132
|
+
|
|
133
|
+
def get_library(self, spec: LibrarySpec) -> LibraryESP32:
|
|
134
|
+
"""Get a library instance for a specification.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
spec: Library specification
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
LibraryESP32 instance
|
|
141
|
+
"""
|
|
142
|
+
lib_name = self._sanitize_name(spec.name)
|
|
143
|
+
lib_dir = self.libs_dir / lib_name
|
|
144
|
+
return LibraryESP32(lib_dir, lib_name)
|
|
145
|
+
|
|
146
|
+
def download_library(self, spec: LibrarySpec, show_progress: bool = True) -> LibraryESP32:
|
|
147
|
+
"""Download a library from PlatformIO registry.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
spec: Library specification
|
|
151
|
+
show_progress: Whether to show progress
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
LibraryESP32 instance
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
LibraryErrorESP32: If download fails
|
|
158
|
+
"""
|
|
159
|
+
try:
|
|
160
|
+
library = self.get_library(spec)
|
|
161
|
+
|
|
162
|
+
# Skip if already downloaded
|
|
163
|
+
if library.exists:
|
|
164
|
+
if show_progress:
|
|
165
|
+
print(f"Library '{spec.name}' already downloaded")
|
|
166
|
+
return library
|
|
167
|
+
|
|
168
|
+
# Download from registry
|
|
169
|
+
self.registry.download_library(spec, library.lib_dir, show_progress=show_progress)
|
|
170
|
+
|
|
171
|
+
return library
|
|
172
|
+
|
|
173
|
+
except RegistryError as e:
|
|
174
|
+
raise LibraryErrorESP32(f"Failed to download library {spec}: {e}") from e
|
|
175
|
+
|
|
176
|
+
def needs_rebuild(self, library: LibraryESP32, compiler_flags: List[str]) -> tuple[bool, str]:
|
|
177
|
+
"""Check if a library needs to be rebuilt.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
library: Library to check
|
|
181
|
+
compiler_flags: Current compiler flags
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Tuple of (needs_rebuild, reason)
|
|
185
|
+
"""
|
|
186
|
+
if not library.archive_file.exists():
|
|
187
|
+
return True, "Archive not found"
|
|
188
|
+
|
|
189
|
+
if not library.build_info_file.exists():
|
|
190
|
+
return True, "Build info missing"
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
with open(library.build_info_file, "r", encoding="utf-8") as f:
|
|
194
|
+
build_info = json.load(f)
|
|
195
|
+
|
|
196
|
+
# Check if compiler flags changed
|
|
197
|
+
stored_flags = build_info.get("compiler_flags", [])
|
|
198
|
+
if stored_flags != compiler_flags:
|
|
199
|
+
return True, "Compiler flags changed"
|
|
200
|
+
|
|
201
|
+
except KeyboardInterrupt as ke:
|
|
202
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
203
|
+
|
|
204
|
+
handle_keyboard_interrupt_properly(ke)
|
|
205
|
+
raise # Never reached, but satisfies type checker
|
|
206
|
+
except Exception:
|
|
207
|
+
return True, "Could not load build info"
|
|
208
|
+
|
|
209
|
+
return False, ""
|
|
210
|
+
|
|
211
|
+
def compile_library(
|
|
212
|
+
self,
|
|
213
|
+
library: LibraryESP32,
|
|
214
|
+
toolchain_path: Path,
|
|
215
|
+
compiler_flags: List[str],
|
|
216
|
+
include_paths: List[Path],
|
|
217
|
+
show_progress: bool = True,
|
|
218
|
+
) -> Path:
|
|
219
|
+
"""Compile a library into a static archive.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
library: Library to compile
|
|
223
|
+
toolchain_path: Path to toolchain bin directory
|
|
224
|
+
compiler_flags: Compiler flags
|
|
225
|
+
include_paths: Include directories
|
|
226
|
+
show_progress: Whether to show progress
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Path to compiled archive file
|
|
230
|
+
|
|
231
|
+
Raises:
|
|
232
|
+
LibraryErrorESP32: If compilation fails
|
|
233
|
+
"""
|
|
234
|
+
try:
|
|
235
|
+
if show_progress:
|
|
236
|
+
print(f"Compiling library: {library.name}")
|
|
237
|
+
|
|
238
|
+
# Get source files
|
|
239
|
+
sources = library.get_source_files()
|
|
240
|
+
if not sources:
|
|
241
|
+
raise LibraryErrorESP32(f"No source files found in library '{library.name}'")
|
|
242
|
+
|
|
243
|
+
if show_progress:
|
|
244
|
+
print(f" Found {len(sources)} source file(s)")
|
|
245
|
+
|
|
246
|
+
# Get library's own include directories
|
|
247
|
+
lib_includes = library.get_include_dirs()
|
|
248
|
+
all_includes = list(include_paths) + lib_includes
|
|
249
|
+
|
|
250
|
+
# Compile each source file
|
|
251
|
+
object_files = []
|
|
252
|
+
gcc_path = toolchain_path / "riscv32-esp-elf-gcc"
|
|
253
|
+
gxx_path = toolchain_path / "riscv32-esp-elf-g++"
|
|
254
|
+
|
|
255
|
+
# Create response file for include paths (avoid Windows command line length limit)
|
|
256
|
+
include_flags = [f"-I{str(inc).replace(chr(92), '/')}" for inc in all_includes]
|
|
257
|
+
response_file = library.lib_dir / "includes.rsp"
|
|
258
|
+
with open(response_file, "w") as f:
|
|
259
|
+
f.write("\n".join(include_flags))
|
|
260
|
+
|
|
261
|
+
for source in sources:
|
|
262
|
+
# Determine compiler based on extension
|
|
263
|
+
if source.suffix in [".cpp", ".cc", ".cxx"]:
|
|
264
|
+
compiler = gxx_path
|
|
265
|
+
else:
|
|
266
|
+
compiler = gcc_path
|
|
267
|
+
|
|
268
|
+
# Output object file
|
|
269
|
+
obj_file = library.lib_dir / f"{source.stem}.o"
|
|
270
|
+
|
|
271
|
+
# Build compile command
|
|
272
|
+
cmd = [str(compiler), "-c"]
|
|
273
|
+
cmd.extend(compiler_flags)
|
|
274
|
+
|
|
275
|
+
# Use response file for include paths
|
|
276
|
+
cmd.append(f"@{response_file}")
|
|
277
|
+
# Add source and output
|
|
278
|
+
cmd.extend(["-o", str(obj_file), str(source)])
|
|
279
|
+
|
|
280
|
+
# Compile
|
|
281
|
+
if show_progress:
|
|
282
|
+
print(f" Compiling {source.name}...")
|
|
283
|
+
|
|
284
|
+
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
|
|
285
|
+
|
|
286
|
+
if result.returncode != 0:
|
|
287
|
+
raise LibraryErrorESP32(f"Failed to compile {source.name}:\n{result.stderr}")
|
|
288
|
+
|
|
289
|
+
object_files.append(obj_file)
|
|
290
|
+
|
|
291
|
+
# Create static archive using ar
|
|
292
|
+
ar_path = toolchain_path / "riscv32-esp-elf-ar"
|
|
293
|
+
|
|
294
|
+
if show_progress:
|
|
295
|
+
print(f" Creating archive: {library.archive_file.name}")
|
|
296
|
+
|
|
297
|
+
# Remove old archive if exists
|
|
298
|
+
if library.archive_file.exists():
|
|
299
|
+
library.archive_file.unlink()
|
|
300
|
+
|
|
301
|
+
# Create new archive
|
|
302
|
+
cmd = [str(ar_path), "rcs", str(library.archive_file)]
|
|
303
|
+
cmd.extend([str(obj) for obj in object_files])
|
|
304
|
+
|
|
305
|
+
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
|
|
306
|
+
|
|
307
|
+
if result.returncode != 0:
|
|
308
|
+
raise LibraryErrorESP32(f"Failed to create archive for {library.name}:\n{result.stderr}")
|
|
309
|
+
|
|
310
|
+
# Save build info
|
|
311
|
+
build_info = {
|
|
312
|
+
"compiler_flags": compiler_flags,
|
|
313
|
+
"source_count": len(sources),
|
|
314
|
+
"object_files": [str(obj) for obj in object_files],
|
|
315
|
+
}
|
|
316
|
+
with open(library.build_info_file, "w", encoding="utf-8") as f:
|
|
317
|
+
json.dump(build_info, f, indent=2)
|
|
318
|
+
|
|
319
|
+
if show_progress:
|
|
320
|
+
print(f"Library '{library.name}' compiled successfully")
|
|
321
|
+
|
|
322
|
+
return library.archive_file
|
|
323
|
+
|
|
324
|
+
except subprocess.CalledProcessError as e:
|
|
325
|
+
raise LibraryErrorESP32(f"Compilation failed for library '{library.name}': {e}") from e
|
|
326
|
+
except KeyboardInterrupt as ke:
|
|
327
|
+
from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
|
|
328
|
+
|
|
329
|
+
handle_keyboard_interrupt_properly(ke)
|
|
330
|
+
raise # Never reached, but satisfies type checker
|
|
331
|
+
except Exception as e:
|
|
332
|
+
raise LibraryErrorESP32(f"Failed to compile library '{library.name}': {e}") from e
|
|
333
|
+
|
|
334
|
+
def ensure_libraries(
|
|
335
|
+
self,
|
|
336
|
+
lib_specs: List[str],
|
|
337
|
+
toolchain_path: Path,
|
|
338
|
+
compiler_flags: List[str],
|
|
339
|
+
include_paths: List[Path],
|
|
340
|
+
show_progress: bool = True,
|
|
341
|
+
) -> List[LibraryESP32]:
|
|
342
|
+
"""Ensure all library dependencies are downloaded and compiled.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
lib_specs: List of library specification strings
|
|
346
|
+
toolchain_path: Path to toolchain bin directory
|
|
347
|
+
compiler_flags: Compiler flags
|
|
348
|
+
include_paths: Include directories
|
|
349
|
+
show_progress: Whether to show progress
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
List of compiled LibraryESP32 instances
|
|
353
|
+
"""
|
|
354
|
+
libraries = []
|
|
355
|
+
|
|
356
|
+
for spec_str in lib_specs:
|
|
357
|
+
# Parse library specification
|
|
358
|
+
spec = LibrarySpec.parse(spec_str)
|
|
359
|
+
|
|
360
|
+
# Download library
|
|
361
|
+
library = self.download_library(spec, show_progress)
|
|
362
|
+
|
|
363
|
+
# Check if rebuild needed
|
|
364
|
+
needs_rebuild, reason = self.needs_rebuild(library, compiler_flags)
|
|
365
|
+
|
|
366
|
+
if needs_rebuild:
|
|
367
|
+
if show_progress and reason:
|
|
368
|
+
print(f"Rebuilding library '{library.name}': {reason}")
|
|
369
|
+
|
|
370
|
+
self.compile_library(
|
|
371
|
+
library,
|
|
372
|
+
toolchain_path,
|
|
373
|
+
compiler_flags,
|
|
374
|
+
include_paths,
|
|
375
|
+
show_progress,
|
|
376
|
+
)
|
|
377
|
+
else:
|
|
378
|
+
if show_progress:
|
|
379
|
+
print(f"Library '{library.name}' is up to date")
|
|
380
|
+
|
|
381
|
+
libraries.append(library)
|
|
382
|
+
|
|
383
|
+
return libraries
|
|
384
|
+
|
|
385
|
+
def get_library_archives(self) -> List[Path]:
|
|
386
|
+
"""Get paths to all compiled library archives.
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
List of .a archive file paths
|
|
390
|
+
"""
|
|
391
|
+
archives = []
|
|
392
|
+
if self.libs_dir.exists():
|
|
393
|
+
for lib_dir in self.libs_dir.iterdir():
|
|
394
|
+
if lib_dir.is_dir():
|
|
395
|
+
archive = lib_dir / f"lib{lib_dir.name}.a"
|
|
396
|
+
if archive.exists():
|
|
397
|
+
archives.append(archive)
|
|
398
|
+
return archives
|
|
399
|
+
|
|
400
|
+
def get_library_include_paths(self) -> List[Path]:
|
|
401
|
+
"""Get all include paths from downloaded libraries.
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
List of include directory paths
|
|
405
|
+
"""
|
|
406
|
+
include_paths = []
|
|
407
|
+
if self.libs_dir.exists():
|
|
408
|
+
for lib_dir in self.libs_dir.iterdir():
|
|
409
|
+
if lib_dir.is_dir():
|
|
410
|
+
library = LibraryESP32(lib_dir, lib_dir.name)
|
|
411
|
+
if library.exists:
|
|
412
|
+
include_paths.extend(library.get_include_dirs())
|
|
413
|
+
return include_paths
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Abstract base classes for package management.
|
|
2
|
+
|
|
3
|
+
This module defines the interface for platform-specific package managers
|
|
4
|
+
(toolchains, frameworks, cores, etc.) to ensure consistent behavior across
|
|
5
|
+
different platforms.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PackageError(Exception):
|
|
14
|
+
"""Base exception for package management errors."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IPackage(ABC):
|
|
20
|
+
"""Interface for downloadable packages.
|
|
21
|
+
|
|
22
|
+
This interface defines the common contract for all package types:
|
|
23
|
+
- Toolchains (AVR, RISC-V, Xtensa)
|
|
24
|
+
- Frameworks (Arduino-ESP32)
|
|
25
|
+
- Cores (Arduino AVR Core)
|
|
26
|
+
- Platforms (ESP32 Platform)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def ensure_package(self) -> Path:
|
|
31
|
+
"""Ensure package is downloaded and extracted.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Path to the extracted package directory
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
PackageError: If download or extraction fails
|
|
38
|
+
"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def is_installed(self) -> bool:
|
|
43
|
+
"""Check if package is already installed.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
True if package directory exists and is valid
|
|
47
|
+
"""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def get_package_info(self) -> Dict[str, Any]:
|
|
52
|
+
"""Get information about the package.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dictionary with package metadata (version, path, etc.)
|
|
56
|
+
"""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class IToolchain(IPackage):
|
|
61
|
+
"""Interface for toolchain packages.
|
|
62
|
+
|
|
63
|
+
Toolchains provide compiler, linker, and binary utilities for
|
|
64
|
+
specific architectures (AVR, RISC-V, Xtensa).
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def get_gcc_path(self) -> Optional[Path]:
|
|
69
|
+
"""Get path to GCC compiler.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Path to gcc binary or None if not found
|
|
73
|
+
"""
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def get_gxx_path(self) -> Optional[Path]:
|
|
78
|
+
"""Get path to G++ compiler.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Path to g++ binary or None if not found
|
|
82
|
+
"""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def get_ar_path(self) -> Optional[Path]:
|
|
87
|
+
"""Get path to archiver (ar).
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Path to ar binary or None if not found
|
|
91
|
+
"""
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def get_objcopy_path(self) -> Optional[Path]:
|
|
96
|
+
"""Get path to objcopy utility.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Path to objcopy binary or None if not found
|
|
100
|
+
"""
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
@abstractmethod
|
|
104
|
+
def get_size_path(self) -> Optional[Path]:
|
|
105
|
+
"""Get path to size utility.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Path to size binary or None if not found
|
|
109
|
+
"""
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def get_bin_dir(self) -> Optional[Path]:
|
|
114
|
+
"""Get path to toolchain bin directory.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Path to bin directory containing compiler binaries
|
|
118
|
+
"""
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
@abstractmethod
|
|
122
|
+
def get_all_tools(self) -> Dict[str, Path]:
|
|
123
|
+
"""Get paths to all required tools.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Dictionary mapping tool names to their paths
|
|
127
|
+
"""
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class IFramework(IPackage):
|
|
132
|
+
"""Interface for framework packages.
|
|
133
|
+
|
|
134
|
+
Frameworks provide core Arduino implementation, variants,
|
|
135
|
+
and built-in libraries for specific platforms.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
def get_cores_dir(self) -> Path:
|
|
140
|
+
"""Get path to cores directory.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Path to cores directory containing Arduino core implementation
|
|
144
|
+
"""
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
@abstractmethod
|
|
148
|
+
def get_variants_dir(self) -> Path:
|
|
149
|
+
"""Get path to variants directory.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Path to variants directory containing board-specific configurations
|
|
153
|
+
"""
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def get_libraries_dir(self) -> Path:
|
|
158
|
+
"""Get path to built-in libraries directory.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Path to libraries directory
|
|
162
|
+
"""
|
|
163
|
+
pass
|