mirror of
https://github.com/okhsunrog/vpnhide.git
synced 2026-05-02 00:22:14 +00:00
Follow-up to #83. Five small fixes I caught while reviewing: - build_lib: spell out the stdlib-only invariant in the module docstring. build-version.py is called from app/build.gradle.kts on every Gradle build, so adding pip/uv deps here would break the APK build. - build_lib: drop unused get_python_exe() (no callers anywhere). - build_lib: add version_sort_key() and use it in zygisk and kmod for NDK / clang auto-detection. The previous lexicographic sorted() picked the wrong directory when major versions span different digit widths (e.g. 100.0.0 < 25.0.1, clang-r9 sorting after clang-r498344b). zygisk/build-zip.sh used `sort -V` before the python port, so this is a regression fix; kmod is a new safety net (DDK containers ship one clang today, but auto-detect should still be correct). - kmod/build-zip.py: drop the manual mtime check before `make strip`. The check only watched vpnhide_kmod.c, so edits to the Makefile, kernel headers, or .config wouldn't trigger a rebuild. Let make's own dependency tracking decide. - build_lib: minor cleanup — hoist `import subprocess` to the top of the module instead of importing it inside get_build_version().
167 lines
5.9 KiB
Python
Executable file
167 lines
5.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Build and package the KernelSU/Magisk kernel module zip.
|
|
|
|
Assembles a module staging directory so the committed module.prop stays at
|
|
its release version while the zip carries the actual build version (git describe).
|
|
|
|
Usage:
|
|
python3 build-zip.py --kdir /path/to/kdir --kmi android14-5.15 # explicit args
|
|
python3 build-zip.py --kdir /path/to/kdir --kmi android14-5.15 --out custom.zip # custom output
|
|
# Or use environment variables: KDIR and KMI (CLI args override env vars)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
|
|
from build_lib import get_build_version, make_zip, version_sort_key # type: ignore[import-not-found]
|
|
|
|
|
|
# Module file names
|
|
KMOD_C = "vpnhide_kmod.c"
|
|
KMOD_KO = "vpnhide_kmod.ko"
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Build and package the kernel module zip.")
|
|
parser.add_argument(
|
|
"--kdir",
|
|
type=str,
|
|
help="Kernel source directory (overrides KDIR or KERNEL_SRC env var)",
|
|
)
|
|
parser.add_argument(
|
|
"--kmi",
|
|
type=str,
|
|
help="Kernel module interface variant (e.g., android14-5.15) for module.prop (overrides KMI env var)",
|
|
)
|
|
parser.add_argument(
|
|
"--out",
|
|
type=str,
|
|
help="Output zip filename (default: vpnhide-kmod-<kmi>.zip)",
|
|
)
|
|
parser.add_argument(
|
|
"--clang-dir",
|
|
type=str,
|
|
help="Clang binaries directory (overrides CLANG_DIR env var or auto-detects)",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
kmod_dir = Path(__file__).resolve().parent
|
|
os.chdir(kmod_dir)
|
|
|
|
# Resolve kdir: CLI arg > KDIR env var > KERNEL_SRC env var
|
|
if args.kdir:
|
|
kdir = args.kdir
|
|
kdir_src = "--kdir CLI argument"
|
|
else:
|
|
kdir = os.environ.get("KDIR")
|
|
kdir_src = "KDIR env var"
|
|
if not kdir:
|
|
kdir = os.environ.get("KERNEL_SRC")
|
|
kdir_src = "KERNEL_SRC env var"
|
|
if not kdir:
|
|
print("Error: --kdir argument, KDIR, or KERNEL_SRC env var is required", file=sys.stderr)
|
|
return 1
|
|
|
|
print(f"Using kdir from {kdir_src}: {kdir}")
|
|
|
|
# Resolve kmi: CLI arg > KMI env var
|
|
if args.kmi:
|
|
kmi = args.kmi
|
|
kmi_src = "--kmi CLI argument"
|
|
else:
|
|
kmi = os.environ.get("KMI")
|
|
kmi_src = "KMI env var"
|
|
if not kmi:
|
|
print("Error: --kmi argument or KMI env var is required", file=sys.stderr)
|
|
return 1
|
|
|
|
print(f"Using kmi from {kmi_src}: {kmi}")
|
|
|
|
# Set up environment for make
|
|
os.environ["KERNEL_SRC"] = kdir
|
|
|
|
# Resolve clang_dir: CLI arg > CLANG_DIR env var > auto-detect
|
|
clang_dir = None
|
|
clang_dir_src = None
|
|
if args.clang_dir:
|
|
clang_dir = args.clang_dir
|
|
clang_dir_src = "--clang-dir CLI argument"
|
|
else:
|
|
clang_dir = os.environ.get("CLANG_DIR")
|
|
if clang_dir:
|
|
clang_dir_src = "CLANG_DIR env var"
|
|
else:
|
|
# Auto-detect: In CI, clang is at /opt/ddk/clang/clang-r*/bin
|
|
clang_base = Path("/opt/ddk/clang")
|
|
if clang_base.exists():
|
|
clang_dirs = sorted(
|
|
(d for d in clang_base.iterdir() if d.is_dir() and d.name.startswith("clang-")),
|
|
key=lambda p: version_sort_key(p.name),
|
|
)
|
|
if clang_dirs:
|
|
clang_dir = str(clang_dirs[-1] / "bin")
|
|
clang_dir_src = "auto-detected from /opt/ddk/clang"
|
|
if clang_dir:
|
|
os.environ["CLANG_DIR"] = clang_dir
|
|
print(f"Using clang-dir from {clang_dir_src}: {clang_dir}")
|
|
else:
|
|
print("Warning: clang-dir not set, using system PATH", file=sys.stderr)
|
|
|
|
# Build the kernel module — let make decide whether anything needs
|
|
# rebuilding; its dependency tracking covers all sources, headers,
|
|
# and the kernel .config, not just vpnhide_kmod.c.
|
|
print("Building kernel module...")
|
|
subprocess.run(["make", "strip"], check=True)
|
|
|
|
# Assemble the module staging directory so the committed module.prop
|
|
# stays at its release version while the zip carries the actual build
|
|
# version (git describe).
|
|
staging = kmod_dir / "module-staging"
|
|
if staging.exists():
|
|
shutil.rmtree(staging)
|
|
shutil.copytree(kmod_dir / "module", staging)
|
|
shutil.copy(kmod_dir / KMOD_KO, staging / KMOD_KO)
|
|
|
|
# Get build version
|
|
build_version = get_build_version(kmod_dir.parent)
|
|
|
|
# Stamp version into module.prop
|
|
module_prop = staging / "module.prop"
|
|
content = module_prop.read_text(encoding="utf-8")
|
|
content = re.sub(r"^version=.*", f"version=v{build_version}", content, flags=re.MULTILINE)
|
|
# Add gkiVariant and updateJson
|
|
content = re.sub(r"^gkiVariant=.*", f"gkiVariant={kmi}", content, flags=re.MULTILINE)
|
|
if not re.search(r"^gkiVariant=", content, flags=re.MULTILINE):
|
|
content = content.rstrip() + f"\ngkiVariant={kmi}\n"
|
|
update_json_url = f"https://raw.githubusercontent.com/okhsunrog/vpnhide/main/update-json/update-kmod-{kmi}.json"
|
|
content = re.sub(r"^updateJson=.*", f"updateJson={update_json_url}", content, flags=re.MULTILINE)
|
|
if not re.search(r"^updateJson=", content, flags=re.MULTILINE):
|
|
content = content.rstrip() + f"\nupdateJson={update_json_url}\n"
|
|
module_prop.write_text(content, encoding="utf-8")
|
|
print(f"Stamped module.prop version=v{build_version} gkiVariant={kmi}")
|
|
|
|
# Create zip in parent directory (workspace root for CI)
|
|
out_zip = kmod_dir.parent / (args.out if args.out else f"vpnhide-kmod-{kmi}.zip")
|
|
if out_zip.exists():
|
|
out_zip.unlink()
|
|
|
|
make_zip(staging, out_zip)
|
|
shutil.rmtree(staging)
|
|
|
|
print()
|
|
print(f"Built: {out_zip.name}")
|
|
size_kb = out_zip.stat().st_size / 1024
|
|
print(f" {out_zip} ({size_kb:.1f} KB)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|