mirror of
https://github.com/borgbackup/borg.git
synced 2026-10-12 02:11:36 +00:00
The Linux binaries were about 70 MiB as a single file and unpacked to 152 MiB at every start, see #10345. Three changes: Strip the debug symbols on Linux: pythons built with the default CFLAGS carry -g, and the spec had strip=False everywhere, so about half of the Linux binaries was debug info. Enable PyInstaller's strip for the collected shared libraries on Linux (only there: on macOS it would interfere with code signing, and it is not recommended on Windows). Python tracebacks do not need the symbols, only gdb would. Measured on the glibc239 x86_64 bundle: 152 -> 78 MiB unpacked, 69.6 -> 43.4 MiB compressed. Bundle only the botocore service models borg needs: botocore (boto3, s3 extra) ships the JSON service models of all ~430 AWS services, 23 MiB, about 14 MiB in the compressed binary. borg only ever creates an s3 client - also for S3-compatible services like MinIO or Backblaze B2, which just get a different endpoint URL - so the spec keeps only the s3 model, the shared top-level files and the models the credential providers may need (sts, sso, sso-oidc). In the oldglibc_binary job, build cryptography (a paramiko dependency from the sftp extra) from source against the OpenSSL built there, instead of using the manylinux wheel that statically links its own copy of OpenSSL - the binary contained two OpenSSLs. The runner image has the Rust toolchain this needs. The job now checks that the bundled OpenSSL and Python libraries are the ones built there by their GNU build IDs (the files are stripped now), that cryptography links and loads that OpenSSL, that no bundled shared library still carries debug info, that the other botocore models are gone, prints the bundle sizes, and smoke-tests the frozen binary against a MinIO server - the test suite's S3 test runs borg from the venv, not the binary. Co-Authored-By: Claude Fable 5.1 <[email protected]>
117 lines
4.6 KiB
RPMSpec
117 lines
4.6 KiB
RPMSpec
# -*- mode: python -*-
|
|
# This PyInstaller spec file is used to build Borg binaries on POSIX platforms and Windows.
|
|
|
|
import os, sys
|
|
|
|
is_win32 = sys.platform.startswith('win32')
|
|
is_linux = sys.platform.startswith('linux')
|
|
|
|
# Strip the symbol tables and debug info from the collected shared libraries
|
|
# (libpython, the extension modules, libcrypto, ...) on Linux: pythons built
|
|
# with the default CFLAGS carry -g debug info, which makes up about half of
|
|
# the size of the Linux binaries, see #10345. Only on Linux: on macOS the
|
|
# strip would interfere with code signing, and it is not recommended on
|
|
# Windows. Python tracebacks do not need any of this, only gdb would.
|
|
strip_binaries = is_linux
|
|
|
|
# Note: SPEC contains the spec file argument given to pyinstaller
|
|
here = os.path.dirname(os.path.abspath(SPEC))
|
|
basepath = os.path.abspath(os.path.join(here, '..'))
|
|
|
|
if is_win32:
|
|
hiddenimports = ['borghash']
|
|
else:
|
|
hiddenimports = ['borg.platform.posix', 'borghash', 'rich._unicode_data.unicode17-0-0']
|
|
|
|
# Anything a compiled chunker module pulls in has to be listed here: the
|
|
# chunkers are Cython extensions, so PyInstaller's static analysis of the
|
|
# Python sources cannot see their imports at all. That covers both the base
|
|
# classes they cimport (ChunkerBase, and ChunkerPHTE for the *-aes chunkers),
|
|
# which are imported at C level during module init, and plain Python modules
|
|
# imported from a .pyx, such as kernel_env - it is imported ONLY from .pyx
|
|
# files, so nothing else would drag it in.
|
|
hiddenimports.append('borg.chunkers.base')
|
|
hiddenimports.append('borg.chunkers.phte_chunker')
|
|
hiddenimports.append('borg.chunkers.kernel_env')
|
|
|
|
block_cipher = None
|
|
|
|
a = Analysis([os.path.join(basepath, 'src', 'borg', '__main__.py'), ],
|
|
pathex=[basepath, ],
|
|
binaries=[],
|
|
datas=[
|
|
(os.path.join(basepath, 'src', 'borg', 'paperkey.html'), 'borg'),
|
|
(os.path.join(basepath, 'src', 'borg', 'cockpit', 'cockpit.tcss'), os.path.join('borg', 'cockpit')),
|
|
],
|
|
hiddenimports=hiddenimports,
|
|
hookspath=[],
|
|
runtime_hooks=[],
|
|
excludes=[
|
|
# '_ssl', 'ssl', # do not exclude these, needed for pyfuse3/trio
|
|
'pkg_resources', # avoid pkg_resources related warnings
|
|
],
|
|
win_no_prefer_redirects=False,
|
|
win_private_assemblies=False,
|
|
cipher=block_cipher)
|
|
|
|
if sys.platform == 'darwin':
|
|
# do not bundle the osxfuse libraries, so we do not get a version
|
|
# mismatch to the installed kernel driver of osxfuse.
|
|
a.binaries = [b for b in a.binaries if 'libosxfuse' not in b[0]]
|
|
|
|
# botocore (boto3, s3 extra) ships the JSON service models of all ~430 AWS
|
|
# services (23 MiB, ~14 MiB in the compressed binary, see #10345), but borg
|
|
# only ever creates an s3 client - also for S3-compatible services like MinIO
|
|
# or Backblaze B2, which just get a different endpoint URL. Keep the s3 model,
|
|
# the shared top-level files (endpoints.json, partitions.json, ...) and the
|
|
# models the credential providers may need: sts (assume-role and web-identity
|
|
# profiles), sso and sso-oidc (SSO profiles). Everything else is dead weight.
|
|
botocore_needed_services = ('s3', 'sts', 'sso', 'sso-oidc')
|
|
|
|
|
|
def is_unneeded_botocore_model(dest_name):
|
|
parts = os.path.normpath(dest_name).split(os.sep)
|
|
if parts[:2] != ['botocore', 'data'] or len(parts) < 4:
|
|
# not a service model - e.g. a shared top-level file like botocore/data/endpoints.json
|
|
return False
|
|
return parts[2] not in botocore_needed_services
|
|
|
|
|
|
a.datas = [d for d in a.datas if not is_unneeded_botocore_model(d[0])]
|
|
|
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
|
|
exe = EXE(pyz,
|
|
a.scripts,
|
|
a.binaries,
|
|
a.zipfiles,
|
|
a.datas,
|
|
name='borg.exe',
|
|
debug=False,
|
|
strip=strip_binaries,
|
|
upx=True,
|
|
console=True,
|
|
icon='NONE')
|
|
|
|
# Build a directory-based binary in addition to a packed
|
|
# single-file binary. This allows you to look at all included
|
|
# files easily (e.g., without having to strace or halt the built binary
|
|
# and introspect /tmp). It also avoids unpacking all libraries when
|
|
# running the app, which is better for application signing on various OSes.
|
|
slim_exe = EXE(pyz,
|
|
a.scripts,
|
|
exclude_binaries=True,
|
|
name='borg.exe',
|
|
debug=False,
|
|
strip=strip_binaries,
|
|
upx=False,
|
|
console=True)
|
|
|
|
coll = COLLECT(slim_exe,
|
|
a.binaries,
|
|
a.zipfiles,
|
|
a.datas,
|
|
strip=strip_binaries,
|
|
upx=False,
|
|
name='borg-dir')
|