diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29079de4..2151f85e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,6 +143,16 @@ jobs: - name: Install MuJoCo working-directory: build run: cmake --install . + - name: Copy plugins (POSIX) + if: ${{ runner.os != 'Windows' }} + working-directory: build + run: mkdir -p ${{ matrix.tmpdir }}/mujoco_install/plugin && + cp lib/libcable.* ${{ matrix.tmpdir }}/mujoco_install/plugin + - name: Copy plugins (Windows) + if: ${{ runner.os == 'Windows' }} + working-directory: build + run: mkdir -p ${{ matrix.tmpdir }}/mujoco_install/plugin && + cp bin/Release/cable.dll ${{ matrix.tmpdir }}/mujoco_install/plugin - name: Configure samples working-directory: sample run: > @@ -180,6 +190,7 @@ jobs: run: > source ${{ matrix.tmpdir }}/venv/bin/activate && MUJOCO_PATH="${{ matrix.tmpdir }}/mujoco_install" + MUJOCO_PLUGIN_PATH="${{ matrix.tmpdir }}/mujoco_install/plugin" MUJOCO_CMAKE_ARGS="-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF ${{ matrix.cmake_args }}" pip wheel -v --no-deps mujoco-*.tar.gz - name: Install Python bindings diff --git a/plugin/elasticity/CMakeLists.txt b/plugin/elasticity/CMakeLists.txt index 43de259e..7d25ffcf 100644 --- a/plugin/elasticity/CMakeLists.txt +++ b/plugin/elasticity/CMakeLists.txt @@ -26,4 +26,16 @@ add_library(cable SHARED) target_sources(cable PRIVATE ${MUJOCO_ELASTICITY_SRCS}) target_include_directories(cable PRIVATE ${MUJOCO_ELASTICITY_INCLUDE}) target_link_libraries(cable PRIVATE mujoco) - +target_compile_options( + cable + PRIVATE ${AVX_COMPILE_OPTIONS} + ${MUJOCO_MACOS_COMPILE_OPTIONS} + ${EXTRA_COMPILE_OPTIONS} + ${MUJOCO_CXX_FLAGS} +) +target_link_options( + cable + PRIVATE + ${MUJOCO_MACOS_LINK_OPTIONS} + ${EXTRA_LINK_OPTIONS} +) diff --git a/python/mujoco/__init__.py b/python/mujoco/__init__.py index e33a132d..5fb63ec4 100644 --- a/python/mujoco/__init__.py +++ b/python/mujoco/__init__.py @@ -54,5 +54,15 @@ if _MUJOCO_GL not in ('disable', 'disabled', 'off', 'false', '0'): from mujoco.glfw import GLContext HEADERS_DIR = os.path.join(os.path.dirname(__file__), 'include/mujoco') +PLUGINS_DIR = os.path.join(os.path.dirname(__file__), 'plugin') + +PLUGIN_HANDLES = [] + +def _load_all_bundled_plugins(): + for directory, _, filenames in os.walk(PLUGINS_DIR): + for filename in filenames: + PLUGIN_HANDLES.append(ctypes.CDLL(os.path.join(directory, filename))) + +_load_all_bundled_plugins() __version__ = mj_versionString() # pylint: disable=undefined-variable diff --git a/python/mujoco/bindings_test.py b/python/mujoco/bindings_test.py index 90b1270c..66e66cb0 100644 --- a/python/mujoco/bindings_test.py +++ b/python/mujoco/bindings_test.py @@ -69,6 +69,14 @@ TEST_XML_SENSOR = r""" """ +TEST_XML_PLUGIN = r""" + + + + + +""" + @contextlib.contextmanager def temporary_callback(setter, callback): @@ -1111,5 +1119,8 @@ Euler integrator, semi-implicit in velocity. self.fail("Attribute '{}' differs from expected value: {}".format( name, str(e))) + def test_load_plugin(self): + mujoco.MjModel.from_xml_string(TEST_XML_PLUGIN) + if __name__ == '__main__': absltest.main() diff --git a/python/setup.py b/python/setup.py index fad9204e..febabe1a 100644 --- a/python/setup.py +++ b/python/setup.py @@ -35,6 +35,7 @@ __version__ = '2.2.2' MUJOCO_CMAKE = 'MUJOCO_CMAKE' MUJOCO_CMAKE_ARGS = 'MUJOCO_CMAKE_ARGS' MUJOCO_PATH = 'MUJOCO_PATH' +MUJOCO_PLUGIN_PATH = 'MUJOCO_PLUGIN_PATH' EXT_PREFIX = 'mujoco.' @@ -70,6 +71,15 @@ def get_external_lib_patterns(): return ['libmujoco.so.*'] +def get_plugin_lib_patterns(): + if platform.system() == 'Windows': + return ['*.dll'] + elif platform.system() == 'Darwin': + return ['lib*.dylib'] + else: + return ['lib*'] + + def start_and_end(iterable): it = iter(iterable) while True: @@ -140,6 +150,7 @@ class BuildCMakeExtension(build_ext.build_ext): self._is_apple = (platform.system() == 'Darwin') (self._mujoco_library_path, self._mujoco_include_path, + self._mujoco_plugins_path, self._mujoco_framework_path) = self._find_mujoco() self._configure_cmake() for ext in self.extensions: @@ -148,23 +159,30 @@ class BuildCMakeExtension(build_ext.build_ext): self.build_extension(ext) self._copy_external_libraries() self._copy_mujoco_headers() + self._copy_plugin_libraries() def _find_mujoco(self): if MUJOCO_PATH not in os.environ: - raise RuntimeError(f'{MUJOCO_PATH} environment variable is not set') + raise RuntimeError( + f'{MUJOCO_PATH} environment variable is not set') + if MUJOCO_PLUGIN_PATH not in os.environ: + raise RuntimeError( + f'{MUJOCO_PLUGIN_PATH} environment variable is not set') library_path = None include_path = None + plugin_path = os.environ['MUJOCO_PLUGIN_PATH'] for directory, subdirs, filenames in os.walk(os.environ['MUJOCO_PATH']): if self._is_apple and 'mujoco.framework' in subdirs: return (os.path.join(directory, 'mujoco.framework/Versions/A'), os.path.join(directory, 'mujoco.framework/Headers'), + plugin_path, directory) if fnmatch.filter(filenames, get_mujoco_lib_pattern()): library_path = directory if os.path.exists(os.path.join(directory, 'mujoco/mujoco.h')): include_path = directory if library_path and include_path: - return library_path, include_path, None + return library_path, include_path, plugin_path, None raise RuntimeError('Cannot find MuJoCo library and/or include paths') def _copy_external_libraries(self): @@ -175,6 +193,17 @@ class BuildCMakeExtension(build_ext.build_ext): shutil.copyfile(os.path.join(directory, filename), os.path.join(dst, filename)) + def _copy_plugin_libraries(self): + dst = os.path.join( + os.path.dirname(self.get_ext_fullpath(self.extensions[0].name)), + 'plugin') + os.makedirs(dst) + for directory, _, filenames in os.walk(self._mujoco_plugins_path): + for pattern in get_plugin_lib_patterns(): + for filename in fnmatch.filter(filenames, pattern): + shutil.copyfile(os.path.join(directory, filename), + os.path.join(dst, filename)) + def _copy_mujoco_headers(self): dst = os.path.join( os.path.dirname(self.get_ext_fullpath(self.extensions[0].name)),