|
| 1 | +# Dynamic linking |
| 2 | + |
| 3 | +If you want to support dynamic linkages between python projects or system |
| 4 | +libraries, you will likely encounter some issues in making sure the compiled |
| 5 | +libraries/python bindings work after the wheel is created and the python project |
| 6 | +is installed on the system. The most common issue are the missing hints pointing |
| 7 | +to where the runtime libraries are located, specifically `RPATH` on Linux and |
| 8 | +MacOS systems, and `PATH`/`os.add_dll_directory` on Windows systems. Here are |
| 9 | +some recommendations on how to address them. |
| 10 | + |
| 11 | +## Link to the static libraries |
| 12 | + |
| 13 | +The easiest solution is to make sure you link to the static libraries |
| 14 | +counterparts during the CMake build. How to achieve this depends on the specific |
| 15 | +dependency, how it is imported in the CMake project and how the dependency is |
| 16 | +packaged in each ecosystem. |
| 17 | + |
| 18 | +For example for [Boost][FindBoost] this is controlled via the variable |
| 19 | +`Boost_USE_STATIC_LIBS`. |
| 20 | + |
| 21 | +[FindBoost]: inv:cmake:cmake:module#module:FindBoost |
| 22 | + |
| 23 | +## Wheel repair tools |
| 24 | + |
| 25 | +There are wheel repair tools for each operating system that bundle any dynamic |
| 26 | +libraries used and patch the libraries/python bindings to point to prioritize |
| 27 | +those libraries. The most common tools for these are [auditwheel] for Linux, |
| 28 | +[delocate] for MacOS and [delvewheel] for Windows. [cibuildwheel] incorporates |
| 29 | +these tools in its [repair wheel] feature. |
| 30 | + |
| 31 | +One downside of these tools is that all dependencies are duplicated and bundled |
| 32 | +in each project. |
| 33 | + |
| 34 | +[auditwheel]: https://pypi.org/project/auditwheel/ |
| 35 | +[delocate]: https://pypi.org/project/delocate/ |
| 36 | +[delvewheel]: https://pypi.org/project/delvewheel/ |
| 37 | +[cibuildwheel]: https://cibuildwheel.pypa.io/en/stable/ |
| 38 | +[repair wheel]: |
| 39 | + https://cibuildwheel.pypa.io/en/stable/options/#repair-wheel-command |
| 40 | + |
| 41 | +## Manual patching |
| 42 | + |
| 43 | +For maximum control, you can do the equivalent patching inside CMake manually. |
| 44 | +This is not recommended because it does not have access to the python |
| 45 | +environment metadata. |
| 46 | + |
| 47 | +The `RPATH` patching can be done as |
| 48 | + |
| 49 | +```cmake |
| 50 | +if(APPLE) |
| 51 | + set(origin_token "@loader_path") |
| 52 | +else() |
| 53 | + set(origin_token "$ORIGIN") |
| 54 | +endif() |
| 55 | +set_property(TARGET <target> PROPERTY INSTALL_RPATH |
| 56 | + "${origin_token}/install_path/to/dynamic_library" |
| 57 | +) |
| 58 | +``` |
| 59 | + |
| 60 | +For Windows patching, this has to be done at the python files using |
| 61 | +`os.add_dll_directory` at the top-most package `__init__.py` file or top-level |
| 62 | +python module files. |
| 63 | + |
| 64 | +```python |
| 65 | +import os |
| 66 | +from pathlib import Path |
| 67 | + |
| 68 | +dependency_dll_path = Path(__file__).parent / "install_path/to/dynamic_library" |
| 69 | +os.add_dll_directory(str(dependency_dll_path)) |
| 70 | +``` |
0 commit comments