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,209 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PlatformIO.ini configuration parser.
|
|
3
|
+
|
|
4
|
+
This module provides functionality to parse platformio.ini files and extract
|
|
5
|
+
environment configurations for building embedded projects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import configparser
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PlatformIOConfigError(Exception):
|
|
14
|
+
"""Exception raised for platformio.ini configuration errors."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PlatformIOConfig:
|
|
20
|
+
"""
|
|
21
|
+
Parser for platformio.ini configuration files.
|
|
22
|
+
|
|
23
|
+
This class handles parsing of PlatformIO-style INI files, extracting
|
|
24
|
+
environment configurations, and validating required fields.
|
|
25
|
+
|
|
26
|
+
Example platformio.ini:
|
|
27
|
+
[env:uno]
|
|
28
|
+
platform = atmelavr
|
|
29
|
+
board = uno
|
|
30
|
+
framework = arduino
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
config = PlatformIOConfig(Path("platformio.ini"))
|
|
34
|
+
envs = config.get_environments()
|
|
35
|
+
uno_config = config.get_env_config("uno")
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
REQUIRED_FIELDS = {"platform", "board", "framework"}
|
|
39
|
+
|
|
40
|
+
def __init__(self, ini_path: Path):
|
|
41
|
+
"""
|
|
42
|
+
Initialize the parser with a platformio.ini file.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
ini_path: Path to the platformio.ini file
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
PlatformIOConfigError: If the file doesn't exist or cannot be parsed
|
|
49
|
+
"""
|
|
50
|
+
self.ini_path = ini_path
|
|
51
|
+
|
|
52
|
+
if not ini_path.exists():
|
|
53
|
+
raise PlatformIOConfigError(f"Configuration file not found: {ini_path}")
|
|
54
|
+
|
|
55
|
+
self.config = configparser.ConfigParser(allow_no_value=True, interpolation=configparser.ExtendedInterpolation())
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
self.config.read(ini_path, encoding="utf-8")
|
|
59
|
+
except configparser.Error as e:
|
|
60
|
+
raise PlatformIOConfigError(f"Failed to parse {ini_path}: {e}") from e
|
|
61
|
+
|
|
62
|
+
def get_environments(self) -> List[str]:
|
|
63
|
+
"""
|
|
64
|
+
Get list of all environment names defined in the config.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List of environment names (e.g., ['uno', 'mega', 'nano'])
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
For [env:uno], [env:mega], returns ['uno', 'mega']
|
|
71
|
+
"""
|
|
72
|
+
envs = []
|
|
73
|
+
for section in self.config.sections():
|
|
74
|
+
if section.startswith("env:"):
|
|
75
|
+
env_name = section.split(":", 1)[1]
|
|
76
|
+
envs.append(env_name)
|
|
77
|
+
return envs
|
|
78
|
+
|
|
79
|
+
def get_env_config(self, env_name: str) -> Dict[str, str]:
|
|
80
|
+
"""
|
|
81
|
+
Get configuration for a specific environment.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
env_name: Name of the environment (e.g., 'uno')
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Dictionary of configuration key-value pairs
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
PlatformIOConfigError: If environment not found or missing required fields
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
config.get_env_config('uno')
|
|
94
|
+
# Returns: {'platform': 'atmelavr', 'board': 'uno', 'framework': 'arduino'}
|
|
95
|
+
"""
|
|
96
|
+
section = f"env:{env_name}"
|
|
97
|
+
|
|
98
|
+
if section not in self.config:
|
|
99
|
+
available = ", ".join(self.get_environments())
|
|
100
|
+
raise PlatformIOConfigError(f"Environment '{env_name}' not found. " + f"Available environments: {available or 'none'}")
|
|
101
|
+
|
|
102
|
+
# Collect all key-value pairs from the environment section
|
|
103
|
+
env_config = {}
|
|
104
|
+
for key in self.config[section]:
|
|
105
|
+
value = self.config[section][key]
|
|
106
|
+
# Handle multi-line values (like lib_deps)
|
|
107
|
+
env_config[key] = value.strip()
|
|
108
|
+
|
|
109
|
+
# Also check if there's a base [env] section to inherit from
|
|
110
|
+
if "env" in self.config:
|
|
111
|
+
base_config = dict(self.config["env"])
|
|
112
|
+
# Environment-specific values override base values
|
|
113
|
+
env_config = {**base_config, **env_config}
|
|
114
|
+
|
|
115
|
+
# Validate required fields
|
|
116
|
+
missing_fields = self.REQUIRED_FIELDS - set(env_config.keys())
|
|
117
|
+
if missing_fields:
|
|
118
|
+
raise PlatformIOConfigError(f"Environment '{env_name}' is missing required fields: " + f"{', '.join(sorted(missing_fields))}")
|
|
119
|
+
|
|
120
|
+
return env_config
|
|
121
|
+
|
|
122
|
+
def get_build_flags(self, env_name: str) -> List[str]:
|
|
123
|
+
"""
|
|
124
|
+
Parse and return build flags for an environment.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
env_name: Name of the environment
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
List of build flags
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
For build_flags = -DDEBUG -DVERSION=1.0
|
|
134
|
+
Returns: ['-DDEBUG', '-DVERSION=1.0']
|
|
135
|
+
"""
|
|
136
|
+
env_config = self.get_env_config(env_name)
|
|
137
|
+
build_flags_str = env_config.get("build_flags", "")
|
|
138
|
+
|
|
139
|
+
if not build_flags_str:
|
|
140
|
+
return []
|
|
141
|
+
|
|
142
|
+
# Split on whitespace and newlines, filter empty strings
|
|
143
|
+
flags = build_flags_str.split()
|
|
144
|
+
return [flag for flag in flags if flag]
|
|
145
|
+
|
|
146
|
+
def get_lib_deps(self, env_name: str) -> List[str]:
|
|
147
|
+
"""
|
|
148
|
+
Parse and return library dependencies for an environment.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
env_name: Name of the environment
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
List of library dependencies
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
For lib_deps =
|
|
158
|
+
SPI
|
|
159
|
+
Wire
|
|
160
|
+
Returns: ['SPI', 'Wire']
|
|
161
|
+
"""
|
|
162
|
+
env_config = self.get_env_config(env_name)
|
|
163
|
+
lib_deps_str = env_config.get("lib_deps", "")
|
|
164
|
+
|
|
165
|
+
if not lib_deps_str:
|
|
166
|
+
return []
|
|
167
|
+
|
|
168
|
+
# Split on newlines and commas, strip whitespace, filter empty
|
|
169
|
+
deps = []
|
|
170
|
+
for line in lib_deps_str.split("\n"):
|
|
171
|
+
for dep in line.split(","):
|
|
172
|
+
dep = dep.strip()
|
|
173
|
+
if dep:
|
|
174
|
+
deps.append(dep)
|
|
175
|
+
return deps
|
|
176
|
+
|
|
177
|
+
def has_environment(self, env_name: str) -> bool:
|
|
178
|
+
"""
|
|
179
|
+
Check if an environment exists in the configuration.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
env_name: Name of the environment to check
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
True if environment exists, False otherwise
|
|
186
|
+
"""
|
|
187
|
+
return f"env:{env_name}" in self.config
|
|
188
|
+
|
|
189
|
+
def get_default_environment(self) -> Optional[str]:
|
|
190
|
+
"""
|
|
191
|
+
Get the default environment from platformio.ini.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Default environment name, or first available environment, or None
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
If [platformio] section has default_envs = uno, returns 'uno'
|
|
198
|
+
Otherwise returns the first environment found
|
|
199
|
+
"""
|
|
200
|
+
# Check for explicit default_envs in [platformio] section
|
|
201
|
+
if "platformio" in self.config:
|
|
202
|
+
default_envs = self.config["platformio"].get("default_envs", "").strip()
|
|
203
|
+
if default_envs:
|
|
204
|
+
# Can be comma-separated, take the first one
|
|
205
|
+
return default_envs.split(",")[0].strip()
|
|
206
|
+
|
|
207
|
+
# Fall back to first environment
|
|
208
|
+
envs = self.get_environments()
|
|
209
|
+
return envs[0] if envs else None
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCU specifications for various embedded platforms.
|
|
3
|
+
|
|
4
|
+
This module centralizes hardware specifications like flash size and RAM
|
|
5
|
+
for different microcontrollers, making it easier to maintain and extend.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class MCUSpec:
|
|
14
|
+
"""Hardware specifications for a microcontroller."""
|
|
15
|
+
|
|
16
|
+
mcu_id: str
|
|
17
|
+
max_flash: int # Maximum flash size in bytes
|
|
18
|
+
max_ram: int # Maximum RAM size in bytes
|
|
19
|
+
bootloader_size: int = 0 # Bootloader size in bytes (subtracted from flash)
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def usable_flash(self) -> int:
|
|
23
|
+
"""Get usable flash after accounting for bootloader."""
|
|
24
|
+
return self.max_flash - self.bootloader_size
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# AVR MCU specifications
|
|
28
|
+
AVR_SPECS = {
|
|
29
|
+
"atmega328p": MCUSpec(
|
|
30
|
+
mcu_id="atmega328p",
|
|
31
|
+
max_flash=32768, # 32KB
|
|
32
|
+
max_ram=2048, # 2KB
|
|
33
|
+
bootloader_size=512, # 512B bootloader
|
|
34
|
+
),
|
|
35
|
+
"atmega2560": MCUSpec(
|
|
36
|
+
mcu_id="atmega2560",
|
|
37
|
+
max_flash=262144, # 256KB
|
|
38
|
+
max_ram=8192, # 8KB
|
|
39
|
+
bootloader_size=8192, # 8KB bootloader
|
|
40
|
+
),
|
|
41
|
+
"atmega32u4": MCUSpec(
|
|
42
|
+
mcu_id="atmega32u4",
|
|
43
|
+
max_flash=32768, # 32KB
|
|
44
|
+
max_ram=2560, # 2.5KB
|
|
45
|
+
bootloader_size=4096, # 4KB bootloader
|
|
46
|
+
),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_mcu_spec(mcu_id: str) -> Optional[MCUSpec]:
|
|
51
|
+
"""
|
|
52
|
+
Get MCU specifications by ID.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
mcu_id: MCU identifier (e.g., 'atmega328p')
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
MCUSpec if found, None otherwise
|
|
59
|
+
"""
|
|
60
|
+
return AVR_SPECS.get(mcu_id.lower())
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_max_flash(mcu_id: str) -> Optional[int]:
|
|
64
|
+
"""
|
|
65
|
+
Get maximum usable flash for an MCU (after bootloader).
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
mcu_id: MCU identifier
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Usable flash size in bytes, or None if MCU not found
|
|
72
|
+
"""
|
|
73
|
+
spec = get_mcu_spec(mcu_id)
|
|
74
|
+
return spec.usable_flash if spec else None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_max_ram(mcu_id: str) -> Optional[int]:
|
|
78
|
+
"""
|
|
79
|
+
Get maximum RAM for an MCU.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
mcu_id: MCU identifier
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
RAM size in bytes, or None if MCU not found
|
|
86
|
+
"""
|
|
87
|
+
spec = get_mcu_spec(mcu_id)
|
|
88
|
+
return spec.max_ram if spec else None
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
fbuild Daemon - Concurrent Deploy and Monitor Management
|
|
3
|
+
|
|
4
|
+
This package provides a singleton daemon for managing concurrent deploy and monitor
|
|
5
|
+
operations with proper locking and process tree tracking.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from fbuild.daemon.client import (
|
|
9
|
+
ensure_daemon_running,
|
|
10
|
+
get_daemon_status,
|
|
11
|
+
request_deploy,
|
|
12
|
+
request_monitor,
|
|
13
|
+
stop_daemon,
|
|
14
|
+
)
|
|
15
|
+
from fbuild.daemon.messages import (
|
|
16
|
+
DaemonState,
|
|
17
|
+
DaemonStatus,
|
|
18
|
+
DeployRequest,
|
|
19
|
+
MonitorRequest,
|
|
20
|
+
OperationType,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"DaemonState",
|
|
25
|
+
"DaemonStatus",
|
|
26
|
+
"DeployRequest",
|
|
27
|
+
"MonitorRequest",
|
|
28
|
+
"OperationType",
|
|
29
|
+
"ensure_daemon_running",
|
|
30
|
+
"get_daemon_status",
|
|
31
|
+
"request_deploy",
|
|
32
|
+
"request_monitor",
|
|
33
|
+
"stop_daemon",
|
|
34
|
+
]
|