Files
gen1recomp/tools/winbuild/zip_impl.py
T
Solidus Snake a9767e5df1 Add native TLS for Android love.system and desktop gen1tls.
Expose a non-blocking TLS socket API to mods (WSS clients) without bundling
any game-specific multiworld content.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 17:43:53 -04:00

112 lines
4.0 KiB
Python

"""Enough of Info-ZIP's `zip` for scripts/build_android.sh, on Windows.
Git for Windows ships unzip but not zip, which is the one tool standing between
a Windows checkout and a local Android build. Rather than fetching a binary
from somewhere unaudited, this covers exactly the two forms the build script
uses and refuses anything it does not understand, so a future flag fails loudly
instead of silently producing a wrong archive:
zip -q -9 -r out.love main.lua src data -x '*.DS_Store' -x 'data/generated/*'
zip -q out.love src/core/Version.lua # add or replace one entry
Paths are stored relative to the working directory with forward slashes, and
directories are walked in sorted order so the archive is reproducible.
"""
import fnmatch
import os
import sys
import zipfile
def excluded(name, patterns):
# zip's wildcards cross directory separators, which is also what fnmatch
# does, so the build script's '*/.git/*' behaves the same either way.
return any(fnmatch.fnmatch(name, pattern) for pattern in patterns)
def collect(paths, patterns):
"""-> archive-relative names, in a stable order."""
names = []
for path in paths:
if os.path.isdir(path):
for root, dirs, files in os.walk(path):
dirs.sort()
for f in sorted(files):
full = os.path.join(root, f)
name = os.path.relpath(full, ".").replace(os.sep, "/")
if not excluded(name, patterns):
names.append(name)
elif os.path.isfile(path):
name = os.path.relpath(path, ".").replace(os.sep, "/")
if not excluded(name, patterns):
names.append(name)
else:
sys.stderr.write("zip: %s not found\n" % path)
return None
return names
def main(argv):
patterns, paths, archive = [], [], None
i = 0
while i < len(argv):
arg = argv[i]
if arg == "-x":
# Info-ZIP accepts one -x followed by several patterns, which is how
# pack_love.sh and build.sh write their excludes. Consume every
# following non-flag argument as a pattern; paths always come before
# -x in those scripts, so this does not steal archive members.
i += 1
if i >= len(argv) or argv[i].startswith("-"):
sys.stderr.write("zip: -x needs a pattern\n")
return 2
while i < len(argv) and not argv[i].startswith("-"):
patterns.append(argv[i])
i += 1
continue
elif arg in ("-q", "-9", "-r", "-X", "-o"):
pass # quiet, compression level, recurse, no-extra, ordering
elif arg.startswith("-"):
sys.stderr.write("zip shim: unsupported flag %s\n" % arg)
return 2
elif archive is None:
archive = arg
else:
paths.append(arg)
i += 1
if archive is None or not paths:
sys.stderr.write("usage: zip [-q9r] [-x pat] archive path...\n")
return 2
names = collect(paths, patterns)
if names is None:
return 1
# Adding to an existing archive means rewriting it: zipfile can append, but
# appending a name that is already in there leaves both copies and readers
# disagree about which one wins. The version stamp does exactly that.
keep = []
if os.path.exists(archive):
replacing = set(names)
with zipfile.ZipFile(archive, "r") as old:
for info in old.infolist():
if info.filename not in replacing:
keep.append((info, old.read(info.filename)))
tmp = archive + ".shimtmp"
with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as out:
for info, data in keep:
out.writestr(info, data)
for name in names:
out.write(name, name)
if os.path.exists(archive):
os.remove(archive)
os.rename(tmp, archive)
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))