dylib.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2013-2022, 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. import PyInstaller.log as logging
  20. from PyInstaller import compat
  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 because they might be resolved when scanning code for
  31. # ctypes dependencies.
  32. r'advapi32\.dll',
  33. r'ws2_32\.dll',
  34. r'gdi32\.dll',
  35. r'oleaut32\.dll',
  36. r'shell32\.dll',
  37. r'ole32\.dll',
  38. r'coredll\.dll',
  39. r'crypt32\.dll',
  40. r'kernel32',
  41. r'kernel32\.dll',
  42. r'msvcrt\.dll',
  43. r'rpcrt4\.dll',
  44. r'user32\.dll',
  45. # Some modules tries to import the Python library. e.g. pyreadline.console.console
  46. r'python\%s\%s',
  47. }
  48. # Regex includes - overrides excludes. Include list is used only to override specific libraries from exclude list.
  49. _includes = set()
  50. _win_includes = {
  51. # We need to allow collection of Visual Studio C++ (VC) runtime DLLs from system directories in order to avoid
  52. # missing DLL errors when the frozen application is run on a system that does not have the corresponding VC
  53. # runtime installed. The VC runtime DLLs may be dependencies of python shared library itself or of extension
  54. # modules provided by 3rd party packages.
  55. # Visual Studio 2010 (VC10) runtime
  56. # http://msdn.microsoft.com/en-us/library/8kche8ah(v=vs.100).aspx
  57. r'atl100\.dll',
  58. r'msvcr100\.dll',
  59. r'msvcp100\.dll',
  60. r'mfc100\.dll',
  61. r'mfc100u\.dll',
  62. r'mfcmifc80\.dll',
  63. r'mfcm100\.dll',
  64. r'mfcm100u\.dll',
  65. # Visual Studio 2012 (VC11) runtime
  66. # https://docs.microsoft.com/en-us/visualstudio/releases/2013/2012-redistribution-vs
  67. #
  68. # VC110.ATL
  69. r'atl110\.dll',
  70. # VC110.CRT
  71. r'msvcp110\.dll',
  72. r'msvcr110\.dll',
  73. r'vccorlib110\.dll',
  74. # VC110.CXXAMP
  75. r'vcamp110\.dll',
  76. # VC110.MFC
  77. r'mfc110\.dll',
  78. r'mfc110u\.dll',
  79. r'mfcm110\.dll',
  80. r'mfcm110u\.dll',
  81. # VC110.MFCLOC
  82. r'mfc110chs\.dll',
  83. r'mfc110cht\.dll',
  84. r'mfc110enu\.dll',
  85. r'mfc110esn\.dll',
  86. r'mfc110deu\.dll',
  87. r'mfc110fra\.dll',
  88. r'mfc110ita\.dll',
  89. r'mfc110jpn\.dll',
  90. r'mfc110kor\.dll',
  91. r'mfc110rus\.dll',
  92. # VC110.OpenMP
  93. r'vcomp110\.dll',
  94. # DIA SDK
  95. r'msdia110\.dll',
  96. # Visual Studio 2013 (VC12) runtime
  97. # https://docs.microsoft.com/en-us/visualstudio/releases/2013/2013-redistribution-vs
  98. #
  99. # VC120.CRT
  100. r'msvcp120\.dll',
  101. r'msvcr120\.dll',
  102. r'vccorlib120\.dll',
  103. # VC120.CXXAMP
  104. r'vcamp120\.dll',
  105. # VC120.MFC
  106. r'mfc120\.dll',
  107. r'mfc120u\.dll',
  108. r'mfcm120\.dll',
  109. r'mfcm120u\.dll',
  110. # VC120.MFCLOC
  111. r'mfc120chs\.dll',
  112. r'mfc120cht\.dll',
  113. r'mfc120deu\.dll',
  114. r'mfc120enu\.dll',
  115. r'mfc120esn\.dll',
  116. r'mfc120fra\.dll',
  117. r'mfc120ita\.dll',
  118. r'mfc120jpn\.dll',
  119. r'mfc120kor\.dll',
  120. r'mfc120rus\.dll',
  121. # VC120.OPENMP
  122. r'vcomp120\.dll',
  123. # DIA SDK
  124. r'msdia120\.dll',
  125. # Cpp REST Windows SDK
  126. r'casablanca120.winrt\.dll',
  127. # Mobile Services Cpp Client
  128. r'zumosdk120.winrt\.dll',
  129. # Cpp REST SDK
  130. r'casablanca120\.dll',
  131. # Universal C Runtime Library (since Visual Studio 2015)
  132. #
  133. # NOTE: these should be put under a switch, as they need not to be bundled if deployment target is Windows 10
  134. # and later, as "UCRT is now a system component in Windows 10 and later, managed by Windows Update".
  135. # (https://docs.microsoft.com/en-us/cpp/windows/determining-which-dlls-to-redistribute?view=msvc-170)
  136. # And as discovered in #6326, Windows prefers system-installed version over the bundled one, anyway
  137. # (see https://docs.microsoft.com/en-us/cpp/windows/universal-crt-deployment?view=msvc-170#local-deployment).
  138. r'api-ms-win-core.*',
  139. r'api-ms-win-crt.*',
  140. r'ucrtbase\.dll',
  141. # Visual Studio 2015/2017/2019/2022 (VC14) runtime
  142. # https://docs.microsoft.com/en-us/visualstudio/releases/2022/redistribution
  143. #
  144. # VC141.CRT/VC142.CRT/VC143.CRT
  145. r'concrt140\.dll',
  146. r'msvcp140\.dll',
  147. r'msvcp140_1\.dll',
  148. r'msvcp140_2\.dll',
  149. r'msvcp140_atomic_wait\.dll',
  150. r'msvcp140_codecvt_ids\.dll',
  151. r'vccorlib140\.dll',
  152. r'vcruntime140\.dll',
  153. r'vcruntime140_1\.dll',
  154. # VC141.CXXAMP/VC142.CXXAMP/VC143.CXXAMP
  155. r'vcamp140\.dll',
  156. # VC141.OpenMP/VC142.OpenMP/VC143.OpenMP
  157. r'vcomp140\.dll',
  158. # DIA SDK
  159. r'msdia140\.dll',
  160. # Allow pythonNN.dll, pythoncomNN.dll, pywintypesNN.dll
  161. r'py(?:thon(?:com(?:loader)?)?|wintypes)\d+\.dll',
  162. }
  163. _win_excludes = {
  164. # On Windows, only .dll files can be loaded.
  165. r'.*\.so',
  166. r'.*\.dylib',
  167. # MS assembly excludes
  168. r'Microsoft\.Windows\.Common-Controls',
  169. }
  170. _unix_excludes = {
  171. r'libc\.so(\..*)?',
  172. r'libdl\.so(\..*)?',
  173. r'libm\.so(\..*)?',
  174. r'libpthread\.so(\..*)?',
  175. r'librt\.so(\..*)?',
  176. r'libthread_db\.so(\..*)?',
  177. # glibc regex excludes.
  178. r'ld-linux\.so(\..*)?',
  179. r'libBrokenLocale\.so(\..*)?',
  180. r'libanl\.so(\..*)?',
  181. r'libcidn\.so(\..*)?',
  182. r'libcrypt\.so(\..*)?',
  183. r'libnsl\.so(\..*)?',
  184. r'libnss_compat.*\.so(\..*)?',
  185. r'libnss_dns.*\.so(\..*)?',
  186. r'libnss_files.*\.so(\..*)?',
  187. r'libnss_hesiod.*\.so(\..*)?',
  188. r'libnss_nis.*\.so(\..*)?',
  189. r'libnss_nisplus.*\.so(\..*)?',
  190. r'libresolv\.so(\..*)?',
  191. r'libutil\.so(\..*)?',
  192. # graphical interface libraries come with graphical stack (see libglvnd)
  193. r'libE?(Open)?GLX?(ESv1_CM|ESv2)?(dispatch)?\.so(\..*)?',
  194. r'libdrm\.so(\..*)?',
  195. # libxcb-dri changes ABI frequently (e.g.: between Ubuntu LTS releases) and is usually installed as dependency of
  196. # the graphics stack anyway. No need to bundle it.
  197. r'libxcb\.so(\..*)?',
  198. r'libxcb-dri.*\.so(\..*)?',
  199. }
  200. _aix_excludes = {
  201. r'libbz2\.a',
  202. r'libc\.a',
  203. r'libC\.a',
  204. r'libcrypt\.a',
  205. r'libdl\.a',
  206. r'libintl\.a',
  207. r'libpthreads\.a',
  208. r'librt\\.a',
  209. r'librtl\.a',
  210. r'libz\.a',
  211. }
  212. if compat.is_win:
  213. _includes |= _win_includes
  214. _excludes |= _win_excludes
  215. elif compat.is_aix:
  216. # The exclude list for AIX differs from other *nix platforms.
  217. _excludes |= _aix_excludes
  218. elif compat.is_unix:
  219. # Common excludes for *nix platforms -- except AIX.
  220. _excludes |= _unix_excludes
  221. class ExcludeList:
  222. def __init__(self):
  223. self.regex = re.compile('|'.join(_excludes), re.I)
  224. def search(self, libname):
  225. # Running re.search() on '' regex never returns None.
  226. if _excludes:
  227. return self.regex.match(os.path.basename(libname))
  228. else:
  229. return False
  230. class IncludeList:
  231. def __init__(self):
  232. self.regex = re.compile('|'.join(_includes), re.I)
  233. def search(self, libname):
  234. # Running re.search() on '' regex never returns None.
  235. if _includes:
  236. return self.regex.match(os.path.basename(libname))
  237. else:
  238. return False
  239. exclude_list = ExcludeList()
  240. include_list = IncludeList()
  241. if compat.is_darwin:
  242. # On Mac use macholib to decide if a binary is a system one.
  243. from macholib import util
  244. class MacExcludeList:
  245. def __init__(self, global_exclude_list):
  246. # Wraps the global 'exclude_list' before it is overridden by this class.
  247. self._exclude_list = global_exclude_list
  248. def search(self, libname):
  249. # First try global exclude list. If it matches, return its result; otherwise continue with other check.
  250. result = self._exclude_list.search(libname)
  251. if result:
  252. return result
  253. else:
  254. return util.in_system_path(libname)
  255. exclude_list = MacExcludeList(exclude_list)
  256. elif compat.is_win:
  257. class WinExcludeList:
  258. def __init__(self, global_exclude_list):
  259. self._exclude_list = global_exclude_list
  260. # use normpath because msys2 uses / instead of \
  261. self._windows_dir = os.path.normpath(winutils.get_windows_dir().lower())
  262. def search(self, libname):
  263. libname = libname.lower()
  264. result = self._exclude_list.search(libname)
  265. if result:
  266. return result
  267. else:
  268. # Exclude everything from the Windows directory by default.
  269. # .. sometimes realpath changes the case of libname, lower it
  270. # .. use normpath because msys2 uses / instead of \
  271. fn = os.path.normpath(os.path.realpath(libname).lower())
  272. return fn.startswith(self._windows_dir)
  273. exclude_list = WinExcludeList(exclude_list)
  274. _seen_wine_dlls = set() # Used for warning tracking in include_library()
  275. def include_library(libname):
  276. """
  277. Check if the dynamic library should be included with application or not.
  278. """
  279. if exclude_list:
  280. if exclude_list.search(libname) and not include_list.search(libname):
  281. # Library is excluded and is not overridden by include list. It should be excluded.
  282. return False
  283. # If we are running under Wine and the library is a Wine built-in DLL, ensure that it is always excluded. Typically,
  284. # excluding a DLL leads to an incomplete bundle and run-time errors when the said DLL is not installed on the target
  285. # system. However, having Wine built-in DLLs collected is even more detrimental, as they usually provide Wine's
  286. # implementation of low-level functionality, and therefore cannot be used on actual Windows (i.e., system libraries
  287. # from the C:\Windows\system32 directory that might end up collected due to ``_win_includes`` list; a prominent
  288. # example are VC runtime DLLs, for which Wine provides their own implementation, unless user explicitly installs
  289. # Microsoft's VC redistributable package in their Wine environment). Therefore, excluding the Wine built-in DLLs
  290. # actually improves the chances of the bundle running on Windows, or at least makes the issue easier to debug by
  291. # turning it into the "standard" missing DLL problem. Exclusion should not affect the bundle's ability to run under
  292. # Wine itself, as the excluded DLLs are available there.
  293. if compat.is_win_wine and compat.is_wine_dll(libname):
  294. if libname not in _seen_wine_dlls:
  295. logger.warning("Excluding Wine built-in DLL: %s", libname) # displayed only if DLL would have been included
  296. _seen_wine_dlls.add(libname) # display only once for each DLL
  297. return False
  298. return True
  299. # Patterns for suppressing warnings about missing dynamically linked libraries
  300. _warning_suppressions = [
  301. # We fail to discover shiboken2 (PySide2) and shiboken6 (PySide6) shared libraries due to the way the packages set
  302. # up the search path to the library, which is located in a separate package. Suppress the harmless warnings to avoid
  303. # confusion.
  304. r'(lib)?shiboken.*',
  305. ]
  306. # On some systems (e.g., openwrt), libc.so might point to ldd. Suppress warnings about it.
  307. if compat.is_linux:
  308. _warning_suppressions.append(r'ldd')
  309. # Suppress false warnings on win 10 and UCRT (see issue #1566).
  310. if compat.is_win_10:
  311. _warning_suppressions.append(r'api-ms-win-crt.*')
  312. _warning_suppressions.append(r'api-ms-win-core.*')
  313. class MissingLibWarningSuppressionList:
  314. def __init__(self):
  315. self.regex = re.compile('|'.join(_warning_suppressions), re.I)
  316. def search(self, libname):
  317. # Running re.search() on '' regex never returns None.
  318. if _warning_suppressions:
  319. return self.regex.match(os.path.basename(libname))
  320. else:
  321. return False
  322. missing_lib_warning_suppression_list = MissingLibWarningSuppressionList()
  323. def warn_missing_lib(libname):
  324. """
  325. Check if a missing-library warning should be displayed for the given library name (or full path).
  326. """
  327. return not missing_lib_warning_suppression_list.search(libname)
  328. def mac_set_relative_dylib_deps(libname, distname):
  329. """
  330. On Mac OS set relative paths to dynamic library dependencies of `libname`.
  331. Relative paths allow to avoid using environment variable DYLD_LIBRARY_PATH. There are known some issues with
  332. DYLD_LIBRARY_PATH. Relative paths is more flexible mechanism.
  333. Current location of dependent libraries is derived from the location of the library path (paths start with
  334. '@loader_path').
  335. 'distname' path of the library relative to dist directory of frozen executable. We need this to determine the level
  336. of directory level for @loader_path of binaries not found in dist directory.
  337. For example, Qt5 plugins are not in the same directory as Qt*.dylib files. Without using
  338. '@loader_path/../..' for Qt plugins, Mac OS would not be able to resolve shared library dependencies,
  339. and Qt plugins will not be loaded.
  340. """
  341. from macholib import util
  342. from macholib.MachO import MachO
  343. # Ignore bootloader; otherwise PyInstaller fails with exception like
  344. # 'ValueError: total_size > low_offset (288 > 0)'
  345. if os.path.basename(libname) in _BOOTLOADER_FNAMES:
  346. return
  347. # Determine how many directories up ('../') is the directory with shared dynamic libraries.
  348. # E.g., ./qt4_plugins/images/ -> ./../../
  349. parent_dir = ''
  350. # Check if distname is not only base filename.
  351. if os.path.dirname(distname):
  352. parent_level = len(os.path.dirname(distname).split(os.sep))
  353. parent_dir = parent_level * (os.pardir + os.sep)
  354. def match_func(pth):
  355. """
  356. For system libraries is still used absolute path. It is unchanged.
  357. """
  358. # Leave system dynamic libraries unchanged.
  359. if util.in_system_path(pth):
  360. return None
  361. # The older python.org builds that use system Tcl/Tk framework have their _tkinter.cpython-*-darwin.so
  362. # library linked against /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl and
  363. # /Library/Frameworks/Tk.framework/Versions/8.5/Tk, although the actual frameworks are located in
  364. # /System/Library/Frameworks. Therefore, they slip through the above in_system_path() check, and we need to
  365. # exempt them manually.
  366. _exemptions = [
  367. '/Library/Frameworks/Tcl.framework/',
  368. '/Library/Frameworks/Tk.framework/',
  369. ]
  370. if any([x in pth for x in _exemptions]):
  371. return None
  372. # Use relative path to dependent dynamic libraries based on the location of the executable.
  373. return os.path.join('@loader_path', parent_dir, os.path.basename(pth))
  374. # Rewrite mach headers with @loader_path.
  375. dll = MachO(libname)
  376. dll.rewriteLoadCommands(match_func)
  377. # Write changes into file. Write code is based on macholib example.
  378. try:
  379. with open(dll.filename, 'rb+') as f:
  380. for header in dll.headers:
  381. f.seek(0)
  382. dll.write(f)
  383. f.seek(0, 2)
  384. f.flush()
  385. except Exception:
  386. pass