Files
2026-09-21 22:29:23 +08:00

85 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""Compile and debug real native programs from the immutable development SYSTEM."""
import base64
import json
from pathlib import Path
import shlex
import subprocess
import sys
import tarfile
import tempfile
import time
project = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(project / 'tools'))
from image_formats import digest
from vm_test import VM
work = Path(tempfile.mkdtemp(prefix='m12-development.', dir=project / 'out'))
rootfs = (project / 'out/rootfs-development.tar').resolve(strict=True)
image = (project / 'out/fds-system-development.img').resolve(strict=True)
with tarfile.open(rootfs) as archive:
assert archive.extractfile('usr/share/fds/image-profile').read().strip() == b'development'
before = digest(image)
(work / 'inputs.sha256').write_text(f'{digest(rootfs)} {rootfs}\n{before} {image}\n')
with VM(work, 'toolchain', image) as vm:
vm.expect(rb'FDS> ')
assert vm.capture('id -u') == '1000'
vm.capture('test "$(date -u +%s)" -ge "$(cat /usr/share/fds/build-epoch)"')
clock = json.loads(vm.capture('cat /run/fds/clock.json'))
assert clock['source'] == 'image_floor' and clock['previous_unix_seconds'] < clock['image_epoch'], clock
(work / 'clock.json').write_text(json.dumps(clock, indent=2) + '\n')
vm.capture('fds-boottrace clock-floor', ok=False)
vm.capture('fds-release --version')
versions = vm.capture('for tool in gcc g++ make cmake meson ninja pkg-config rustc cargo git gdb strace vim; do "$tool" --version | head -n 1; done')
(work / 'tool-versions.txt').write_text(versions + '\n')
vm.capture('mkdir -p /tmp/development/c /tmp/development/rust/src')
files = {
'/tmp/development/c/main.c': '#include <stdio.h>\nint main(void) { puts("FDS C OK"); return 0; }\n',
'/tmp/development/c/main.cpp': '#include <iostream>\nint main() { std::cout << "FDS C++ OK\\n"; }\n',
'/tmp/development/c/CMakeLists.txt': 'cmake_minimum_required(VERSION 3.20)\nproject(fds_development C CXX)\nadd_executable(fds_c main.c)\nadd_executable(fds_cpp main.cpp)\n',
'/tmp/development/c/meson.build': "project('fds-development', 'c')\nexecutable('fds_meson', 'main.c')\n",
'/tmp/development/c/Makefile': 'fds_make: main.c\n\t$(CC) -g -O0 -o $@ $<\n',
'/tmp/development/rust/Cargo.toml': '[package]\nname="fds-development-test"\nversion="0.1.0"\nedition="2024"\n',
'/tmp/development/rust/src/main.rs': 'fn main() { println!("FDS RUST OK"); }\n',
}
for path, text in files.items():
# Serial input passes through interactive Bash readline. Encode the
# bytes so a Makefile tab cannot be consumed as tab completion.
encoded = base64.b64encode(text.encode()).decode('ascii')
vm.capture('printf %s ' + shlex.quote(encoded) + ' | base64 -d >' + shlex.quote(path))
vm.capture('cmake -S /tmp/development/c -B /tmp/development/cmake -G Ninja && cmake --build /tmp/development/cmake', timeout=600)
assert vm.capture('/tmp/development/cmake/fds_c') == 'FDS C OK'
assert vm.capture('/tmp/development/cmake/fds_cpp') == 'FDS C++ OK'
vm.capture('meson setup /tmp/development/meson /tmp/development/c && ninja -C /tmp/development/meson', timeout=600)
assert vm.capture('/tmp/development/meson/fds_meson') == 'FDS C OK'
vm.capture('make -C /tmp/development/c', timeout=180)
assert vm.capture('/tmp/development/c/fds_make') == 'FDS C OK'
vm.capture('cd /tmp/development/rust && cargo build --offline', timeout=600)
assert vm.capture('/tmp/development/rust/target/debug/fds-development-test') == 'FDS RUST OK'
debug = vm.capture("gdb -q -batch -ex 'break main' -ex run -ex bt -ex continue --args /tmp/development/c/fds_make", timeout=180)
assert 'Breakpoint 1' in debug and 'main' in debug and 'FDS C OK' in debug, debug
(work / 'gdb.txt').write_text(debug + '\n')
vm.capture('strace -o /tmp/development/strace.txt -e trace=write /tmp/development/c/fds_make && grep -q "FDS C OK" /tmp/development/strace.txt')
vm.capture('git -C /tmp/development init && git -C /tmp/development add c/main.c && git -C /tmp/development -c user.name=FDS -c user.email=test.invalid@example.invalid commit -m "Local development acceptance"')
vm.capture("vim -Nu NONE -n -es /tmp/development/editor.txt -c 'call setline(1, \"FDS EDITOR OK\")' -c wq")
assert vm.capture('cat /tmp/development/editor.txt') == 'FDS EDITOR OK'
vm.capture('pkg-config --version')
vm.capture('test "$(findmnt -nro FSTYPE /)" = erofs && findmnt -nro OPTIONS / | grep -qw ro')
vm.capture('touch /etc/development-write-probe', ok=False)
vm.capture('! pgrep -x Xorg && ! pgrep -x dhcpcd && ! pgrep -x sshd && ! pgrep -x dbus-daemon')
boot = json.loads(vm.capture('fds --json boot-profile'))
(work / 'boot-profile.json').write_text(json.dumps(boot, indent=2) + '\n')
deadline = time.monotonic() + 60
while vm.capture('test -S /run/fds/control.sock && echo ready || echo pending') != 'ready':
assert time.monotonic() < deadline
vm.send('fds poweroff')
vm.expect(rb'reboot: Power down')
assert vm.child.wait(timeout=20) == 0
assert digest(image) == before
link = project / 'out/m12-development-latest.next'
link.symlink_to(work.name)
link.replace(project / 'out/m12-development-latest')
print(f'PASS: native C/C++/Rust compilation and execution, CMake/Meson/Ninja/Make, GDB/strace, Git/editor, read-only SYSTEM and native shutdown: {work}')
print('SKIP: Pi compiler throughput and physical boot timing require hardware')