From 8db92ab4a9cf947d5cd38daad7559e6ea3d59fe9 Mon Sep 17 00:00:00 2001 From: Michael Moss Date: Thu, 18 Jun 2026 08:43:37 -0700 Subject: [PATCH] Enable building Studio (Linux only) as a component of the main MuJoCo build. And remove the separate, conditional (and hacky) "build_studio" step. PiperOrigin-RevId: 934375150 Change-Id: I46514d0f33c17bb29625b818cdde3a285fd2c439 --- .../mujoco/experimental/studio/CMakeLists.txt | 15 ++++++++++ python/setup.py | 28 +++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/python/mujoco/experimental/studio/CMakeLists.txt b/python/mujoco/experimental/studio/CMakeLists.txt index 14f46473..7b392f53 100644 --- a/python/mujoco/experimental/studio/CMakeLists.txt +++ b/python/mujoco/experimental/studio/CMakeLists.txt @@ -68,3 +68,18 @@ target_include_directories(implot PRIVATE ../dear_imgui) set_target_properties(implot PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../implot" ) + +# Workaround for duplicate symbol error (wp_fractional_scale_manager_v1_interface) +# when linking both GLFW (via Filament) and SDL2 statically on Linux. +if(UNIX AND NOT APPLE) + set(STUDIO_PY_MODULES + parser + native_viewer_cc + renderer + ux + sim + ) + foreach(target ${STUDIO_PY_MODULES}) + target_link_options(${target} PRIVATE "-Wl,--allow-multiple-definition") + endforeach() +endif() diff --git a/python/setup.py b/python/setup.py index 02153019..11feec15 100644 --- a/python/setup.py +++ b/python/setup.py @@ -218,11 +218,35 @@ class BuildCMakeExtension(build_ext.build_ext): ) os.makedirs(dst) for directory, _, filenames in os.walk(self._mujoco_include_path): + rel_dir = os.path.relpath(directory, self._mujoco_include_path) + + # Skip third-party directories + if rel_dir.startswith(('SDL2', 'math', 'misc')): + continue + for filename in fnmatch.filter(filenames, '*.h'): - shutil.copyfile( - os.path.join(directory, filename), os.path.join(dst, filename) + rel_file_path = os.path.relpath( + os.path.join(directory, filename), self._mujoco_include_path ) + # Skip third-party files in the root include path (for framework case) + if rel_dir == '.' and not ( + filename == 'mujoco.h' or filename.startswith('mj') + ): + continue + + # Reconstruct destination path preserving structure + # Strip leading 'mujoco/' if present to avoid duplicate 'mujoco/mujoco/' + if rel_file_path.startswith('mujoco/'): + target_rel_path = rel_file_path[len('mujoco/') :] + else: + target_rel_path = rel_file_path + + target_dst = os.path.join(dst, target_rel_path) + os.makedirs(os.path.dirname(target_dst), exist_ok=True) + shutil.copyfile(os.path.join(directory, filename), target_dst) + + def _copy_studio_assets(self): assets_src = None for directory, subdirs, _ in os.walk(os.environ[MUJOCO_PATH]):