From 4c24be9ee3926e6bfe83ea63b6968bb4920340ef Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Fri, 27 Oct 2023 14:25:27 -0700 Subject: [PATCH] Make `mjpython.py` work with Python from CommandLineTools. PiperOrigin-RevId: 577305478 Change-Id: Iec4b23c97f80462e5c16aca5ac4450faf3181a0c --- doc/changelog.rst | 12 +++++++++--- python/mujoco/mjpython/mjpython.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 31cf71f0..386e63b6 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -17,16 +17,22 @@ MJX 3. Made ``device_put`` type validation more verbose (fixes :github:issue:`1113`). 4. Removed empty EFC rows from `MJX`, for joints with no limits (fixes :github:issue:`1117`). +Python bindings +^^^^^^^^^^^^^^^ + +5. Fix the macOS ``mjpython`` launcher to work with the Python interpreter from Apple Command Line + Tools. + Simulate ^^^^^^^^ -5. :ref:`simulate`: correct handling of "Pause update", "Fullscreen" and "VSync" buttons. +6. :ref:`simulate`: correct handling of "Pause update", "Fullscreen" and "VSync" buttons. Documentation ^^^^^^^^^^^^^ -6. Added documentation for the :ref:`UI` framework. -7. Fixed typos and supported fields in docs (fixes :github:issue:`1105` and :github:issue:`1106`). +7. Added documentation for the :ref:`UI` framework. +8. Fixed typos and supported fields in docs (fixes :github:issue:`1105` and :github:issue:`1106`). Version 3.0.0 (October 18, 2023) diff --git a/python/mujoco/mjpython/mjpython.py b/python/mujoco/mjpython/mjpython.py index 62a9853d..4c21a4f3 100644 --- a/python/mujoco/mjpython/mjpython.py +++ b/python/mujoco/mjpython/mjpython.py @@ -25,6 +25,8 @@ import ctypes import importlib.util import os import platform +import re +import subprocess import sys if platform.system() != 'Darwin': @@ -48,7 +50,32 @@ def main(argv): # Conda doesn't create a separate shared library for Python. # We instead use the Python binary itself, which can be dlopened just as well. - os.environ['MJPYTHON_LIBPYTHON'] = get_executable_path() + libpython_path = get_executable_path() + os.environ['MJPYTHON_LIBPYTHON'] = libpython_path + + # In some installations (e.g. CommandLineTools), the Python interpreter loads + # dylibs from @executable_path-relative paths. This will not resolve + # correctly since @executable_path will be the directory containing the + # mjpython binary when we execve. We therefore preemptively resolve all + # @executable_path-relative paths now and add them to + # DYLD_FALLBACK_LIBRARY_PATH. + libpython_dir = os.path.dirname(libpython_path) + dyld_fallback_paths = ( + os.environ.get('DYLD_FALLBACK_LIBRARY_PATH', '').split(':')) + pattern = re.compile(r'@executable_path/(.+) \(offset \d+\)\Z') + otool_out = subprocess.run( + ['otool', '-l', libpython_path], + capture_output=True, + check=True, + ).stdout.decode() + for line in otool_out.split('\n'): + m = pattern.search(line) + if m is not None: + new_path = os.path.dirname(os.path.join(libpython_dir, m.group(1))) + if new_path not in dyld_fallback_paths: + dyld_fallback_paths.insert(0, new_path) + + os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = ':'.join(dyld_fallback_paths) # argv[0] is currently the path to this script. # Replace it with sys.executable to preserve e.g. virtualenv path.