dylib.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2013-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. Manipulating with dynamic libraries.
  13. """
  14. import os.path
  15. from PyInstaller.utils.win32 import winutils
  16. __all__ = ['exclude_list', 'include_list', 'include_library']
  17. import os
  18. import re
  19. from PyInstaller import compat
  20. import PyInstaller.log as logging
  21. logger = logging.getLogger(__name__)
  22. _BOOTLOADER_FNAMES = {'run', 'run_d', 'runw', 'runw_d'}
  23. # Ignoring some system libraries speeds up packaging process
  24. _excludes = {
  25. # Ignore annoying warnings with Windows system DLLs.
  26. #
  27. # 'W: library kernel32.dll required via ctypes not found'
  28. # 'W: library coredll.dll required via ctypes not found'
  29. #
  30. # These these dlls has to be ignored for all operating systems
  31. # because they might be resolved when scanning code for ctypes
  32. # dependencies.
  33. r'advapi32\.dll',
  34. r'ws2_32\.dll',
  35. r'gdi32\.dll',
  36. r'oleaut32\.dll',
  37. r'shell32\.dll',
  38. r'ole32\.dll',
  39. r'coredll\.dll',
  40. r'crypt32\.dll',
  41. r'kernel32',
  42. r'kernel32\.dll',
  43. r'msvcrt\.dll',
  44. r'rpcrt4\.dll',
  45. r'user32\.dll',
  46. # Some modules tries to import the Python library.
  47. # e.g. pyreadline.console.console
  48. r'python\%s\%s',
  49. }
  50. # Regex includes - overrides excludes.
  51. # Include list is used only to override specific libraries
  52. # from exclude list.
  53. _includes = set()
  54. _win_includes = {
  55. # DLLs are from 'Microsoft Visual C++ 2010 Redistributable Package'.
  56. # http://msdn.microsoft.com/en-us/library/8kche8ah(v=vs.100).aspx
  57. #
  58. # Python 3.3 and 3.4 depends use Visual Studio C++ 2010 for Windows builds.
  59. # python33.dll depends on msvcr100.dll.
  60. #
  61. # Visual Studio C++ 2010 does not need Assembly manifests anymore and
  62. # uses C++ runtime libraries the old way - pointing to C:\Windows\System32.
  63. # It is necessary to allow inclusion of these libraries from
  64. # C:\Windows\System32.
  65. r'atl100\.dll',
  66. r'msvcr100\.dll',
  67. r'msvcp100\.dll',
  68. r'mfc100\.dll',
  69. r'mfc100u\.dll',
  70. r'mfcmifc80\.dll',
  71. r'mfcm100\.dll',
  72. r'mfcm100u\.dll',
  73. # Python 3.5 uses the Univeral C Runtime which consists of these DLLs:
  74. r'api-ms-win-core.*',
  75. r'api-ms-win-crt.*',
  76. r'ucrtbase\.dll',
  77. r'vcruntime140\.dll',
  78. # Additional DLLs from VC 2015/2017/2019 runtime. Allow these to be
  79. # collected to avoid missing-DLL errors when the target machine does
  80. # not have the VC redistributable installed.
  81. r'msvcp140\.dll',
  82. r'msvcp140_1\.dll',
  83. r'msvcp140_2\.dll',
  84. r'vcruntime140_1\.dll',
  85. r'vcomp140\.dll',
  86. r'concrt140\.dll',
  87. # Allow pythonNN.dll, pythoncomNN.dll, pywintypesNN.dll
  88. r'py(?:thon(?:com(?:loader)?)?|wintypes)\d+\.dll',
  89. }
  90. _win_excludes = {
  91. # On Windows, only .dll files can be loaded.
  92. r'.*\.so',
  93. r'.*\.dylib',
  94. # MS assembly excludes
  95. r'Microsoft\.Windows\.Common-Controls',
  96. }
  97. _unix_excludes = {
  98. r'libc\.so(\..*)?',
  99. r'libdl\.so(\..*)?',
  100. r'libm\.so(\..*)?',
  101. r'libpthread\.so(\..*)?',
  102. r'librt\.so(\..*)?',
  103. r'libthread_db\.so(\..*)?',
  104. # glibc regex excludes.
  105. r'ld-linux\.so(\..*)?',
  106. r'libBrokenLocale\.so(\..*)?',
  107. r'libanl\.so(\..*)?',
  108. r'libcidn\.so(\..*)?',
  109. r'libcrypt\.so(\..*)?',
  110. r'libnsl\.so(\..*)?',
  111. r'libnss_compat.*\.so(\..*)?',
  112. r'libnss_dns.*\.so(\..*)?',
  113. r'libnss_files.*\.so(\..*)?',
  114. r'libnss_hesiod.*\.so(\..*)?',
  115. r'libnss_nis.*\.so(\..*)?',
  116. r'libnss_nisplus.*\.so(\..*)?',
  117. r'libresolv\.so(\..*)?',
  118. r'libutil\.so(\..*)?',
  119. # graphical interface libraries come with graphical stack (see libglvnd)
  120. r'libE?(Open)?GLX?(ESv1_CM|ESv2)?(dispatch)?\.so(\..*)?',
  121. r'libdrm\.so(\..*)?',
  122. # libxcb-dri changes ABI frequently (e.g.: between Ubuntu LTS releases) and
  123. # is usually installed as dependency of the graphics stack anyway. No need
  124. # to bundle it.
  125. r'libxcb\.so(\..*)?',
  126. r'libxcb-dri.*\.so(\..*)?',
  127. }
  128. _aix_excludes = {
  129. r'libbz2\.a',
  130. r'libc\.a',
  131. r'libC\.a',
  132. r'libcrypt\.a',
  133. r'libdl\.a',
  134. r'libintl\.a',
  135. r'libpthreads\.a',
  136. r'librt\\.a',
  137. r'librtl\.a',
  138. r'libz\.a',
  139. }
  140. if compat.is_win:
  141. _includes |= _win_includes
  142. _excludes |= _win_excludes
  143. elif compat.is_aix:
  144. # The exclude list for AIX differs from other *nix platforms.
  145. _excludes |= _aix_excludes
  146. elif compat.is_unix:
  147. # Common excludes for *nix platforms -- except AIX.
  148. _excludes |= _unix_excludes
  149. class ExcludeList(object):
  150. def __init__(self):
  151. self.regex = re.compile('|'.join(_excludes), re.I)
  152. def search(self, libname):
  153. # Running re.search() on '' regex never returns None.
  154. if _excludes:
  155. return self.regex.match(os.path.basename(libname))
  156. else:
  157. return False
  158. class IncludeList(object):
  159. def __init__(self):
  160. self.regex = re.compile('|'.join(_includes), re.I)
  161. def search(self, libname):
  162. # Running re.search() on '' regex never returns None.
  163. if _includes:
  164. return self.regex.match(os.path.basename(libname))
  165. else:
  166. return False
  167. exclude_list = ExcludeList()
  168. include_list = IncludeList()
  169. if compat.is_darwin:
  170. # On Mac use macholib to decide if a binary is a system one.
  171. from macholib import util
  172. class MacExcludeList(object):
  173. def __init__(self, global_exclude_list):
  174. # Wraps the global 'exclude_list' before it is overriden
  175. # by this class.
  176. self._exclude_list = global_exclude_list
  177. def search(self, libname):
  178. # First try global exclude list. If it matches then
  179. # return it's result otherwise continue with other check.
  180. result = self._exclude_list.search(libname)
  181. if result:
  182. return result
  183. else:
  184. return util.in_system_path(libname)
  185. exclude_list = MacExcludeList(exclude_list)
  186. elif compat.is_win:
  187. class WinExcludeList(object):
  188. def __init__(self, global_exclude_list):
  189. self._exclude_list = global_exclude_list
  190. # use normpath because msys2 uses / instead of \
  191. self._windows_dir = os.path.normpath(
  192. winutils.get_windows_dir().lower()
  193. )
  194. def search(self, libname):
  195. libname = libname.lower()
  196. result = self._exclude_list.search(libname)
  197. if result:
  198. return result
  199. else:
  200. # Exclude everything from the Windows directory by default.
  201. # .. sometimes realpath changes the case of libname, lower it
  202. # .. use normpath because msys2 uses / instead of \
  203. fn = os.path.normpath(os.path.realpath(libname).lower())
  204. return fn.startswith(self._windows_dir)
  205. exclude_list = WinExcludeList(exclude_list)
  206. def include_library(libname):
  207. """
  208. Check if a dynamic library should be included with application or not.
  209. """
  210. # For configuration phase we need to have exclude / include lists None
  211. # so these checking is skipped and library gets included.
  212. if exclude_list:
  213. if exclude_list.search(libname) and not include_list.search(libname):
  214. # Library is excluded and is not overriden by include list.
  215. # It should be then excluded.
  216. return False
  217. else:
  218. # Include library
  219. return True
  220. else:
  221. # By default include library.
  222. return True
  223. # Patterns for suppressing warnings about missing dynamically linked
  224. # libraries
  225. _warning_suppressions = [
  226. # We fail to discover shiboken2 (PySide2) and shiboken6 (PySide6)
  227. # shared libraries due to the way the packages set up the search
  228. # path to the library, which is located in a separate package.
  229. # Suppress the harmless warnings to avoid confusion.
  230. r'(lib)?shiboken.*',
  231. ]
  232. # On some systems (e.g., openwrt), libc.so might point to ldd. Suppress
  233. # warnings about it.
  234. if compat.is_linux:
  235. _warning_suppressions.append(r'ldd')
  236. # Suppress false warnings on win 10 and UCRT (see issue #1566).
  237. if compat.is_win_10:
  238. _warning_suppressions.append(r'api-ms-win-crt.*')
  239. class MissingLibWarningSuppressionList:
  240. def __init__(self):
  241. self.regex = re.compile('|'.join(_warning_suppressions), re.I)
  242. def search(self, libname):
  243. # Running re.search() on '' regex never returns None.
  244. if _warning_suppressions:
  245. return self.regex.match(os.path.basename(libname))
  246. else:
  247. return False
  248. missing_lib_warning_suppression_list = MissingLibWarningSuppressionList()
  249. def warn_missing_lib(libname):
  250. """
  251. Check if a missing-library warning should be displayed for the
  252. given library name (or full path).
  253. """
  254. return not missing_lib_warning_suppression_list.search(libname)
  255. def mac_set_relative_dylib_deps(libname, distname):
  256. """
  257. On Mac OS X set relative paths to dynamic library dependencies
  258. of `libname`.
  259. Relative paths allow to avoid using environment variable DYLD_LIBRARY_PATH.
  260. There are known some issues with DYLD_LIBRARY_PATH. Relative paths is
  261. more flexible mechanism.
  262. Current location of dependend libraries is derived from the location
  263. of the library path (paths start with '@loader_path').
  264. 'distname' path of the library relative to dist directory of frozen
  265. executable. We need this to determine the level of directory
  266. level for @loader_path of binaries not found in dist directory.
  267. E.g. qt4 plugins are not in the same directory as Qt*.dylib
  268. files. Without using '@loader_path/../..' for qt plugins
  269. Mac OS X would not be able to resolve shared library
  270. dependencies and qt plugins will not be loaded.
  271. """
  272. from macholib import util
  273. from macholib.MachO import MachO
  274. # Ignore bootloader otherwise PyInstaller fails with exception like
  275. # 'ValueError: total_size > low_offset (288 > 0)'
  276. if os.path.basename(libname) in _BOOTLOADER_FNAMES:
  277. return
  278. # Determine how many directories up is the directory with shared
  279. # dynamic libraries. '../'
  280. # E.g. ./qt4_plugins/images/ -> ./../../
  281. parent_dir = ''
  282. # Check if distname is not only base filename.
  283. if os.path.dirname(distname):
  284. parent_level = len(os.path.dirname(distname).split(os.sep))
  285. parent_dir = parent_level * (os.pardir + os.sep)
  286. def match_func(pth):
  287. """
  288. For system libraries is still used absolute path. It is unchanged.
  289. """
  290. # Leave system dynamic libraries unchanged
  291. if util.in_system_path(pth):
  292. return None
  293. # The older python.org builds that use system Tcl/Tk framework
  294. # have their _tkinter.cpython-*-darwin.so library linked against
  295. # /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl and
  296. # /Library/Frameworks/Tk.framework/Versions/8.5/Tk, although the
  297. # actual frameworks are located in /System/Library/Frameworks.
  298. # Therefore, they slip through the above in_system_path() check,
  299. # and we need to exempt them manually.
  300. _exemptions = [
  301. '/Library/Frameworks/Tcl.framework/',
  302. '/Library/Frameworks/Tk.framework/'
  303. ]
  304. if any([x in pth for x in _exemptions]):
  305. return None
  306. # Use relative path to dependent dynamic libraries based on the
  307. # location of the executable.
  308. return os.path.join('@loader_path', parent_dir, os.path.basename(pth))
  309. # Rewrite mach headers with @loader_path.
  310. dll = MachO(libname)
  311. dll.rewriteLoadCommands(match_func)
  312. # Write changes into file.
  313. # Write code is based on macholib example.
  314. try:
  315. with open(dll.filename, 'rb+') as f:
  316. for header in dll.headers:
  317. f.seek(0)
  318. dll.write(f)
  319. f.seek(0, 2)
  320. f.flush()
  321. except Exception:
  322. pass