85 lines
5.4 KiB
Python
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')
|