hook-gi.repository.GdkPixbuf.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2005-2021, PyInstaller Development Team.
  3. #
  4. # Distributed under the terms of the GNU General Public License (version 2
  5. # or later) with exception for distributing the bootloader.
  6. #
  7. # The full license is in the file COPYING.txt, distributed with this software.
  8. #
  9. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  10. #-----------------------------------------------------------------------------
  11. """
  12. Import hook for PyGObject's "gi.repository.GdkPixbuf" package.
  13. """
  14. import glob
  15. import os
  16. from shutil import which
  17. from PyInstaller.compat import exec_command_stdout, is_darwin, is_linux, is_win
  18. from PyInstaller.config import CONF
  19. from PyInstaller.utils.hooks import get_hook_config, logger
  20. from PyInstaller.utils.hooks.gi import (collect_glib_translations, get_gi_libdir, get_gi_typelibs)
  21. loaders_path = os.path.join('gdk-pixbuf-2.0', '2.10.0', 'loaders')
  22. destpath = "lib/gdk-pixbuf/loaders"
  23. cachedest = "lib/gdk-pixbuf"
  24. # If the "gdk-pixbuf-query-loaders" command is not in the current ${PATH}, or is not in the GI lib path, GDK and thus
  25. # GdkPixbuf is unavailable. Return with a non-fatal warning.
  26. gdk_pixbuf_query_loaders = None
  27. try:
  28. libdir = get_gi_libdir('GdkPixbuf', '2.0')
  29. except ValueError:
  30. logger.warning('"hook-gi.repository.GdkPixbuf" ignored, since GdkPixbuf library not found')
  31. libdir = None
  32. if libdir:
  33. # Distributions either package gdk-pixbuf-query-loaders in the GI libs directory (not on the path), or on the path
  34. # with or without a -x64 suffix depending on the architecture
  35. cmds = [
  36. os.path.join(libdir, 'gdk-pixbuf-2.0/gdk-pixbuf-query-loaders'),
  37. 'gdk-pixbuf-query-loaders-64',
  38. 'gdk-pixbuf-query-loaders',
  39. ]
  40. for cmd in cmds:
  41. gdk_pixbuf_query_loaders = which(cmd)
  42. if gdk_pixbuf_query_loaders is not None:
  43. break
  44. if gdk_pixbuf_query_loaders is None:
  45. logger.warning(
  46. '"hook-gi.repository.GdkPixbuf" ignored, since "gdk-pixbuf-query-loaders" is not in $PATH or gi lib dir.'
  47. )
  48. # Else, GDK is available. Let's do this.
  49. else:
  50. binaries, datas, hiddenimports = get_gi_typelibs('GdkPixbuf', '2.0')
  51. # To add support for a new platform, add a new "elif" branch below with the proper is_<platform>() test and glob
  52. # for finding loaders on that platform.
  53. if is_win:
  54. ext = "*.dll"
  55. elif is_darwin or is_linux:
  56. ext = "*.so"
  57. # If loader detection is supported on this platform, bundle all detected loaders and an updated loader cache.
  58. if ext:
  59. loader_libs = []
  60. # Bundle all found loaders with this user application.
  61. pattern = os.path.join(libdir, loaders_path, ext)
  62. for f in glob.glob(pattern):
  63. binaries.append((f, destpath))
  64. loader_libs.append(f)
  65. # Sometimes the loaders are stored in a different directory from the library (msys2)
  66. if not loader_libs:
  67. pattern = os.path.join(libdir, '..', 'lib', loaders_path, ext)
  68. for f in glob.glob(pattern):
  69. binaries.append((f, destpath))
  70. loader_libs.append(f)
  71. # Filename of the loader cache to be written below.
  72. cachefile = os.path.join(CONF['workpath'], 'loaders.cache')
  73. # Run the "gdk-pixbuf-query-loaders" command and capture its standard output providing an updated loader
  74. # cache; then write this output to the loader cache bundled with this frozen application. On all platforms,
  75. # we also move the package structure to point to lib/gdk-pixbuf instead of lib/gdk-pixbuf-2.0/2.10.0 in
  76. # order to make compatible for OSX application signing.
  77. #
  78. # On Mac OS we use @executable_path to specify a path relative to the generated bundle. However, on
  79. # non-Windows, we need to rewrite the loader cache because it is not relocatable by default. See
  80. # https://bugzilla.gnome.org/show_bug.cgi?id=737523
  81. #
  82. # To make it easier to rewrite, we just always write @executable_path, since its significantly easier to
  83. # find/replace at runtime. :)
  84. #
  85. # To permit string munging, decode the encoded bytes output by this command (i.e., enable the
  86. # "universal_newlines" option).
  87. #
  88. # On Fedora, the default loaders cache is /usr/lib64, but the libdir is actually /lib64. To get around this,
  89. # we pass the path to the loader command, and it will create a cache with the right path.
  90. #
  91. # On Windows, the loaders lib directory is relative, starts with 'lib', and uses \\ as path separators
  92. # (escaped \).
  93. cachedata = exec_command_stdout(gdk_pixbuf_query_loaders, *loader_libs)
  94. cd = []
  95. prefix = '"' + os.path.join(libdir, 'gdk-pixbuf-2.0', '2.10.0')
  96. plen = len(prefix)
  97. win_prefix = '"' + '\\\\'.join(['lib', 'gdk-pixbuf-2.0', '2.10.0'])
  98. win_plen = len(win_prefix)
  99. # For each line in the updated loader cache...
  100. for line in cachedata.splitlines():
  101. if line.startswith('#'):
  102. continue
  103. if line.startswith(prefix):
  104. line = '"@executable_path/' + cachedest + line[plen:]
  105. elif line.startswith(win_prefix):
  106. line = '"' + cachedest.replace('/', '\\\\') + line[win_plen:]
  107. cd.append(line)
  108. cachedata = '\n'.join(cd)
  109. # Write the updated loader cache to this file.
  110. with open(cachefile, 'w') as fp:
  111. fp.write(cachedata)
  112. # Bundle this loader cache with this frozen application.
  113. datas.append((cachefile, cachedest))
  114. # Else, loader detection is unsupported on this platform.
  115. else:
  116. logger.warning('GdkPixbuf loader bundling unsupported on your platform.')
  117. def hook(hook_api):
  118. hook_datas = []
  119. lang_list = get_hook_config(hook_api, "gi", "languages")
  120. if libdir and gdk_pixbuf_query_loaders is not None:
  121. hook_datas += collect_glib_translations('gdk-pixbuf', lang_list)
  122. hook_api.add_datas(hook_datas)