hook-gi.repository.GdkPixbuf.py 6.5 KB

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