# -*- coding: utf-8 -*- """Erlang and build-tool command resolution.""" from .shared import * def get_erl_cmd(erl_path: str = None) -> str: """根据配置路径获取 erl 可执行文件路径 Args: erl_path: Erlang 安装根目录(如 E:/app/erlang/Erlang_OTP_25) Returns: erl 可执行文件的完整路径,或默认的 'erl' """ if erl_path and erl_path.strip(): if IS_WINDOWS: erl_exe = Path(erl_path.strip()) / 'bin' / 'erl.exe' if erl_exe.exists(): return str(erl_exe) else: erl_bin = Path(erl_path.strip()) / 'bin' / 'erl' if erl_bin.exists(): return str(erl_bin) return 'erl' def subprocess_args_to_display(args: List[str]) -> str: """将 subprocess 参数列表转为可读的命令字符串(仅用于显示/日志)""" parts = [] for a in args: if ' ' in a or '"' in a or "'" in a or any(c in a for c in '()[]{}&|<>^'): parts.append(f'"{a}"') else: parts.append(a) return ' '.join(parts) def get_escript_cmd(erl_path: str = None) -> str: """根据配置路径获取 escript 可执行文件路径 Args: erl_path: Erlang 安装根目录 Returns: escript 可执行文件的完整路径,或默认的 'escript' """ if erl_path and erl_path.strip(): if IS_WINDOWS: escript_exe = Path(erl_path.strip()) / 'bin' / 'escript.exe' if escript_exe.exists(): return str(escript_exe) else: escript_bin = Path(erl_path.strip()) / 'bin' / 'escript' if escript_bin.exists(): return str(escript_bin) return 'escript' def _ensure_epmd_daemon(erl_path: Optional[str] = None) -> None: """尽量执行 epmd -daemon。本机已有 epmd 时通常无害(就绪则不再重复监听)。""" erl_exe = Path(get_erl_cmd(erl_path)) epmd = erl_exe.parent / ("epmd.exe" if IS_WINDOWS else "epmd") if not epmd.is_file(): which = shutil.which("epmd") if not which: return epmd = Path(which) try: subprocess.run( [str(epmd), "-daemon"], capture_output=True, timeout=5, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0) if IS_WINDOWS else 0, ) except Exception: pass def get_rebar3_cmd(server_root: str, erl_path: str = None) -> str: """获取 rebar3 执行命令 如果配置了 erl_path,直接用配置路径的 escript 来运行 rebar3 脚本, 而不是依赖系统 PATH 中的 escript。 Args: server_root: 服务器根目录(rebar3 脚本所在目录) erl_path: Erlang 安装根目录 Returns: rebar3 执行命令字符串 """ escript = get_escript_cmd(erl_path) if IS_WINDOWS: rebar3_script = Path(server_root) / 'rebar3' if escript != 'escript': return f'"{escript}" "{rebar3_script}"' return REBAR3_CMD else: rebar3_script = Path(server_root) / 'rebar3' if escript != 'escript': return f'"{escript}" "{rebar3_script}"' return './rebar3' def get_local_ip() -> str: """获取本机IP地址""" try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except: return "127.0.0.1" def get_ebin_paths(server_root: str, profile: str = 'game_server_dev') -> List[str]: """获取所有 ebin 目录路径 Args: server_root: 服务器根目录 profile: rebar3 profile 名称 (game_server_dev/login_server_dev/client_server_dev) Returns: ebin 目录路径列表 """ ebin_paths = [] build_dir = Path(server_root) / "_build" / profile / "lib" if build_dir.exists(): for app_dir in build_dir.iterdir(): if app_dir.is_dir(): ebin_dir = app_dir / "ebin" if ebin_dir.exists(): ebin_paths.append(str(ebin_dir)) # 添加 checkouts 目录 checkouts_dir = Path(server_root) / "_build" / profile / "checkouts" if checkouts_dir.exists(): for app_dir in checkouts_dir.iterdir(): if app_dir.is_dir(): ebin_dir = app_dir / "ebin" if ebin_dir.exists(): ebin_paths.append(str(ebin_dir)) return ebin_paths