feat: create firmware skeleton and reproducible build
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"""PlatformIO hooks for deterministic assets and partition size limits."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
Import("env")
|
||||
|
||||
|
||||
PROJECT_DIRECTORY = Path(env.subst("$PROJECT_DIR"))
|
||||
BUILD_DIRECTORY = Path(env.subst("$BUILD_DIR"))
|
||||
PARTITION_FILE = PROJECT_DIRECTORY / "partitions.csv"
|
||||
ASSET_SCRIPT = PROJECT_DIRECTORY / "scripts" / "generate_web_assets.py"
|
||||
FILESYSTEM_WRAPPER = PROJECT_DIRECTORY / "scripts" / "reproducible_mklittlefs.py"
|
||||
|
||||
|
||||
def parse_size(value: str) -> int:
|
||||
value = value.strip()
|
||||
suffixes = {"K": 1024, "M": 1024 * 1024}
|
||||
if value[-1:].upper() in suffixes:
|
||||
return int(value[:-1], 0) * suffixes[value[-1].upper()]
|
||||
return int(value, 0)
|
||||
|
||||
|
||||
def partition_sizes() -> dict[str, int]:
|
||||
sizes: dict[str, int] = {}
|
||||
with PARTITION_FILE.open(newline="", encoding="utf-8") as partition_stream:
|
||||
for row in csv.reader(partition_stream):
|
||||
if not row or row[0].lstrip().startswith("#"):
|
||||
continue
|
||||
if len(row) < 5:
|
||||
raise RuntimeError(f"Invalid partition row: {row}")
|
||||
sizes[row[0].strip()] = parse_size(row[4])
|
||||
return sizes
|
||||
|
||||
|
||||
def generate_assets(source, target, env) -> None:
|
||||
command = [
|
||||
env.subst("$PYTHONEXE"),
|
||||
str(ASSET_SCRIPT),
|
||||
"--source",
|
||||
str(PROJECT_DIRECTORY / "web"),
|
||||
"--output",
|
||||
str(PROJECT_DIRECTORY / "data"),
|
||||
]
|
||||
subprocess.run(command, check=True)
|
||||
|
||||
|
||||
def enforce_size(path: Path, partition_name: str) -> None:
|
||||
limits = partition_sizes()
|
||||
if partition_name not in limits:
|
||||
raise RuntimeError(f"Missing required partition: {partition_name}")
|
||||
actual_size = path.stat().st_size
|
||||
maximum_size = limits[partition_name]
|
||||
if actual_size > maximum_size:
|
||||
raise RuntimeError(
|
||||
f"{path.name} is {actual_size} bytes and exceeds the "
|
||||
f"{partition_name} partition limit of {maximum_size} bytes"
|
||||
)
|
||||
print(
|
||||
f"maze32 size check: {path.name} uses {actual_size}/{maximum_size} bytes "
|
||||
f"in partition {partition_name}"
|
||||
)
|
||||
|
||||
|
||||
def check_application(source, target, env) -> None:
|
||||
enforce_size(application_image, "factory")
|
||||
|
||||
|
||||
def check_filesystem(source, target, env) -> None:
|
||||
enforce_size(filesystem_image, "littlefs")
|
||||
|
||||
|
||||
filesystem_image = BUILD_DIRECTORY / "littlefs.bin"
|
||||
application_image = BUILD_DIRECTORY / f"{env.subst('$PROGNAME')}.bin"
|
||||
real_filesystem_tool = env.subst("$MKFSTOOL")
|
||||
|
||||
env["ENV"]["MAZE32_REAL_MKFSTOOL"] = real_filesystem_tool
|
||||
env["ENV"]["MAZE32_BUILD_DIR"] = str(BUILD_DIRECTORY)
|
||||
env.Replace(MKFSTOOL=str(FILESYSTEM_WRAPPER))
|
||||
|
||||
env.AddPreAction("$BUILD_DIR/littlefs.bin", generate_assets)
|
||||
env.AddPostAction("$BUILD_DIR/littlefs.bin", check_filesystem)
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", check_application)
|
||||
env.AddCustomTarget(
|
||||
"webassets",
|
||||
None,
|
||||
generate_assets,
|
||||
title="Generate deterministic web assets",
|
||||
description="Compress web sources into the LittleFS data directory",
|
||||
)
|
||||
Reference in New Issue
Block a user