Files
2026-06-11 14:29:20 +09:00

133 lines
4.4 KiB
Python

#!/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
build_dir.mkdir(parents=True, exist_ok=True)
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())