diff --git a/src/Build/Tools/makeself_repro_finalize.py b/src/Build/Tools/makeself_repro_finalize.py index 9514211f..159503d1 100755 --- a/src/Build/Tools/makeself_repro_finalize.py +++ b/src/Build/Tools/makeself_repro_finalize.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # # Copyright (c) 2026 VeraCrypt # Governed by the Apache License 2.0. @@ -14,6 +14,8 @@ # CRCsum makes its extractor skip the redundant CRC check. # - MD5 is recomputed, which the extractor still verifies. # +# Compatible with Python >= 2.6 and Python 3. +# # Usage: makeself_repro_finalize.py import hashlib @@ -23,28 +25,27 @@ import sys def finalize(path): with open(path, "rb") as f: - raw = bytearray(f.read()) - text = raw.decode("latin1", errors="replace") + raw = f.read() + text = raw.decode("latin1") # Locate payload start by line count, mirroring makeself's own extractor. m = re.search(r'^skip="(\d+)"', text, re.MULTILINE) if not m: - sys.exit(f"{path}: no skip= line in makeself header") + sys.exit("%s: no skip= line in makeself header" % path) skip = int(m.group(1)) header_text = "\n".join(text.split("\n")[:skip]) + "\n" offset = len(header_text.encode("latin1")) - if bytes(raw[offset:offset + 3]) != b"\x1f\x8b\x08": - sys.exit(f"{path}: no gzip magic at payload offset {offset}") + if raw[offset:offset + 3] != b"\x1f\x8b\x08": + sys.exit("%s: no gzip magic at payload offset %d" % (path, offset)) # gzip header mtime: 4-byte LE uint at offset+4 (RFC 1952 section 2.3.1). - raw[offset + 4:offset + 8] = b"\x00\x00\x00\x00" - payload = bytes(raw[offset:]) + payload = raw[offset:offset + 4] + b"\x00\x00\x00\x00" + raw[offset + 8:] new_md5 = hashlib.md5(payload).hexdigest() # CRCsum -> all zeros (extractor then skips the CRC check); MD5 -> fresh. new_header = re.sub(r'CRCsum="[^"]*"', 'CRCsum="0000000000"', header_text) - new_header = re.sub(r'MD5="[0-9a-fA-F]+"', f'MD5="{new_md5}"', new_header) + new_header = re.sub(r'MD5="[0-9a-fA-F]+"', 'MD5="%s"' % new_md5, new_header) new_bytes = new_header.encode("latin1") # Line count must stay the same so makeself's "skip=" remains accurate. if new_bytes.count(b"\n") != skip: - sys.exit(f"{path}: header line count changed during rewrite") + sys.exit("%s: header line count changed during rewrite" % path) with open(path, "wb") as f: f.write(new_bytes + payload) diff --git a/src/Main/Main.make b/src/Main/Main.make index d90d40d3..faa25fbb 100755 --- a/src/Main/Main.make +++ b/src/Main/Main.make @@ -319,6 +319,7 @@ TAR_DETERMINISTIC := $(strip $(shell t=$$(mktemp 2>/dev/null) && tar --sor GZIP_NO_TIMESTAMP := $(strip $(shell printf x | gzip -n -c >/dev/null 2>&1 && echo yes)) MAKESELF_PACKAGING_DATE := $(strip $(shell makeself --help 2>&1 | grep -q -- '--packaging-date' && echo yes)) MAKESELF_TAR_EXTRA := $(strip $(shell makeself --help 2>&1 | grep -q -- '--tar-extra' && echo yes)) +PYTHON_REPRO_FINALIZE := $(strip $(shell for p in python3 python python2; do command -v $$p >/dev/null 2>&1 || continue; $$p -c 'import sys; v=sys.version_info; sys.exit(not (v[0] > 2 or (v[0] == 2 and v[1] >= 6)))' >/dev/null 2>&1 && command -v $$p && break; done)) INSTALL_UNINSTALLER ?= 1 INSTALL_LICENSE ?= 1 @@ -631,11 +632,11 @@ endif # makeself runs 'gzip -c9 < tmpfile' which writes tmpfile's mtime into # the gzip header (SOURCE_DATE_EPOCH is ignored for redirected stdin). # Zero the mtime and refresh CRCsum/MD5; installer --check still passes. - @if command -v python3 >/dev/null 2>&1; then \ - python3 $(BASE_DIR)/Build/Tools/makeself_repro_finalize.py \ - $(BASE_DIR)/Setup/Linux/$(INSTALLER_NAME); \ + @if [ -n "$(PYTHON_REPRO_FINALIZE)" ]; then \ + "$(PYTHON_REPRO_FINALIZE)" "$(BASE_DIR)/Build/Tools/makeself_repro_finalize.py" \ + "$(BASE_DIR)/Setup/Linux/$(INSTALLER_NAME)"; \ else \ - echo "Reproducible build: python3 unavailable, skipping makeself finalize"; \ + echo "Reproducible build: Python 2.6+ unavailable, skipping makeself finalize"; \ fi appimage: prepare