"""Compile an EA from .mq5 to .ex5 with MetaEditor (doc 07 §1). The tester runs a compiled ``.ex5``. Compile from the command line so the bridge can do it programmatically. Custom indicators the EA calls must be compiled too and placed in ``MQL5\\Indicators\\``. If the EA's ``#include`` files live in a non-standard folder, compile the terminal in portable mode (``/portable``) so MetaEditor resolves includes from that terminal's ``MQL5\\Include\\``. """ from __future__ import annotations import subprocess from pathlib import Path DEFAULT_MT5_INSTALL = r"C:\Program Files\MetaTrader 5 IC Markets Global" def default_metaeditor_path(mt5_install: str = DEFAULT_MT5_INSTALL) -> str: """Return the metaeditor64.exe path for the given MT5 install.""" return str(Path(mt5_install) / "metaeditor64.exe") def compile_ea( mq5_path: str | Path, *, metaeditor_path: str | None = None, mt5_install: str = DEFAULT_MT5_INSTALL, timeout: int = 120, ) -> tuple[bool, str]: """Compile an ``.mq5`` EA to ``.ex5`` via MetaEditor's command line. Returns ``(success, log_text)``. MetaEditor writes a ``.log`` next to the source; on a clean compile the ``.ex5`` appears beside the ``.mq5``. Command line (doc 07 §1):: metaeditor64.exe /compile:"C:\\path\\to\\Expert.mq5" /log """ mq5 = Path(mq5_path) if not mq5.exists(): return False, f"source not found: {mq5}" editor = metaeditor_path or default_metaeditor_path(mt5_install) if not Path(editor).exists(): return False, f"metaeditor not found: {editor}" cmd = [editor, f"/compile:{mq5}", "/log"] try: proc = subprocess.run( cmd, capture_output=True, text=True, timeout=timeout, check=False, ) log_path = mq5.with_suffix(".log") log_text = proc.stdout + "\n" + proc.stderr if log_path.exists(): try: log_text += "\n--- metaeditor log ---\n" + log_path.read_text( encoding="utf-16-le", errors="replace" ) except Exception: pass ex5 = mq5.with_suffix(".ex5") success = ex5.exists() and ex5.stat().st_size > 0 return success, log_text except subprocess.TimeoutExpired: return False, f"compile timed out after {timeout}s" except FileNotFoundError as e: return False, f"failed to launch metaeditor: {e}"