77 lines
2.1 KiB
Python
Executable File
77 lines
2.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Generate a disk-2.0 LittleFS customer_fs image on macOS."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from littlefs import LittleFS, UserContext
|
|
|
|
|
|
IMAGE_SIZE = 589824
|
|
BLOCK_SIZE = 4096
|
|
BLOCK_COUNT = 144
|
|
DISK_VERSION = 0x00020000
|
|
|
|
|
|
def fail(message: str) -> None:
|
|
raise SystemExit(f"customer_fs generation failed: {message}")
|
|
|
|
|
|
def collect_source_files(source: Path) -> list[Path]:
|
|
if not source.is_dir():
|
|
fail(f"source directory does not exist: {source}")
|
|
entries = sorted(source.iterdir(), key=lambda path: path.name.encode("utf-8"))
|
|
if not entries:
|
|
fail(f"source directory is empty: {source}")
|
|
invalid = [entry.name for entry in entries if not entry.is_file() or entry.name.startswith(".")]
|
|
if invalid:
|
|
fail(
|
|
"source must contain only visible root-level files; invalid entries: "
|
|
+ ", ".join(invalid)
|
|
)
|
|
return entries
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--source", required=True, type=Path)
|
|
parser.add_argument("--output", required=True, type=Path)
|
|
args = parser.parse_args()
|
|
|
|
files = collect_source_files(args.source)
|
|
context = UserContext(buffsize=IMAGE_SIZE)
|
|
fs = LittleFS(
|
|
context=context,
|
|
mount=False,
|
|
block_size=BLOCK_SIZE,
|
|
block_count=BLOCK_COUNT,
|
|
read_size=16,
|
|
prog_size=16,
|
|
cache_size=256,
|
|
lookahead_size=32,
|
|
block_cycles=500,
|
|
disk_version=DISK_VERSION,
|
|
)
|
|
fs.format()
|
|
fs.mount()
|
|
try:
|
|
for source_file in files:
|
|
with source_file.open("rb") as source_handle:
|
|
with fs.open("/" + source_file.name, "wb") as image_handle:
|
|
while chunk := source_handle.read(64 * 1024):
|
|
image_handle.write(chunk)
|
|
finally:
|
|
fs.unmount()
|
|
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_bytes(context.buffer)
|
|
print(f"generated {args.output} from {len(files)} files")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|