diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 8767aecf..6136d2af 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -127,6 +127,17 @@ the required size. XML saving automatically compiles the spec before saving. Save spec to XML file, return 0 on success, -1 otherwise. XML saving requires that the spec first be compiled. +.. _mju_getXMLDependencies: + +`mju_getXMLDependencies <#mju_getXMLDependencies>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_getXMLDependencies + +Given MJCF filename, fills dependencies with a list of all other asset files it depends on. + +The search is recursive, and the list includes the filename itself. + .. _Mainsimulation: Main simulation diff --git a/doc/changelog.rst b/doc/changelog.rst index 9c3347dc..b38e1b03 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -24,6 +24,10 @@ General :ref:`visual/rgba/constraint`. For spatial tendons, this visualization aid is active only if no :ref:`material` is set and :ref:`rgba` is default. +- Added :ref:`mju_getXMLDependencies` for computing a list of unique asset dependencies from an MJCF file. + +- Added the code sample ``dependencies`` which provides command line utility for printing the result of :ref:`mju_getXMLDependencies`. + MJX ^^^ diff --git a/doc/includes/references.h b/doc/includes/references.h index 80a8e766..31b8d118 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -3021,6 +3021,7 @@ int mj_saveLastXML(const char* filename, const mjModel* m, char* error, int erro void mj_freeLastXML(void); int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int error_sz); int mj_saveXML(const mjSpec* s, const char* filename, char* error, int error_sz); +void mju_getXMLDependencies(const char* filename, mjStringVec* dependencies); void mj_step(const mjModel* m, mjData* d); void mj_step1(const mjModel* m, mjData* d); void mj_step2(const mjModel* m, mjData* d); @@ -3182,7 +3183,6 @@ mjtNum mju_rayFlex(const mjModel* m, const mjData* d, int flex_layer, mjtByte fl const mjtNum* pnt, const mjtNum* vec, int vertid[1]); mjtNum mju_raySkin(int nface, int nvert, const int* face, const float* vert, const mjtNum pnt[3], const mjtNum vec[3], int vertid[1]); -void mju_getXMLDependencies(const char* filename, mjStringVec* dependencies); void mjv_defaultCamera(mjvCamera* cam); void mjv_defaultFreeCamera(const mjModel* m, mjvCamera* cam); void mjv_defaultPerturb(mjvPerturb* pert); diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index f971ae3c..0ab1e8dd 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -151,6 +151,9 @@ MJAPI int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, // Nullable: error MJAPI int mj_saveXML(const mjSpec* s, const char* filename, char* error, int error_sz); +// Given MJCF filename, fills dependencies with a list of all other asset files it depends on. +// The search is recursive, and the list includes the filename itself. +MJAPI void mju_getXMLDependencies(const char* filename, mjStringVec* dependencies); //---------------------------------- Main simulation ----------------------------------------------- @@ -644,11 +647,6 @@ MJAPI mjtNum mju_rayFlex(const mjModel* m, const mjData* d, int flex_layer, mjtB MJAPI mjtNum mju_raySkin(int nface, int nvert, const int* face, const float* vert, const mjtNum pnt[3], const mjtNum vec[3], int vertid[1]); -//---------------------------------- Dependencies -------------------------------------------------- - -// Given MJCF filename, fills dependencies with a list of all other files it depends on. -MJAPI void mju_getXMLDependencies(const char* filename, mjStringVec* dependencies); - //---------------------------------- Interaction --------------------------------------------------- // Set default camera. diff --git a/python/mujoco/introspect/functions.py b/python/mujoco/introspect/functions.py index fb042ae2..6d33975a 100644 --- a/python/mujoco/introspect/functions.py +++ b/python/mujoco/introspect/functions.py @@ -481,6 +481,26 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Save spec to XML file, return 0 on success, -1 otherwise.', )), + ('mju_getXMLDependencies', + FunctionDecl( + name='mju_getXMLDependencies', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='filename', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='dependencies', + type=PointerType( + inner_type=ValueType(name='mjStringVec'), + ), + ), + ), + doc='Given MJCF filename, fills dependencies with a list of all other asset files it depends on. The search is recursive, and the list includes the filename itself.', # pylint: disable=line-too-long + )), ('mj_step', FunctionDecl( name='mj_step', @@ -3925,26 +3945,6 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Intersect ray with skin, return nearest distance or -1 if no intersection, and also output nearest vertex id.', # pylint: disable=line-too-long )), - ('mju_getXMLDependencies', - FunctionDecl( - name='mju_getXMLDependencies', - return_type=ValueType(name='void'), - parameters=( - FunctionParameterDecl( - name='filename', - type=PointerType( - inner_type=ValueType(name='char', is_const=True), - ), - ), - FunctionParameterDecl( - name='dependencies', - type=PointerType( - inner_type=ValueType(name='mjStringVec'), - ), - ), - ), - doc='Given MJCF filename, fills dependencies with a list of all other files it depends on.', # pylint: disable=line-too-long - )), ('mjv_defaultCamera', FunctionDecl( name='mjv_defaultCamera', diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 563dd8da..fed247f5 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -76,12 +76,19 @@ target_compile_options(compile PUBLIC ${MUJOCO_SAMPLE_COMPILE_OPTIONS}) target_link_libraries(compile Threads::Threads) target_link_options(compile PRIVATE ${MUJOCO_SAMPLE_LINK_OPTIONS}) +# Build sample binaries +add_executable(dependencies dependencies.cc) +target_compile_options(dependencies PUBLIC ${MUJOCO_SAMPLE_COMPILE_OPTIONS}) +target_link_libraries(dependencies Threads::Threads) +target_link_options(dependencies PRIVATE ${MUJOCO_SAMPLE_LINK_OPTIONS}) + add_executable(testspeed testspeed.cc) target_compile_options(testspeed PUBLIC ${MUJOCO_SAMPLE_COMPILE_OPTIONS}) target_link_libraries(testspeed Threads::Threads) target_link_options(testspeed PRIVATE ${MUJOCO_SAMPLE_LINK_OPTIONS}) target_link_libraries(compile mujoco::mujoco) +target_link_libraries(dependencies mujoco::mujoco) target_link_libraries(testspeed mujoco::mujoco) # Build samples that require GLFW. @@ -109,6 +116,7 @@ target_link_options(record PRIVATE ${MUJOCO_SAMPLE_LINK_OPTIONS}) if(APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS) embed_in_bundle(basic simulate) embed_in_bundle(compile simulate) + embed_in_bundle(dependencies simulate) embed_in_bundle(record simulate) embed_in_bundle(testspeed simulate) endif() @@ -127,6 +135,7 @@ if(_INSTALL_SAMPLES) TARGETS basic compile + dependencies record testspeed INSTALL_DIRECTORY @@ -140,6 +149,7 @@ if(_INSTALL_SAMPLES) install( TARGETS basic compile + dependencies record testspeed EXPORT ${PROJECT_NAME} diff --git a/sample/dependencies.cc b/sample/dependencies.cc new file mode 100644 index 00000000..6b360747 --- /dev/null +++ b/sample/dependencies.cc @@ -0,0 +1,45 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +// help +static constexpr char helpstring[] = + "\n Usage: list_dependencies infile\n" + " infile must be in MJCF\n" + " Example: list_dependencies model.xml\n"; + + +// main function +int main(int argc, char** argv) { + + // print help if arguments are missing + if (argc!=3 && argc!=2) { + std::cout << helpstring << std::endl; + return EXIT_SUCCESS; + } + + mjStringVec dependencies; + mju_getXMLDependencies(argv[1], &dependencies); + std::sort(dependencies.begin(), dependencies.end()); + + std::cout << "Dependencies:" << std::endl; + for (int i = 0; i < dependencies.size(); ++i) { + std::cout << "\t " << dependencies[i] << std::endl; + } + return EXIT_SUCCESS; +} diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 0075cbb7..f3dc8f43 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -6367,6 +6367,9 @@ public static unsafe extern int mj_saveLastXML([MarshalAs(UnmanagedType.LPStr)]s [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mj_freeLastXML(); +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern void mju_getXMLDependencies([MarshalAs(UnmanagedType.LPStr)]string filename, void* dependencies); + [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mj_step(mjModel_* m, mjData_* d); @@ -6757,9 +6760,6 @@ public static unsafe extern double mju_rayFlex(mjModel_* m, mjData_* d, int flex [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern double mju_raySkin(int nface, int nvert, int* face, float* vert, double* pnt, double* vec, int* vertid); -[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] -public static unsafe extern void mju_getXMLDependencies([MarshalAs(UnmanagedType.LPStr)]string filename, void* dependencies); - [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mjv_defaultCamera(mjvCamera_* cam);