| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- r"""Java 后端**离线构建**(P12):本机 Maven 仓缺 jar 插件的运行期依赖(maven-archiver 全缺、
- plexus-utils:1.1 缺),所以 `mvn package` 打不出 jar;本脚本改走
- **`mvn -o compile` + 自组装 `lib/`** 的路线,产出可运行的"瘦 classes + lib"布局。
- 为什么还要它:用户令是"用本机 maven + 本机 JDK 1.8";本机仓是**残缺**的(离线实逮见下),
- 与其假装 `package` 可用,不如把可用的路走实,并把缺什么如实记下来。
- 用法:
- python app_backEnd/backend-java/build_offline.py # 编译 + 组装 lib/ + 打印启动命令
- python app_backEnd/backend-java/build_offline.py --run # 编译后直接起服务(前台)
- python appBackEnd/backend-java/build_offline.py --port 28120
- """
- from __future__ import annotations
- import argparse
- import os
- import pathlib
- import re
- import shutil
- import subprocess
- import sys
- sys.stdout.reconfigure(encoding='utf-8', errors='replace')
- HERE = pathlib.Path(__file__).resolve().parent
- MAIN = 'com.guanlan.backend.GuanlanBackendApplication'
- def _find(env: str, *cands: str, names: tuple = ()) -> pathlib.Path | None:
- """按"环境变量 → 候选路径 → PATH 里的可执行名"找工具(不在仓里写死本机盘符)。"""
- import os
- import shutil
- if os.environ.get(env):
- return pathlib.Path(os.environ[env])
- for c in cands:
- q = pathlib.Path(c)
- if q.exists():
- return q
- for n in names:
- w = shutil.which(n)
- if w:
- return pathlib.Path(w)
- return None
- MVN = _find('GUANLAN_MVN', r'D:\Program Files\apache-maven-3.8.9\bin\mvn.cmd',
- names=('mvn.cmd', 'mvn'))
- JDK = _find('JAVA_HOME', r'D:\Program Files\Java\jdk1.8.0_202')
- SETTINGS = (pathlib.Path(os.environ['GUANLAN_MVN_SETTINGS']) if os.environ.get('GUANLAN_MVN_SETTINGS')
- else (HERE / 'settings-offline.xml'))
- def run_maven(args: list[str], extra: list[str] | None = None) -> tuple[int, str]:
- if MVN is None or JDK is None:
- print('[X] 找不到 maven 或 JDK —— 设 GUANLAN_MVN / JAVA_HOME 后重试')
- return 127, ''
- cmd = [str(MVN), '-s', str(SETTINGS), '-o', '-Dmaven.legacyLocalRepo=true'] + (extra or []) + args
- env = dict(os.environ)
- env['JAVA_HOME'] = str(JDK)
- r = subprocess.run(cmd, cwd=str(HERE), capture_output=True, text=True, errors='replace', env=env)
- return r.returncode, (r.stdout or '') + (r.stderr or '')
- def main() -> int:
- ap = argparse.ArgumentParser()
- ap.add_argument('--run', action='store_true')
- ap.add_argument('--port', type=int, default=28120)
- a = ap.parse_args()
- print('① 离线编译(mvn -o compile)…')
- rc, out = run_maven(['compile'], ['-X'])
- if rc != 0:
- tail = [l for l in out.splitlines() if l.startswith('[ERROR]')][-6:]
- print(' [X] 编译失败:' if 'COMPILATION ERROR' in out else ' [X] maven 失败:')
- for l in tail:
- print(' ' + l[:150])
- return 1
- print(' [OK] 编译通过')
- # ② 从 -X 输出里把"编译类路径"抽出来(这是 Maven 亲自解析出的依赖集,最可靠)
- cands: list[str] = []
- for m in re.finditer(r'([A-Za-z]:\\[^;\r\n"\']+?\.jar)', out):
- cands.append(m.group(1))
- for m in re.finditer(r'/root/\.m2/[^\s;:]+?\.jar', out):
- cands.append(m.group(0))
- jars: list[str] = []
- skip_roots = (str(JDK).lower(), r'd:\program files\apache-maven-3.8.9')
- for c in cands:
- p = pathlib.Path(c)
- if not p.is_file() or str(p) in jars:
- continue
- # JDK 自带的 rt.jar/jsse.jar… 与 Maven 自己的 plexus-classworlds 都不能进 lib/
- # (JRE 在目标机上由随包 JRE 提供;混进来会把包撑大且可能与目标 JRE 冲突)
- if any(str(p).lower().startswith(r) for r in skip_roots):
- continue
- jars.append(str(p))
- lib = HERE / 'target' / 'lib'
- if lib.exists():
- shutil.rmtree(lib, ignore_errors=True)
- lib.mkdir(parents=True, exist_ok=True)
- for j in jars:
- shutil.copy2(j, lib / pathlib.Path(j).name)
- (HERE / 'target' / 'classpath.txt').write_text(
- ';'.join([str(HERE / 'target' / 'classes')] + [str(lib / pathlib.Path(j).name) for j in jars]),
- encoding='utf-8')
- print('② 依赖已组装: %s(%d 个 jar,%.1f MB)' % (
- lib.relative_to(HERE).as_posix(), len(jars),
- sum((lib / pathlib.Path(j).name).stat().st_size for j in jars) / 1e6))
- cp = (HERE / 'target' / 'classpath.txt').read_text(encoding='utf-8')
- java = JDK / 'bin' / 'java.exe'
- cmd = [str(java), '-Dfile.encoding=UTF-8', '-Dserver.port=%d' % a.port, '-cp', cp, MAIN]
- print('③ 启动命令:')
- print(' ' + ' '.join(['"%s"' % str(java)] + cmd[1:4] + ['-cp', '"%s"' % cp, MAIN]))
- if a.run:
- print(' 前台运行(Ctrl+C 结束)…')
- return subprocess.run(cmd).returncode
- print('\n(加 --run 可直接起服务;或把上面命令交给构建脚本/服务宿主)')
- return 0
- if __name__ == '__main__':
- sys.exit(main())
|