1
0
Fork 0
forked from hanez/xtc
xtc/tools/package.py

76 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Stage a Linux binary/development archive without modifying a live prefix."""
import argparse
import gzip
import hashlib
import io
import json
import os
from pathlib import Path
import platform
import re
import shlex
import subprocess
import tarfile
import tempfile
from dist import version
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--build-dir', type=Path, required=True)
p.add_argument('--output', type=Path, required=True)
p.add_argument('--gui', action='store_true')
a = p.parse_args()
epoch = int(os.environ.get('SOURCE_DATE_EPOCH', '0'))
if not 0 <= epoch <= 4294967295:
p.error('SOURCE_DATE_EPOCH must fit uint32')
name = f'xtc-{version(Path.cwd())}-linux-{platform.machine()}'
a.output.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix='xtc-binary-stage-') as temporary:
stage = Path(temporary)
env = dict(os.environ, MAKEFLAGS='', MFLAGS='')
subprocess.run(shlex.split(env.get('MAKE', 'make')) + ['install-gui' if a.gui else 'install',
f'BUILD_DIR={a.build_dir.resolve()}', f'DESTDIR={stage}', 'PREFIX=/usr'], env=env, check=True)
pc = stage / 'usr/lib/pkgconfig/xtc.pc'
pc.write_text(pc.read_text().replace('prefix=/usr\n', 'prefix=${pcfiledir}/../..\n'))
entries = []
needed = {}
for path in sorted(stage.rglob('*')):
if not path.is_file():
continue
assert not path.is_symlink()
relative = path.relative_to(stage).as_posix()
entries.append(dict(path=relative, size=path.stat().st_size,
sha256=hashlib.sha256(path.read_bytes()).hexdigest()))
if relative in ('usr/bin/xtc', 'usr/bin/xtc-check'):
dynamic = subprocess.check_output(['readelf', '-d', str(path)], text=True)
needed[relative] = re.findall(r'\(NEEDED\).*\[(.*?)\]', dynamic)
manifest = dict(schema=1, version=version(Path.cwd()), architecture=platform.machine(),
libc=list(platform.libc_ver()), needed=needed, files=entries,
scope='System-linked Linux build; no libraries, plugins or media bundled.')
(stage / 'usr/share/doc/xtc/BINARY_MANIFEST.json').write_text(json.dumps(manifest, indent=2) + '\n')
fd, pending = tempfile.mkstemp(prefix='.xtc-package-', dir=a.output.parent)
try:
with os.fdopen(fd, 'wb') as raw, gzip.GzipFile(fileobj=raw, filename='', mode='wb', mtime=epoch) as gz:
with tarfile.open(fileobj=gz, mode='w', format=tarfile.PAX_FORMAT) as tar:
for path in sorted(stage.rglob('*')):
if not path.is_file():
continue
data = path.read_bytes()
info = tarfile.TarInfo(name + '/' + path.relative_to(stage).as_posix())
info.size = len(data)
info.mode = 0o755 if os.access(path, os.X_OK) else 0o644
info.mtime = epoch
tar.addfile(info, io.BytesIO(data))
os.replace(pending, a.output)
finally:
if os.path.exists(pending):
os.unlink(pending)
sha = hashlib.sha256(a.output.read_bytes()).hexdigest()
a.output.with_name(a.output.name + '.sha256').write_text(f'{sha} {a.output.name}\n')
print(f'{a.output}: {sha}')
if __name__ == '__main__':
main()