diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d13d17..111482f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,39 @@ if(MAGNUM_WITH_PYTHON AND MAGNUM_BUILD_STATIC AND UNIX) option(MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL "Build Python bindings linking to static libraries with RTLD_GLOBAL enabled for loading" ON) endif() +# Python since version 3.8 doesn't take dependency DLLs implicitly from PATH +# anymore, and one has to either place them right next to the Python modules, +# into system locations, or add extra paths via os.add_dll_directory(). +# +# Be nice to the user and implicitly populate that with Corrade and Magnum DLL +# directories, if found, on the first run. Anything else (such as SDL or GLFW +# DLL path) has to be specified by the user. +if(MAGNUM_WITH_PYTHON AND WIN32) + # Do this lookup only on the first ever run. But add the option always, so + # in case it's passed via command line in the initial CMake run, it + # actually becomes a cached option with a help text and not just a loose + # variable. + if(NOT MAGNUM_PYTHON_BINDINGS_DLL_PATH) + set(dll_path ) + foreach(dll_variable CORRADE_UTILITY_DLL_DEBUG CORRADE_UTILITY_DLL_RELEASE MAGNUM_DLL_DEBUG MAGNUM_DLL_RELEASE) + if(${dll_variable}) + get_filename_component(dll_directory ${${dll_variable}} DIRECTORY) + list(APPEND dll_path ${dll_directory}) + endif() + endforeach() + list(REMOVE_DUPLICATES dll_path) + + # If no DLLs were found, it means Corrade and Magnum is static and thus no + # DLL directories may need to be passed + if(dll_path) + message(STATUS "Autodetected ${dll_path} as directories to pass to Python for finding Corrade, Magnum and other dependency DLLs. Update the MAGNUM_PYTHON_BINDINGS_DLL_PATH variable if needed.") + endif() + endif() + + # This variable is then used in src/python/corrade/__init__.py.in + set(MAGNUM_PYTHON_BINDINGS_DLL_PATH "${dll_path}" CACHE STRING "Semicolon-separated directories where to look for Corrade, Magnum and other dependency DLLs") +endif() + # Backwards compatibility for unprefixed CMake options. If the user isn't # explicitly using prefixed options in the first run already, accept the # unprefixed options, and remember this decision for subsequent runs diff --git a/doc/python/pages/building.rst b/doc/python/pages/building.rst index 363a0b0..7474f7a 100644 --- a/doc/python/pages/building.rst +++ b/doc/python/pages/building.rst @@ -29,6 +29,8 @@ Downloading and building .. role:: sh(code) :language: sh +.. role:: bat(code) + :language: bat :summary: Installation guide for the Python bindings. :ref-prefix: @@ -176,6 +178,16 @@ depending on Magnum loaded later. See also :dox:`MAGNUM_BUILD_STATIC_UNIQUE_GLOBALS` in Corrade and Magnum for more information. +On Windows, Python since version 3.8 no longer loads dependency DLLs from +:bat:`%PATH%`. Instead, it requires relevant DLL directories to be explicitly +passed to :ref:`os.add_dll_directory()`. CMake by default adds the (absolute) +path to Corrade and Magnum DLLs there, you can edit the path or add additional +directories for other dependencies (such as SDL or GLFW) using the +``MAGNUM_PYTHON_BINDINGS_DLL_PATH`` CMake option. Non-absolute paths are +interpreted as relative to the package root --- for example, if the Corrade +module is in ``tools/python/corrade/``, specifying ``bin/`` as the DLL path +will resolve to ``tools/bin/``. + `Running unit tests`_ --------------------- diff --git a/src/python/corrade/__init__.py.in b/src/python/corrade/__init__.py.in index 2b4dd8b..1e26939 100644 --- a/src/python/corrade/__init__.py.in +++ b/src/python/corrade/__init__.py.in @@ -39,16 +39,22 @@ ${_MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL}sys.setdlopenflags(sys.getdlopenflag import platform if platform.system() == 'Windows': import os + # os.add_dll_directory() is only since Python 3.8, in earlier versions + # PATH is used and that alone may be sufficient + if hasattr(os, 'add_dll_directory'): + # The variable might be empty, or might contain empty parts, so skip + # all empty items. There's no splitWithoutEmptyParts() like in Corrade, + # sigh. Using """ to prevent accidental syntax errors if the path + # itself would contain quotes. + for directory in """${MAGNUM_PYTHON_BINDINGS_DLL_PATH}""".split(';'): + if not directory: + continue - for directory in [ - # Prebuilt binaries from the magnum-ci repo have this file in - # python/corrade/ and DLLs in bin/ - '../../bin' - ]: - bin_path = os.path.join(os.path.dirname(__file__), directory) - if os.path.exists(bin_path): - os.add_dll_directory(bin_path) - break + # If the directory is relative, interpret is as relative to parent + # dir. Also add it only if it exists. + bin_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), directory) + if os.path.exists(bin_path): + os.add_dll_directory(bin_path) from _corrade import *