From 8e65ac42c00cddb880df7e71cfb89e0447cd9d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Krzy=C5=9Bk=C3=B3w?= Date: Thu, 29 Feb 2024 00:07:11 +0100 Subject: [PATCH] feat(docs): Add troubleshooting guide for CairoSVG crash --- docs/plugins/requirements/image-processing.md | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/docs/plugins/requirements/image-processing.md b/docs/plugins/requirements/image-processing.md index c53cbf6adb2..236668f2269 100644 --- a/docs/plugins/requirements/image-processing.md +++ b/docs/plugins/requirements/image-processing.md @@ -93,6 +93,189 @@ The following environments come with a preinstalled version of [Cairo Graphics]: [Docker image]: https://hub.docker.com/r/squidfunk/mkdocs-material/ [GitHub Actions]: ../../publishing-your-site.md#with-github-actions +#### Troubleshooting { id="cairosvg-troubleshooting" } + +After following the installation guide above it may happen that you still +get the following error: + +```bash +no library called "cairo-2" was found +no library called "cairo" was found +no library called "libcairo-2" was found +cannot load library 'libcairo.so.2': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.so.2' +cannot load library 'libcairo.2.dylib': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.2.dylib' +cannot load library 'libcairo-2.dll': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo-2.dll' +``` + +This means that the [`cairosvg`][PyPi CairoSVG] package was installed, +but the underlying [`cairocffi`][PyPi CairoCFFI] dependency couldn't +[find][cffi-dopen] the installed library. Depending on the operating system +the library lookup process is different: + +!!! tip + Before proceeding remember to fully restart any open Terminal windows, and + their parent hosts like IDEs to reload any environmental variables, which + were altered during the installation process. This might be the quick fix. + +=== ":material-apple: macOS" + + On macOS the library lookup checks inside paths defined in [dyld]. + Additionally each library `name` is [checked][find-library-macOS] in three + variants with the `libname.dylib`, `name.dylib` and `name.framework/name` + format. + + [Homebrew] should set every needed variable to point at the installed + library, but if that didn't happen, you can use the debug script below + to maybe find the issue. + + A [known workaround][cffi-issue] is to add the homebrew lib path + directly before running MkDocs: + + ```bash + export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib + ``` + + ??? tip "Python Debug macOS Script" + + You can run the code below as a `debug.py` script or directly in + the interpreter. + + ```py + import os + from ctypes.macholib import dyld + from itertools import chain + + library_names = ("cairo-2", "cairo", "libcairo-2") + filenames = ("libcairo.so.2", "libcairo.2.dylib", "libcairo-2.dll") + found_path = "" + names = [] + + for name in library_names: + names += [ + 'lib%s.dylib' % name, + '%s.dylib' % name, + '%s.framework/%s' % (name, name) + ] + + for name in names: + for path in dyld.dyld_image_suffix_search( + chain( + dyld.dyld_override_search(name), + dyld.dyld_executable_path_search(name), + dyld.dyld_default_search(name) + ) + ): + print(path) + if os.path.isfile(path): + found_path = path + break + try: + if dyld._dyld_shared_cache_contains_path(path): + found_path = path + break + except NotImplementedError: + pass + if found_path: + filenames = (found_path, ) + filenames + break + else: + found_path = "not found" + + print(f"The path is {found_path}") + print("List of files that FFI will try to load:") + for filename in filenames: + print("-", filename) + ``` + +=== ":fontawesome-brands-windows: Windows" + + On Windows the library lookup checks inside the paths defined in the + environmental `PATH` variable. Additionally each library `name` is checked + in [two variants][find-library-Windows] with the `name` and `name.dll` format. + + The default installation path of [GTK runtime] is: + + ```powershell + C:\Program Files\GTK3-Runtime Win64 + ``` + + and the libraries are in the `lib` directory. Use the debug script + below to check if the path is included. If it isn't then: + + 1. Press ++windows+r++ + 2. Run the `SystemPropertiesAdvanced` applet + 3. Select "Environmental Variables" at the bottom + 4. Add the whole path to the `lib` directory to your `PATH` variable. + 5. Fully restart any open Terminal windows and parent hosts like IDEs to + reload the `PATH` inside them. + + ??? tip "Python Debug Windows Script" + + You can run the code below as a `debug.py` script or directly in + the interpreter. + + ```py + import os + + library_names = ("cairo-2.dll", "cairo.dll", "libcairo-2.dll") + filenames = ("libcairo.so.2", "libcairo.2.dylib", "libcairo-2.dll") + found_path = "" + + for name in library_names: + for path in os.environ['PATH'].split(os.pathsep): + resolved_path = os.path.join(path, name) + print(resolved_path) + if os.path.exists(resolved_path): + filenames = (resolved_path, ) + filenames + found_path = resolved_path + break + if found_path: + break + else: + found_path = "not found" + + print(f"The path is {found_path}") + print("List of files that FFI will try to load:") + for filename in filenames: + print("-", filename) + ``` + +=== ":material-linux: Linux" + + On Linux the library lookup [differs greatly][find-library-Linux] and + is dependant from the installed distribution. For example Python could + run a specific system shell command to find out which libraries are + available for a given C compiler. + + The below Python script will show, which function is being run to find + installed libraries. You can check the source to find out what specific + commands are run on your system during execution. + + ??? tip "Python Debug Linux Script" + + You can run the code below as a `debug.py` script or directly in + the interpreter. + + ```py + import inspect + from ctypes.util import find_library + + print("find_library script:") + print(inspect.getsourcefile(find_library)) + print("\nfind_library function:") + print(inspect.getsource(find_library)) + ``` + + [PyPi CairoSVG]: https://pypi.org/project/CairoSVG + [PyPi CairoCFFI]: https://pypi.org/project/CairoCFFI + [dyld]: https://www.unix.com/man-page/OSX/1/dyld/ + [cffi-issue]: https://github.com/squidfunk/mkdocs-material/issues/5121 + [cffi-dopen]: https://github.com/Kozea/cairocffi/blob/f1984d644bbc462ef0ec33b97782cf05733d7b53/cairocffi/__init__.py#L24-L49 + [find-library-macOS]: https://github.com/python/cpython/blob/4d58a1d8fb27048c11bcbda3da1bebf78f979335/Lib/ctypes/util.py#L70-L81 + [find-library-Windows]: https://github.com/python/cpython/blob/4d58a1d8fb27048c11bcbda3da1bebf78f979335/Lib/ctypes/util.py#L59-L67 + [find-library-Linux]: https://github.com/python/cpython/blob/4d58a1d8fb27048c11bcbda3da1bebf78f979335/Lib/ctypes/util.py#L92 + + ### pngquant [pngquant] is an excellent library for lossy PNG compression, and a direct