现有组件功能与Amesim组件比较对齐,补全功能;仿真服务启动环境检测,旧版json工程文件适配读取
This commit is contained in:
1 parent
b6c22a54f8
commit
6fc9afe41d
119 files changed
+260114
-351
No files matched your search
@@ -0,0 +1,208 @@
|
||||
"""Small, uncached compile/link/run check using the production native toolchain."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
from threading import Event
|
||||
from time import monotonic
|
||||
from typing import Callable
|
||||
|
||||
from .build import CACHE, NativeCommandError, link_library_arguments, platform_build_inputs, toolchain
|
||||
from .processes import command_environment, is_driver_launch_failure
|
||||
|
||||
STAGE_MESSAGES = {
|
||||
"toolchain": "正在查找编译器与 SUNDIALS",
|
||||
"preparing": "正在准备最小自检程序",
|
||||
"preprocessing": "正在预处理最小自检程序",
|
||||
"compiling": "正在编译最小自检程序",
|
||||
"linking": "正在链接最小自检程序",
|
||||
"running": "正在运行最小自检程序",
|
||||
"schema": "正在检查模型 XML 解析环境",
|
||||
}
|
||||
OUTPUT_MARKER = "native-runtime-check-ok"
|
||||
# One smooth scalar equation exercises every library used by production BDF.
|
||||
# Its known answer is independent of the user's model and cannot hit model cache.
|
||||
SOURCE = r'''
|
||||
#include <cvode/cvode.h>
|
||||
#include <cvode/cvode_ls.h>
|
||||
#include <nvector/nvector_serial.h>
|
||||
#include <sunmatrix/sunmatrix_dense.h>
|
||||
#include <sunlinsol/sunlinsol_dense.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
static int rhs(sunrealtype t, N_Vector y, N_Vector dy, void *data) {
|
||||
(void)t; (void)data;
|
||||
NV_Ith_S(dy,0)=-NV_Ith_S(y,0);
|
||||
return 0;
|
||||
}
|
||||
int main(void) {
|
||||
SUNContext context=NULL;
|
||||
N_Vector y=NULL;
|
||||
SUNMatrix matrix=NULL;
|
||||
SUNLinearSolver linear=NULL;
|
||||
void *solver=NULL;
|
||||
int result=1;
|
||||
sunrealtype time=0;
|
||||
if (SUNContext_Create(SUN_COMM_NULL,&context)) goto done;
|
||||
y=N_VNew_Serial(1,context);
|
||||
if (!y) goto done;
|
||||
NV_Ith_S(y,0)=1;
|
||||
matrix=SUNDenseMatrix(1,1,context);
|
||||
if (!matrix) goto done;
|
||||
linear=SUNLinSol_Dense(y,matrix,context);
|
||||
solver=CVodeCreate(CV_BDF,context);
|
||||
if (!linear || !solver) goto done;
|
||||
if (CVodeInit(solver,rhs,0,y)<0 ||
|
||||
CVodeSStolerances(solver,1e-8,1e-10)<0 ||
|
||||
CVodeSetLinearSolver(solver,linear,matrix)<0 ||
|
||||
CVode(solver,0.1,y,&time,CV_NORMAL)<0) goto done;
|
||||
if (!isfinite(NV_Ith_S(y,0)) || fabs(NV_Ith_S(y,0)-exp(-0.1))>1e-6) goto done;
|
||||
puts("native-runtime-check-ok");
|
||||
result=0;
|
||||
done:
|
||||
if (result) fputs("Minimal BDF self-check failed.\n",stderr);
|
||||
if (solver) CVodeFree(&solver);
|
||||
if (linear) SUNLinSolFree(linear);
|
||||
if (matrix) SUNMatDestroy(matrix);
|
||||
if (y) N_VDestroy(y);
|
||||
if (context) SUNContext_Free(&context);
|
||||
return result;
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class CheckCancelled(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CheckFailure(RuntimeError):
|
||||
def __init__(self, message: str, *, command=(), cwd=None, exit_code=None,
|
||||
stdout="", stderr="", error_type="RuntimeError"):
|
||||
super().__init__(message)
|
||||
self.details = {"command": list(command), "cwd": str(cwd) if cwd else None,
|
||||
"exitCode": exit_code, "stdout": stdout[-8000:],
|
||||
"stderr": stderr[-8000:], "errorType": error_type}
|
||||
|
||||
|
||||
def _stop_process_tree(process: subprocess.Popen) -> None:
|
||||
# GCC starts cc1/as/ld. Killing only its driver can leave inherited pipes open.
|
||||
if os.name == "nt":
|
||||
try:
|
||||
subprocess.run(["taskkill", "/PID", str(process.pid), "/T", "/F"],
|
||||
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, timeout=5,
|
||||
creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
pass
|
||||
if process.poll() is None:
|
||||
process.kill()
|
||||
else:
|
||||
try:
|
||||
os.killpg(process.pid, signal.SIGKILL)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
|
||||
|
||||
def _command(command: list[str], directory: Path, stop: Event, timeout: float = 30) -> str:
|
||||
if stop.is_set():
|
||||
raise CheckCancelled()
|
||||
try:
|
||||
with subprocess.Popen(command, cwd=directory, stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
env=command_environment(command[0]), close_fds=True,
|
||||
start_new_session=os.name != "nt",
|
||||
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0) as process:
|
||||
deadline = monotonic() + timeout
|
||||
while True:
|
||||
try:
|
||||
stdout, stderr = process.communicate(timeout=0.2)
|
||||
break
|
||||
except subprocess.TimeoutExpired:
|
||||
if stop.is_set() or monotonic() >= deadline:
|
||||
_stop_process_tree(process)
|
||||
stdout, stderr = process.communicate()
|
||||
if stop.is_set():
|
||||
raise CheckCancelled()
|
||||
raise CheckFailure(f"命令执行超过 {timeout:g} 秒", command=command,
|
||||
cwd=directory, exit_code=process.returncode,
|
||||
stdout=stdout.decode("utf-8", errors="replace"),
|
||||
stderr=stderr.decode("utf-8", errors="replace"),
|
||||
error_type="TimeoutExpired")
|
||||
except OSError as exc:
|
||||
raise CheckFailure(str(exc), command=command, cwd=directory,
|
||||
error_type=type(exc).__name__) from exc
|
||||
output = stdout.decode("utf-8", errors="replace")
|
||||
if process.returncode:
|
||||
stderr_text = stderr.decode('utf-8', errors='replace')
|
||||
message = ('GCC 已启动,但无法启动编译子进程;该错误不能直接判定为编译器不存在'
|
||||
if is_driver_launch_failure(stderr_text) else '命令执行失败')
|
||||
# Whole-check retries remain owned by warmup (initial check + 5 rounds).
|
||||
raise CheckFailure(message, command=command, cwd=directory,
|
||||
exit_code=process.returncode, stdout=output,
|
||||
stderr=stderr_text,
|
||||
error_type="CalledProcessError")
|
||||
return output
|
||||
|
||||
|
||||
def check_native_runtime(progress: Callable[..., None], stop: Event) -> None:
|
||||
def stage(name: str, **details):
|
||||
if stop.is_set():
|
||||
raise CheckCancelled()
|
||||
progress(stage=name, message=STAGE_MESSAGES[name], **details)
|
||||
|
||||
candidate = os.environ.get("SIMULATION_NATIVE_CC") or shutil.which("gcc")
|
||||
stage("toolchain", compiler=candidate)
|
||||
try:
|
||||
compiler, sundials, version = toolchain()
|
||||
except NativeCommandError as exc:
|
||||
details = exc.details
|
||||
failure = CheckFailure(str(exc), command=details['command'], cwd=details['cwd'],
|
||||
exit_code=details['exitCode'], stdout=details['stdout'], stderr=details['stderr'],
|
||||
error_type=details['errorType'])
|
||||
failure.details['attempts'] = details['attempts']
|
||||
raise failure from exc
|
||||
except subprocess.SubprocessError as exc:
|
||||
def decoded(value):
|
||||
return value.decode("utf-8", errors="replace") if isinstance(value, bytes) else (value or "")
|
||||
raise CheckFailure(str(exc), command=getattr(exc, "cmd", ()), cwd=Path.cwd(),
|
||||
exit_code=getattr(exc, "returncode", None),
|
||||
stdout=decoded(getattr(exc, "stdout", "")),
|
||||
stderr=decoded(getattr(exc, "stderr", "")),
|
||||
error_type=type(exc).__name__) from exc
|
||||
except OSError as exc:
|
||||
raise CheckFailure(str(exc), command=[candidate, "--version"] if candidate else [],
|
||||
cwd=Path.cwd(), error_type=type(exc).__name__) from exc
|
||||
compiler = str(Path(shutil.which(compiler) or compiler).resolve())
|
||||
stage("preparing", compiler=compiler, compilerVersion=version, sundialsRoot=str(sundials))
|
||||
flags, libraries, dlls, executable_name = platform_build_inputs(sundials)
|
||||
# Keep the probe on the build filesystem: Linux /tmp may be mounted noexec.
|
||||
# Disposable probes never populate or evict the model/object caches.
|
||||
directory_root = CACHE.parent / "native-runtime-checks"
|
||||
directory_root.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory(prefix="check-", dir=directory_root) as temporary:
|
||||
directory = Path(temporary).resolve()
|
||||
source, preprocessed, obj = directory / "check.c", directory / "check.i", directory / "check.o"
|
||||
executable = directory / executable_name
|
||||
source.write_text(SOURCE, encoding="utf-8", newline="\n")
|
||||
stage("preprocessing")
|
||||
_command([compiler, *flags, "-E", "-P", f"-fmacro-prefix-map={directory}=/generated",
|
||||
"-I", str(sundials / "include"), str(source), "-o", str(preprocessed)], directory, stop)
|
||||
stage("compiling")
|
||||
_command([compiler, *flags, "-x", "cpp-output", "-c", str(preprocessed), "-o", str(obj)], directory, stop)
|
||||
stage("linking")
|
||||
_command([compiler, *flags, str(obj), *link_library_arguments(libraries),
|
||||
"-lm", "-o", str(executable)], directory, stop)
|
||||
stage("running")
|
||||
for dll in dlls:
|
||||
shutil.copyfile(dll, directory / dll.name)
|
||||
output = _command([str(executable)], directory, stop, timeout=10)
|
||||
if output.strip() != OUTPUT_MARKER:
|
||||
raise CheckFailure("最小程序未返回预期的自检成功标记", command=[str(executable)],
|
||||
cwd=directory, exit_code=0, stdout=output, error_type="UnexpectedOutput")
|
||||
stage("schema")
|
||||
from app.system_xml import validate_system_xml_document
|
||||
validate_system_xml_document(b"<System/>")
|
||||
Reference in new issue
Block a user