#!/usr/bin/env python3 """ Extract listed ninca-crk-* lib jars from fat jar, CFR-decompile each into maven-ninca-crk-from-lib//, emit parent + child POMs (embedded pom from jar when present, sanitized parent). """ from __future__ import annotations import os import re import shutil import subprocess import sys import tempfile import zipfile from pathlib import Path FAT_JAR_DEFAULT = Path( "/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/" "星河湾星中星/星中心/ninca_crk_std_01-ninca_crk_std_backend/ninca-crk-std-backend-V2.9.2_20210730.jar" ) REPO_ROOT = Path(__file__).resolve().parents[1] OUT_ROOT = REPO_ROOT / "maven-ninca-crk-from-lib" CFR_JAR = Path("/tmp/cfr-0.152.jar") TARGET_NAMES = [ "ninca-crk-access-control-biz-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-access-control-common-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-access-control-data-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-access-control-facade-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-access-control-interface-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-access-control-service-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-access-control-web-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-biz-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-common-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-data-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-facade-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-interface-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-service-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-conference-attendance-web-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-biz-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-common-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-data-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-facade-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-interface-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-service-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-smart-attendance-web-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-biz-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-common-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-data-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-facade-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-interface-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-service-2.9.1_210630-SNAPSHOT.jar", "ninca-crk-visitor-management-web-2.9.1_210630-SNAPSHOT.jar", ] GROUP_ID = "cn.cloudwalk.ninca" REACTOR_VERSION = "2.9.1_210630-SNAPSHOT" JAVA_VERSION = "1.8" def artifact_dir(jar_name: str) -> str: return jar_name.replace(".jar", "").replace("-2.9.1_210630-SNAPSHOT", "") def ensure_cfr() -> Path: if CFR_JAR.is_file(): return CFR_JAR raise SystemExit( "Missing CFR at {} — download:\n" " curl -fsSL -o {} " "https://github.com/leibnitz27/cfr/releases/download/0.152/cfr-0.152.jar".format(CFR_JAR, CFR_JAR) ) def extract_embedded_pom(zf: zipfile.ZipFile, inner_path: str) -> str | None: try: return zf.read(inner_path).decode("utf-8", errors="replace") except KeyError: return None def find_embedded_pom_xml(zf: zipfile.ZipFile) -> tuple[str | None, str | None]: """Return (pom_xml_content, relative_path_in_zip).""" for n in zf.namelist(): if n.endswith("/pom.xml") and "/META-INF/maven/" in n and "/ninca-crk-" in n: return extract_embedded_pom(zf, n), n return None, None def sanitize_pom_xml(raw: str, artifact_id: str) -> str: """Remove embedded parent; attach reactor parent after modelVersion.""" raw = raw.lstrip("\ufeff").strip() raw = re.sub(r"]*>[\s\S]*?\s*", "", raw, count=1, flags=re.I) parent_block = ( " \n" " %s\n" " ninca-crk-from-lib-reactor\n" " %s\n" " ../pom.xml\n" " \n" % (GROUP_ID, REACTOR_VERSION) ) m = re.search(r"\s*", raw, flags=re.I) if not m: raise ValueError("embedded pom missing modelVersion") pos = m.end() return raw[:pos] + parent_block + raw[pos:] def minimal_pom(artifact_id: str, description: str) -> str: return """ 4.0.0 {gid} ninca-crk-from-lib-reactor {ver} ../pom.xml {aid} {aid} {desc} {jv} {jv} UTF-8 """.format( gid=GROUP_ID, ver=REACTOR_VERSION, aid=artifact_id, desc=description, jv=JAVA_VERSION, ) def reactor_pom(module_dirs: list[str]) -> str: mods = "".join(" {}\n".format(m) for m in module_dirs) return """ 4.0.0 {gid} ninca-crk-from-lib-reactor {ver} pom ninca-crk-from-lib-reactor CFR 反编译自交付 fat jar lib/ 内 ninca-crk 四条业务线(access-control / conference-attendance / smart-attendance / visitor-management)共 28 个构件;用于源码走查与对照,不代表官方原始工程结构。 UTF-8 {jv} ${{java.version}} ${{java.version}} {mods} org.apache.maven.plugins maven-compiler-plugin 3.8.1 ${{java.version}} ${{java.version}} UTF-8 """.format( gid=GROUP_ID, ver=REACTOR_VERSION, mods=mods, jv=JAVA_VERSION, ) def run_cfr(jar_path: Path, java_out: Path) -> None: java_out.mkdir(parents=True, exist_ok=True) cmd = [ "java", "-jar", str(ensure_cfr()), str(jar_path), "--outputdir", str(java_out), "--silent", "true", ] subprocess.run(cmd, check=True) def main() -> int: fat_jar = Path(sys.argv[1]) if len(sys.argv) > 1 else FAT_JAR_DEFAULT if not fat_jar.is_file(): print("Fat jar not found:", fat_jar, file=sys.stderr) return 1 if OUT_ROOT.exists(): shutil.rmtree(OUT_ROOT) OUT_ROOT.mkdir(parents=True) module_dirs: list[str] = [] tmpdir = Path(tempfile.mkdtemp(prefix="crk_lib_extract_")) try: with zipfile.ZipFile(fat_jar, "r") as zf: for name in TARGET_NAMES: inner = "lib/" + name if inner not in zf.namelist(): print("MISSING in fat jar:", inner, file=sys.stderr) return 2 extract_path = tmpdir / name with zf.open(inner) as src, open(extract_path, "wb") as dst: shutil.copyfileobj(src, dst) aid = artifact_dir(name) mod_path = OUT_ROOT / aid mod_path.mkdir(parents=True) java_root = mod_path / "src" / "main" / "java" java_root.mkdir(parents=True) run_cfr(extract_path, java_root) with zipfile.ZipFile(extract_path, "r") as inner_zf: embedded, emb_path = find_embedded_pom_xml(inner_zf) desc = "CFR from {} (embedded pom: {})".format(name, emb_path or "none") if embedded: try: pom_body = sanitize_pom_xml(embedded, aid) (mod_path / "pom.xml").write_text(pom_body, encoding="utf-8") except Exception as exc: print("WARN sanitize pom:", aid, exc, file=sys.stderr) (mod_path / "pom.xml").write_text(minimal_pom(aid, desc), encoding="utf-8") else: (mod_path / "pom.xml").write_text(minimal_pom(aid, desc), encoding="utf-8") module_dirs.append(aid) (OUT_ROOT / "pom.xml").write_text(reactor_pom(module_dirs), encoding="utf-8") readme = OUT_ROOT / "README.txt" readme.write_text( "Generated by scripts/decompile_ninca_crk_lib_modules.py\n" "Source fat jar: {}\n".format(fat_jar) + "CFR: {}\n".format(CFR_JAR) + "\n反编译产物仅供走查;首次编译需在私服可用的环境下补齐依赖。\n", encoding="utf-8", ) finally: shutil.rmtree(tmpdir, ignore_errors=True) print("OK ->", OUT_ROOT) return 0 if __name__ == "__main__": sys.exit(main())