61 lines
3.5 KiB
Python
Executable File
61 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Build a deterministic Rust-only initramfs with the required early display daemon."""
|
|
import argparse
|
|
import gzip
|
|
import json
|
|
from pathlib import Path
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
project = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(project/'tools'))
|
|
from image_formats import digest, newc
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--stage0', type=Path, default=project/'out/fds-stage0')
|
|
parser.add_argument('--output-directory', type=Path)
|
|
args = parser.parse_args()
|
|
payloads = {'fds-stage0': args.stage0.resolve(strict=True), 'dasungd': project/'out/dasungd'}
|
|
|
|
for tool in ('lz4', 'zstd'):
|
|
if not shutil.which(tool): sys.exit(f'ERROR: missing host {tool}; see docs/developer/m4-work.md')
|
|
for executable in ('fds-stage0', 'dasungd'):
|
|
subprocess.run([str(project/'tools/verify-elf'), str(payloads[executable]), 'aarch64', 'static'], check=True)
|
|
epoch = int(subprocess.check_output(['git', '-C', str(project/'vendor/void-packages'), 'show', '-s', '--format=%ct', 'HEAD']))
|
|
work = args.output_directory or Path(tempfile.mkdtemp(prefix='initramfs-build.', dir=project/'out'))
|
|
if not work.is_dir() or any(work.iterdir()): parser.error('output directory must exist and be empty')
|
|
entries = []
|
|
for name in ['dev', 'proc', 'sys', 'newroot', 'sbin', 'etc', 'usr', 'usr/lib', 'usr/lib/firmware', 'usr/lib/firmware/edid']:
|
|
entries.append((name, stat.S_IFDIR | 0o755, b'', 0, 0))
|
|
entries.append(('dev/console', stat.S_IFCHR | 0o600, b'', 5, 1))
|
|
entries.append(('init', stat.S_IFLNK | 0o777, b'sbin/fds-stage0', 0, 0))
|
|
entries.append(('lib', stat.S_IFLNK | 0o777, b'usr/lib', 0, 0))
|
|
for executable in ('fds-stage0', 'dasungd'):
|
|
entries.append(('sbin/'+executable, stat.S_IFREG | 0o755, payloads[executable].read_bytes(), 0, 0))
|
|
config = (project/'packages/fds-dasungd/files/dasungd.toml').read_text().replace('/run/dasungd/', '/dev/fds-early/')
|
|
entries.append(('etc/dasungd-early.toml', stat.S_IFREG | 0o600, config.encode(), 0, 0))
|
|
entries.append(('usr/lib/firmware/edid/dasung-paperlike13k-37hz.bin', stat.S_IFREG | 0o644,
|
|
(project/'rust/dasungd/profiles/paperlike13k-37hz.edid').read_bytes(), 0, 0))
|
|
archive = newc(sorted(entries), epoch)
|
|
(work/'initramfs.cpio').write_bytes(archive)
|
|
(work/'initramfs.cpio.gz').write_bytes(gzip.compress(archive, compresslevel=9, mtime=0))
|
|
for tool, options, suffix in [('lz4', ['-q', '-l', '-9', '-c'], 'lz4'), ('zstd', ['-q', '-T1', '-19', '-c'], 'zst')]:
|
|
with (work/f'initramfs.cpio.{suffix}').open('wb') as stream:
|
|
subprocess.run([tool, *options], input=archive, stdout=stream, check=True)
|
|
# All variants must decode to the exact same cpio stream.
|
|
assert gzip.decompress((work/'initramfs.cpio.gz').read_bytes()) == archive
|
|
for tool, suffix in [('lz4', 'lz4'), ('zstd', 'zst')]:
|
|
assert subprocess.check_output([tool, '-q', '-d', '-c', str(work/f'initramfs.cpio.{suffix}')]) == archive
|
|
manifest = {path.name: {'bytes': path.stat().st_size, 'sha256': digest(path)} for path in sorted(work.glob('initramfs.cpio*'))}
|
|
(work/'formats.json').write_text(json.dumps(manifest, indent=2)+'\n')
|
|
for target, source in ([] if args.output_directory else [('initramfs', work.name), ('fds-initramfs.img', work.name+'/initramfs.cpio')]):
|
|
temporary = project/'out'/(target+'.next')
|
|
temporary.symlink_to(source)
|
|
temporary.replace(project/'out'/target)
|
|
print('PASS: uncompressed, gzip, legacy-LZ4 and Zstandard initramfs variants agree')
|
|
print(f'Initramfs artifacts: {work}')
|
|
print('SKIP: compression boot-time comparison requires the physical Pi')
|