portable_fixture.py (2052B)
1 #!/usr/bin/env python3 2 """Build a relocated Python/glibc test OS, separate from the distributed library.""" 3 import argparse 4 import shutil 5 import subprocess 6 from pathlib import Path 7 from portable import dependencies 8 9 def is_elf(path): 10 if not path.is_file(): 11 return False 12 with path.open('rb') as stream: 13 return stream.read(4) == b'\x7fELF' 14 15 16 p = argparse.ArgumentParser(description=__doc__) 17 p.add_argument('--python', type=Path, required=True) 18 p.add_argument('--stdlib', type=Path, required=True) 19 p.add_argument('--loader', type=Path, required=True) 20 p.add_argument('--output', type=Path, required=True) 21 a = p.parse_args() 22 root = a.output 23 (root/'python/bin').mkdir(parents=True) 24 (root/'system/lib').mkdir(parents=True) 25 shutil.copytree(a.stdlib, root/'python/lib'/a.stdlib.name, 26 ignore=shutil.ignore_patterns('__pycache__', 'site-packages', 'test', 'tests', 'config-*')) 27 shutil.copyfile(a.python.resolve(), root/'python/bin/python3') 28 elfs = [path for path in (root/'python').rglob('*') 29 if is_elf(path)] 30 native = {} 31 for path in elfs: 32 path.chmod(0o755) 33 for name, original in dependencies(path).items(): 34 if name in native and native[name] != original: 35 raise RuntimeError(f'ambiguous test-system dependency: {name}') 36 native[name] = original 37 # Supply the test OS's full glibc surface, including librt used by the RTS. 38 for path in a.loader.parent.glob('*.so*'): 39 if is_elf(path): 40 native[path.name] = path.resolve() 41 native[a.loader.name] = a.loader 42 for name, original in native.items(): 43 target = root/'system/lib'/name 44 shutil.copyfile(original, target) 45 elfs.append(target) 46 for path in elfs: 47 path.chmod(0o755) 48 # Preserve the loader's own ELF layout; only loaded objects need RUNPATH. 49 if path.name != a.loader.name: 50 subprocess.run(['patchelf', '--set-rpath', '/system/lib', str(path)], check=True) 51 subprocess.run(['patchelf', '--set-interpreter', '/system/lib/'+a.loader.name, 52 str(root/'python/bin/python3')], check=True)