67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
"""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}"
|