#!/usr/bin/env python3 """Pack the vendored CPython 2.7.18 Lib/ into the python27.zip the host puts on sys.path. make_stdlib_zip.py 40250 shipped a real python27 installation, so `import encodings` found Lib/ through the interpreter's prefix. The embedded interpreter has no prefix (Py_FrozenFlag, no site), so the host supplies the stdlib as this one file — the arrangement validated on the device by tools/py_embed_android/build-and-run.sh (PORT-PLAN 批次 2P step 3). Entries are STORED, never deflated: the zlib module is not in the vendored Modules/ (see docs/THIRD-PARTY.md), so zipimport could not inflate them. Only .py goes in; zipimport reads source as happily as bytecode, and .pyc would pin the magic number to the build host's interpreter. Written deterministically (sorted, fixed timestamp) so the file's sha256 identifies its contents. The hash is written next to the zip as .sha256 and ships with it: on Android/iOS the zip lives in the read-only bundle and has to be copied into the sandbox before CPython can open it, and that copy is accepted only when it hashes to this value (extension/src/python_stdlib.cpp, step 3c). """ from __future__ import annotations import hashlib import sys import zipfile from pathlib import Path # Test suites, the Tk/2to3/bsddb/pip trees and the platform-specific stubs: none of the 40250 scripts # or their imports reach them, and they are most of the size. SKIP_DIRS = {"test", "tests", "lib-tk", "idlelib", "lib2to3", "bsddb", "ensurepip", "curses", "distutils", "unittest", "wsgiref", "multiprocessing", "compiler"} def main() -> int: if len(sys.argv) != 3: print(__doc__, file=sys.stderr) return 2 lib, out = Path(sys.argv[1]), Path(sys.argv[2]) if not (lib / "os.py").is_file(): print(f"make_stdlib_zip: {lib} is not a CPython Lib/ directory", file=sys.stderr) return 1 members = [] for path in sorted(lib.rglob("*.py")): rel = path.relative_to(lib) if rel.parts[0].startswith("plat-") or SKIP_DIRS.intersection(rel.parts[:-1]): continue members.append((rel, path)) out.parent.mkdir(parents=True, exist_ok=True) tmp = out.with_suffix(".zip.tmp") with zipfile.ZipFile(tmp, "w", zipfile.ZIP_STORED) as zf: for rel, path in members: info = zipfile.ZipInfo(rel.as_posix(), date_time=(1980, 1, 1, 0, 0, 0)) info.external_attr = 0o644 << 16 zf.writestr(info, path.read_bytes()) tmp.replace(out) digest = hashlib.sha256(out.read_bytes()).hexdigest() out.with_name(out.name + ".sha256").write_text(digest + "\n") print(f"make_stdlib_zip: {out} ({len(members)} modules, {out.stat().st_size} bytes, sha256 {digest})") return 0 if __name__ == "__main__": raise SystemExit(main())