#!/usr/bin/env python3 """Run no-Abaqus Intel Fortran manifest tests.""" from __future__ import annotations import json import os import subprocess import sys from pathlib import Path try: from fortran_toolchain import FortranToolchain, resolve_toolchain, wrap_command except ModuleNotFoundError: from scripts.fortran_toolchain import FortranToolchain, resolve_toolchain, wrap_command MANIFEST_PATH = Path("tests/fortran/manifest.json") BUILD_ROOT = Path("build/fortran-tests") VALIDATION_MODES = {"off", "detect", "auto", "compile"} class FortranValidationError(RuntimeError): pass def validation_mode(env: dict[str, str] | None = None) -> str: env = env or os.environ mode = env.get("HARNESS_FORTRAN_VALIDATION", "auto").lower() if mode not in VALIDATION_MODES: raise FortranValidationError(f"Unsupported HARNESS_FORTRAN_VALIDATION: {mode}") return mode def load_manifest(root: Path) -> dict | None: manifest = root / MANIFEST_PATH if not manifest.exists(): return None try: payload = json.loads(manifest.read_text(encoding="utf-8")) except json.JSONDecodeError as exc: raise FortranValidationError(f"Invalid Fortran manifest JSON: {manifest}: {exc}") from exc if not isinstance(payload, dict) or not isinstance(payload.get("tests", []), list): raise FortranValidationError(f"Fortran manifest must contain a tests array: {manifest}") return payload def _validate_test_record(record: dict) -> tuple[str, list[str]]: if not isinstance(record, dict): raise FortranValidationError("Fortran manifest test record must be an object.") name = record.get("name") sources = record.get("sources") if not isinstance(name, str) or not name: raise FortranValidationError("Fortran manifest test record is missing name.") if not isinstance(sources, list) or not all(isinstance(source, str) and source for source in sources): raise FortranValidationError(f"Fortran manifest test {name} must define source paths.") return name, sources def build_test_commands(root: Path, manifest: dict, toolchain: FortranToolchain) -> list[str]: commands: list[str] = [] for record in manifest.get("tests", []): name, sources = _validate_test_record(record) build_dir = root / BUILD_ROOT / name exe_path = build_dir / f"{name}.exe" source_paths = [root / source for source in sources] compile_args: list[str | Path] = [ toolchain.executable, "/nologo", *source_paths, f"/exe:{exe_path}", ] commands.append(wrap_command(toolchain, compile_args)) commands.append(str(exe_path)) return commands def discover_commands(root: Path, env: dict[str, str] | None = None) -> list[str]: env = env or os.environ mode = validation_mode(env) if mode == "off": return [] manifest = load_manifest(root) if manifest is None: return [] toolchain = resolve_toolchain(env) if toolchain is None: raise FortranValidationError("Fortran validation manifest exists, but no Intel oneAPI Fortran compiler was found.") if mode == "detect": return [] return build_test_commands(root, manifest, toolchain) def run_command(command: str, root: Path) -> subprocess.CompletedProcess: return subprocess.run(command, cwd=root, shell=True, capture_output=True, text=True, encoding="utf-8", errors="replace") def main() -> int: root = Path(__file__).resolve().parent.parent try: commands = discover_commands(root) except FortranValidationError as exc: print(f"Fortran validation failed: {exc}", file=sys.stderr) return 2 if not commands: print("No Fortran validation commands configured.") return 0 for command in commands: print(f"$ {command}") result = run_command(command, root) if result.stdout.strip(): print("[stdout]") print(result.stdout.strip()) if result.stderr.strip(): print("[stderr]", file=sys.stderr) print(result.stderr.strip(), file=sys.stderr) if result.returncode != 0: print(f"Fortran validation failed: {command}", file=sys.stderr) return result.returncode print("Fortran validation succeeded.") return 0 if __name__ == "__main__": raise SystemExit(main())