Phase 20: package Linux builds and document release and maintenance workflows
This commit is contained in:
parent
6b85f28add
commit
830b6e1914
22 changed files with 490 additions and 17 deletions
76
tools/package.py
Normal file
76
tools/package.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#!/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()
|
||||
|
|
@ -8,7 +8,8 @@ import stat
|
|||
def owned_files(source):
|
||||
files = ['lib/libxtc.a', 'lib/pkgconfig/xtc.pc', 'bin/xtc-check', 'bin/xtc',
|
||||
'share/applications/xtc.desktop', 'share/licenses/xtc/LICENSE',
|
||||
'share/doc/xtc/README.md']
|
||||
'share/doc/xtc/README.md', 'share/doc/xtc/performance-baseline.json',
|
||||
'share/doc/xtc/BINARY_MANIFEST.json']
|
||||
files += ['include/xtc/' + p.name for p in sorted((source / 'include/xtc').glob('*.h'))]
|
||||
files += ['share/doc/xtc/' + p.name for p in sorted((source / 'docs').glob('*.md'))]
|
||||
files += ['share/doc/xtc/decisions/' + p.name for p in sorted((source / 'docs/decisions').glob('*.md'))]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue