Refactor compiler warning handling.

Compiler warnings are now accumulated in a vector of strings within the mjSpec object. New API functions `mjs_numWarnings` and `mjs_getWarning` are added to access these warnings. The compiler's log handler now chains warnings to the global log handler, ensuring they are still displayed immediately. Call sites in `mj_loadXML`, `mj_compile`, and the Python and WASM bindings have been updated to use the new warning API.

PiperOrigin-RevId: 933361650
Change-Id: I47cab98a460c57b0898c0a1a43fce2a5b9648eb1
This commit is contained in:
Yuval Tassa
2026-06-16 16:27:52 -07:00
committed by Copybara-Service
parent 55c6332f20
commit 6f8bb5ef55
25 changed files with 640 additions and 89 deletions
+17 -1
View File
@@ -24,6 +24,7 @@
#include <mujoco/mujoco.h>
#include "errors.h"
#include "indexers.h" // IWYU pragma: keep
#include "private.h"
#include "raw.h"
#include "structs.h" // IWYU pragma: keep
#include <pybind11/cast.h>
@@ -118,7 +119,14 @@ raw::MjModel* MjSpec::Compile(mjVFS* vfs) {
raw::MjModel* m;
{
py::gil_scoped_release no_gil;
// Install a no-op handler to suppress stderr output from warnings.
// Compile() installs its own (setjmp/longjmp) handler and then chains to
// prev. We want to raise a `warnings.warn`, so pass in a no-op handler.
mjfLogHandler prev =
_mjPRIVATE_setTlsLogHandler([](const mjLogMessage*) {});
m = mj_compile(ptr, vfs);
_mjPRIVATE_setTlsLogHandler(prev);
}
if (local_vfs.has_value()) {
@@ -127,9 +135,17 @@ raw::MjModel* MjSpec::Compile(mjVFS* vfs) {
local_vfs = std::nullopt;
}
if (!m || mjs_isWarning(ptr)) {
if (!m) {
throw py::value_error(mjs_getError(ptr));
}
int num_warnings = mjs_numWarnings(ptr);
if (num_warnings > 0) {
py::object warnings = py::module_::import("warnings");
for (int i = 0; i < num_warnings; ++i) {
warnings.attr("warn")(mjs_getWarning(ptr, i));
}
}
return m;
}