bindepend.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  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. Find external dependencies of binary libraries.
  13. """
  14. import ctypes.util
  15. import os
  16. import re
  17. import sys
  18. from glob import glob
  19. # Required for extracting eggs.
  20. import zipfile
  21. import collections
  22. from PyInstaller import compat
  23. from PyInstaller.depend import dylib, utils
  24. from PyInstaller import log as logging
  25. from PyInstaller.utils.win32 import winutils
  26. logger = logging.getLogger(__name__)
  27. seen = set()
  28. # Import windows specific stuff.
  29. if compat.is_win:
  30. from distutils.sysconfig import get_python_lib
  31. from PyInstaller.utils.win32 import winmanifest, winresource
  32. import pefile
  33. # Do not load all the directories information from the PE file
  34. pefile.fast_load = True
  35. def getfullnameof(mod, xtrapath=None):
  36. """
  37. Return the full path name of MOD.
  38. MOD is the basename of a dll or pyd.
  39. XTRAPATH is a path or list of paths to search first.
  40. Return the full path name of MOD.
  41. Will search the full Windows search path, as well as sys.path
  42. """
  43. pywin32_paths = []
  44. if compat.is_win:
  45. pywin32_paths = [os.path.join(get_python_lib(), 'pywin32_system32')]
  46. if compat.is_venv:
  47. pywin32_paths.append(
  48. os.path.join(compat.base_prefix, 'Lib', 'site-packages',
  49. 'pywin32_system32')
  50. )
  51. epath = (sys.path + # Search sys.path first!
  52. pywin32_paths +
  53. winutils.get_system_path() +
  54. compat.getenv('PATH', '').split(os.pathsep))
  55. if xtrapath is not None:
  56. if type(xtrapath) == type(''):
  57. epath.insert(0, xtrapath)
  58. else:
  59. epath = xtrapath + epath
  60. for p in epath:
  61. npth = os.path.join(p, mod)
  62. if os.path.exists(npth) and matchDLLArch(npth):
  63. return npth
  64. return ''
  65. def _getImports_pe(pth):
  66. """
  67. Find the binary dependencies of PTH.
  68. This implementation walks through the PE header
  69. and uses library pefile for that and supports
  70. 32/64bit Windows
  71. """
  72. dlls = set()
  73. # By default library pefile parses all PE information.
  74. # We are only interested in the list of dependent dlls.
  75. # Performance is improved by reading only needed information.
  76. # https://code.google.com/p/pefile/wiki/UsageExamples
  77. pe = pefile.PE(pth, fast_load=True)
  78. pe.parse_data_directories(directories=[
  79. pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
  80. pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
  81. ],
  82. forwarded_exports_only=True,
  83. import_dllnames_only=True,
  84. )
  85. # Some libraries have no other binary dependencies. Use empty list
  86. # in that case. Otherwise pefile would return None.
  87. # e.g. C:\windows\system32\kernel32.dll on Wine
  88. for entry in getattr(pe, 'DIRECTORY_ENTRY_IMPORT', []):
  89. dll_str = winutils.convert_dll_name_to_str(entry.dll)
  90. dlls.add(dll_str)
  91. # We must also read the exports table to find forwarded symbols:
  92. # http://blogs.msdn.com/b/oldnewthing/archive/2006/07/19/671238.aspx
  93. exportSymbols = getattr(pe, 'DIRECTORY_ENTRY_EXPORT', None)
  94. if exportSymbols:
  95. for sym in exportSymbols.symbols:
  96. if sym.forwarder is not None:
  97. # sym.forwarder is a bytes object. Convert it to a string.
  98. forwarder = winutils.convert_dll_name_to_str(sym.forwarder)
  99. # sym.forwarder is for example 'KERNEL32.EnterCriticalSection'
  100. dll = forwarder.split('.')[0]
  101. dlls.add(dll + ".dll")
  102. pe.close()
  103. return dlls
  104. def _extract_from_egg(toc):
  105. """
  106. Ensure all binary modules in zipped eggs get extracted and
  107. included with the frozen executable.
  108. return modified table of content
  109. """
  110. new_toc = []
  111. for item in toc:
  112. # Item is a tupple
  113. # (mod_name, path, type)
  114. modname, pth, typ = item
  115. if not os.path.isfile(pth):
  116. pth = check_extract_from_egg(pth)[0][0]
  117. # Add value to new data structure.
  118. new_toc.append((modname, pth, typ))
  119. return new_toc
  120. BindingRedirect = collections.namedtuple('BindingRedirect',
  121. 'name language arch oldVersion newVersion publicKeyToken')
  122. def match_binding_redirect(manifest, redirect):
  123. return all([
  124. manifest.name == redirect.name,
  125. manifest.version == redirect.oldVersion,
  126. manifest.language == redirect.language,
  127. manifest.processorArchitecture == redirect.arch,
  128. manifest.publicKeyToken == redirect.publicKeyToken,
  129. ])
  130. _exe_machine_type = None
  131. def matchDLLArch(filename):
  132. """
  133. Return True if the DLL given by filename matches the CPU type/architecture of the
  134. Python process running PyInstaller.
  135. Always returns True on non-Windows platforms
  136. :param filename:
  137. :type filename:
  138. :return:
  139. :rtype:
  140. """
  141. # TODO: check machine type on other platforms?
  142. if not compat.is_win:
  143. return True
  144. global _exe_machine_type
  145. try:
  146. if _exe_machine_type is None:
  147. pefilename = compat.python_executable # for exception handling
  148. exe_pe = pefile.PE(pefilename, fast_load=True)
  149. _exe_machine_type = exe_pe.FILE_HEADER.Machine
  150. exe_pe.close()
  151. pefilename = filename # for exception handling
  152. pe = pefile.PE(filename, fast_load=True)
  153. match_arch = pe.FILE_HEADER.Machine == _exe_machine_type
  154. pe.close()
  155. except pefile.PEFormatError as exc:
  156. raise SystemExit('Can not get architecture from file: %s\n'
  157. ' Reason: %s' % (pefilename, exc))
  158. return match_arch
  159. def Dependencies(lTOC, xtrapath=None, manifest=None, redirects=None):
  160. """
  161. Expand LTOC to include all the closure of binary dependencies.
  162. `LTOC` is a logical table of contents, ie, a seq of tuples (name, path).
  163. Return LTOC expanded by all the binary dependencies of the entries
  164. in LTOC, except those listed in the module global EXCLUDES
  165. `manifest` may be a winmanifest.Manifest instance for a program manifest, so
  166. that all dependent assemblies of python.exe can be added to the built exe.
  167. `redirects` may be a list. Any assembly redirects found via policy files will
  168. be added to the list as BindingRedirect objects so they can later be used
  169. to modify any manifests that reference the redirected assembly.
  170. """
  171. # Extract all necessary binary modules from Python eggs to be included
  172. # directly with PyInstaller.
  173. lTOC = _extract_from_egg(lTOC)
  174. for nm, pth, typ in lTOC:
  175. if nm.upper() in seen:
  176. continue
  177. logger.debug("Analyzing %s", pth)
  178. seen.add(nm.upper())
  179. if compat.is_win:
  180. for ftocnm, fn in getAssemblyFiles(pth, manifest, redirects):
  181. lTOC.append((ftocnm, fn, 'BINARY'))
  182. for lib, npth in selectImports(pth, xtrapath):
  183. if lib.upper() in seen or npth.upper() in seen:
  184. continue
  185. seen.add(npth.upper())
  186. lTOC.append((lib, npth, 'BINARY'))
  187. return lTOC
  188. def pkg_resources_get_default_cache():
  189. """
  190. Determine the default cache location
  191. This returns the ``PYTHON_EGG_CACHE`` environment variable, if set.
  192. Otherwise, on Windows, it returns a 'Python-Eggs' subdirectory of the
  193. 'Application Data' directory. On all other systems, it's '~/.python-eggs'.
  194. """
  195. # This function borrowed from setuptools/pkg_resources
  196. egg_cache = compat.getenv('PYTHON_EGG_CACHE')
  197. if egg_cache is not None:
  198. return egg_cache
  199. if os.name != 'nt':
  200. return os.path.expanduser('~/.python-eggs')
  201. app_data = 'Application Data' # XXX this may be locale-specific!
  202. app_homes = [
  203. (('APPDATA',), None), # best option, should be locale-safe
  204. (('USERPROFILE',), app_data),
  205. (('HOMEDRIVE', 'HOMEPATH'), app_data),
  206. (('HOMEPATH',), app_data),
  207. (('HOME',), None),
  208. (('WINDIR',), app_data), # 95/98/ME
  209. ]
  210. for keys, subdir in app_homes:
  211. dirname = ''
  212. for key in keys:
  213. if key in os.environ:
  214. dirname = os.path.join(dirname, compat.getenv(key))
  215. else:
  216. break
  217. else:
  218. if subdir:
  219. dirname = os.path.join(dirname, subdir)
  220. return os.path.join(dirname, 'Python-Eggs')
  221. else:
  222. raise RuntimeError(
  223. "Please set the PYTHON_EGG_CACHE environment variable"
  224. )
  225. def check_extract_from_egg(pth, todir=None):
  226. r"""
  227. Check if path points to a file inside a python egg file, extract the
  228. file from the egg to a cache directory (following pkg_resources
  229. convention) and return [(extracted path, egg file path, relative path
  230. inside egg file)].
  231. Otherwise, just return [(original path, None, None)].
  232. If path points to an egg file directly, return a list with all files
  233. from the egg formatted like above.
  234. Example:
  235. >>> check_extract_from_egg(r'C:\Python26\Lib\site-packages\my.egg\mymodule\my.pyd')
  236. [(r'C:\Users\UserName\AppData\Roaming\Python-Eggs\my.egg-tmp\mymodule\my.pyd',
  237. r'C:\Python26\Lib\site-packages\my.egg', r'mymodule/my.pyd')]
  238. """
  239. rv = []
  240. if os.path.altsep:
  241. pth = pth.replace(os.path.altsep, os.path.sep)
  242. components = pth.split(os.path.sep)
  243. for i, name in enumerate(components):
  244. if name.lower().endswith(".egg"):
  245. eggpth = os.path.sep.join(components[:i + 1])
  246. if os.path.isfile(eggpth):
  247. # eggs can also be directories!
  248. try:
  249. egg = zipfile.ZipFile(eggpth)
  250. except zipfile.BadZipfile as e:
  251. raise SystemExit("Error: %s %s" % (eggpth, e))
  252. if todir is None:
  253. # Use the same directory as setuptools/pkg_resources. So,
  254. # if the specific egg was accessed before (not necessarily
  255. # by pyinstaller), the extracted contents already exist
  256. # (pkg_resources puts them there) and can be used.
  257. todir = os.path.join(pkg_resources_get_default_cache(),
  258. name + "-tmp")
  259. if components[i + 1:]:
  260. members = ["/".join(components[i + 1:])]
  261. else:
  262. members = egg.namelist()
  263. for member in members:
  264. pth = os.path.join(todir, member)
  265. if not os.path.isfile(pth):
  266. dirname = os.path.dirname(pth)
  267. if not os.path.isdir(dirname):
  268. os.makedirs(dirname)
  269. with open(pth, "wb") as f:
  270. f.write(egg.read(member))
  271. rv.append((pth, eggpth, member))
  272. return rv
  273. return [(pth, None, None)]
  274. def getAssemblies(pth):
  275. """
  276. On Windows return the dependent Side-by-Side (SxS) assemblies of a binary as a
  277. list of Manifest objects.
  278. Dependent assemblies are required only by binaries compiled with MSVC 9.0.
  279. Python 2.7 and 3.2 is compiled with MSVC 9.0 and thus depends on Microsoft
  280. Redistributable runtime libraries 9.0.
  281. Python 3.3+ is compiled with version 10.0 and does not use SxS assemblies.
  282. FIXME: Can this be removed since we now only support Python 3.5+?
  283. FIXME: IS there some test-case covering this?
  284. """
  285. if pth.lower().endswith(".manifest"):
  286. return []
  287. # check for manifest file
  288. manifestnm = pth + ".manifest"
  289. if os.path.isfile(manifestnm):
  290. with open(manifestnm, "rb") as fd:
  291. res = {winmanifest.RT_MANIFEST: {1: {0: fd.read()}}}
  292. else:
  293. # check the binary for embedded manifest
  294. try:
  295. res = winmanifest.GetManifestResources(pth)
  296. except winresource.pywintypes.error as exc:
  297. if exc.args[0] == winresource.ERROR_BAD_EXE_FORMAT:
  298. logger.info('Cannot get manifest resource from non-PE '
  299. 'file %s', pth)
  300. return []
  301. raise
  302. rv = []
  303. if winmanifest.RT_MANIFEST in res and len(res[winmanifest.RT_MANIFEST]):
  304. for name in res[winmanifest.RT_MANIFEST]:
  305. for language in res[winmanifest.RT_MANIFEST][name]:
  306. # check the manifest for dependent assemblies
  307. try:
  308. manifest = winmanifest.Manifest()
  309. manifest.filename = ":".join([
  310. pth, str(winmanifest.RT_MANIFEST),
  311. str(name), str(language),
  312. ])
  313. manifest.parse_string(
  314. res[winmanifest.RT_MANIFEST][name][language], False)
  315. except Exception as exc:
  316. logger.error("Can not parse manifest resource %s, %s"
  317. " from %s", name, language, pth, exc_info=1)
  318. else:
  319. if manifest.dependentAssemblies:
  320. logger.debug("Dependent assemblies of %s:", pth)
  321. logger.debug(", ".join([assembly.getid()
  322. for assembly in
  323. manifest.dependentAssemblies]))
  324. rv.extend(manifest.dependentAssemblies)
  325. return rv
  326. def getAssemblyFiles(pth, manifest=None, redirects=None):
  327. """
  328. Find all assemblies that are dependencies of the given binary and return the files
  329. that make up the assemblies as (name, fullpath) tuples.
  330. If a WinManifest object is passed as `manifest`, also updates that manifest to
  331. reference the returned assemblies. This is done only to update the built app's .exe
  332. with the dependencies of python.exe
  333. If a list is passed as `redirects`, and binding redirects in policy files are
  334. applied when searching for assemblies, BindingRedirect objects are appended to this
  335. list.
  336. Return a list of pairs (name, fullpath)
  337. """
  338. rv = []
  339. if manifest:
  340. _depNames = set(dep.name for dep in manifest.dependentAssemblies)
  341. for assembly in getAssemblies(pth):
  342. if assembly.getid().upper() in seen:
  343. continue
  344. if manifest and assembly.name not in _depNames:
  345. # Add assembly as dependency to our final output exe's manifest
  346. logger.info("Adding %s to dependent assemblies "
  347. "of final executable\n required by %s",
  348. assembly.name, pth)
  349. manifest.dependentAssemblies.append(assembly)
  350. _depNames.add(assembly.name)
  351. if not dylib.include_library(assembly.name):
  352. logger.debug("Skipping assembly %s", assembly.getid())
  353. continue
  354. if assembly.optional:
  355. logger.debug("Skipping optional assembly %s", assembly.getid())
  356. continue
  357. from PyInstaller.config import CONF
  358. if CONF.get("win_no_prefer_redirects"):
  359. files = assembly.find_files()
  360. else:
  361. files = []
  362. if not len(files):
  363. # If no files were found, it may be the case that the required version
  364. # of the assembly is not installed, and the policy file is redirecting it
  365. # to a newer version. So, we collect the newer version instead.
  366. files = assembly.find_files(ignore_policies=False)
  367. if len(files) and redirects is not None:
  368. # New version was found, old version was not. Add a redirect in the
  369. # app configuration
  370. old_version = assembly.version
  371. new_version = assembly.get_policy_redirect()
  372. logger.info("Adding redirect %s version %s -> %s",
  373. assembly.name, old_version, new_version)
  374. redirects.append(BindingRedirect(
  375. name=assembly.name,
  376. language=assembly.language,
  377. arch=assembly.processorArchitecture,
  378. publicKeyToken=assembly.publicKeyToken,
  379. oldVersion=old_version,
  380. newVersion=new_version,
  381. ))
  382. if files:
  383. seen.add(assembly.getid().upper())
  384. for fn in files:
  385. fname, fext = os.path.splitext(fn)
  386. if fext.lower() == ".manifest":
  387. nm = assembly.name + fext
  388. else:
  389. nm = os.path.basename(fn)
  390. ftocnm = nm
  391. if assembly.language not in (None, "", "*", "neutral"):
  392. ftocnm = os.path.join(assembly.getlanguage(),
  393. ftocnm)
  394. nm, ftocnm, fn = [item.encode(sys.getfilesystemencoding())
  395. for item in
  396. (nm,
  397. ftocnm,
  398. fn)]
  399. if fn.upper() not in seen:
  400. logger.debug("Adding %s", ftocnm)
  401. seen.add(nm.upper())
  402. seen.add(fn.upper())
  403. rv.append((ftocnm, fn))
  404. else:
  405. #logger.info("skipping %s part of assembly %s dependency of %s",
  406. # ftocnm, assembly.name, pth)
  407. pass
  408. else:
  409. logger.error("Assembly %s not found", assembly.getid())
  410. # Convert items in list from 'bytes' type to 'str' type.
  411. # NOTE: With Python 3 we somehow get type 'bytes' and it
  412. # then causes other issues and failures with PyInstaller.
  413. new_rv = []
  414. for item in rv:
  415. a = item[0].decode('ascii')
  416. b = item[1].decode('ascii')
  417. new_rv.append((a, b))
  418. rv = new_rv
  419. return rv
  420. def selectImports(pth, xtrapath=None):
  421. """
  422. Return the dependencies of a binary that should be included.
  423. Return a list of pairs (name, fullpath)
  424. """
  425. rv = []
  426. if xtrapath is None:
  427. xtrapath = [os.path.dirname(pth)]
  428. else:
  429. assert isinstance(xtrapath, list)
  430. xtrapath = [os.path.dirname(pth)] + xtrapath # make a copy
  431. dlls = getImports(pth)
  432. for lib in dlls:
  433. if lib.upper() in seen:
  434. continue
  435. if not compat.is_win:
  436. # all other platforms
  437. npth = lib
  438. lib = os.path.basename(lib)
  439. else:
  440. # plain win case
  441. npth = getfullnameof(lib, xtrapath)
  442. # now npth is a candidate lib if found
  443. # check again for excludes but with regex FIXME: split the list
  444. if npth:
  445. candidatelib = npth
  446. else:
  447. candidatelib = lib
  448. if not dylib.include_library(candidatelib):
  449. if (candidatelib.find('libpython') < 0 and
  450. candidatelib.find('Python.framework') < 0):
  451. # skip libs not containing (libpython or Python.framework)
  452. if npth.upper() not in seen:
  453. logger.debug("Skipping %s dependency of %s",
  454. lib, os.path.basename(pth))
  455. continue
  456. else:
  457. pass
  458. if npth:
  459. if npth.upper() not in seen:
  460. logger.debug("Adding %s dependency of %s from %s",
  461. lib, os.path.basename(pth), npth)
  462. rv.append((lib, npth))
  463. elif dylib.warn_missing_lib(lib):
  464. logger.warning("lib not found: %s dependency of %s", lib, pth)
  465. return rv
  466. def _getImports_ldd(pth):
  467. """
  468. Find the binary dependencies of PTH.
  469. This implementation is for ldd platforms (mostly unix).
  470. """
  471. rslt = set()
  472. if compat.is_aix:
  473. # Match libs of the form
  474. # 'archivelib.a(objectmember.so/.o)'
  475. # or
  476. # 'sharedlib.so'
  477. # Will not match the fake lib '/unix'
  478. lddPattern = re.compile(r"^\s*(((?P<libarchive>(.*\.a))(?P<objectmember>\(.*\)))|((?P<libshared>(.*\.so))))$")
  479. elif compat.is_hpux:
  480. # Match libs of the form
  481. # 'sharedlib.so => full-path-to-lib
  482. # e.g.
  483. # 'libpython2.7.so => /usr/local/lib/hpux32/libpython2.7.so'
  484. lddPattern = re.compile(r"^\s+(.*)\s+=>\s+(.*)$")
  485. elif compat.is_solar:
  486. # Match libs of the form
  487. # 'sharedlib.so => full-path-to-lib
  488. # e.g.
  489. # 'libpython2.7.so.1.0 => /usr/local/lib/libpython2.7.so.1.0'
  490. # Will not match the platform specific libs starting with '/platform'
  491. lddPattern = re.compile(r"^\s+(.*)\s+=>\s+(.*)$")
  492. else:
  493. lddPattern = re.compile(r"\s*(.*?)\s+=>\s+(.*?)\s+\(.*\)")
  494. for line in compat.exec_command('ldd', pth).splitlines():
  495. m = lddPattern.search(line)
  496. if m:
  497. if compat.is_aix:
  498. libarchive = m.group('libarchive')
  499. if libarchive:
  500. # We matched an archive lib with a request for a particular
  501. # embedded shared object.
  502. # 'archivelib.a(objectmember.so/.o)'
  503. lib = libarchive
  504. name = os.path.basename(lib) + m.group('objectmember')
  505. else:
  506. # We matched a stand-alone shared library.
  507. # 'sharedlib.so'
  508. lib = m.group('libshared')
  509. name = os.path.basename(lib)
  510. elif compat.is_hpux:
  511. name, lib = m.group(1), m.group(2)
  512. else:
  513. name, lib = m.group(1), m.group(2)
  514. if name[:10] in ('linux-gate', 'linux-vdso'):
  515. # linux-gate is a fake library which does not exist and
  516. # should be ignored. See also:
  517. # http://www.trilithium.com/johan/2005/08/linux-gate/
  518. continue
  519. if compat.is_cygwin:
  520. # exclude Windows system library
  521. if lib.lower().startswith('/cygdrive/c/windows/system'):
  522. continue
  523. if os.path.exists(lib):
  524. # Add lib if it is not already found.
  525. if lib not in rslt:
  526. rslt.add(lib)
  527. elif dylib.warn_missing_lib(name):
  528. logger.warning('Cannot find %s in path %s (needed by %s)',
  529. name, lib, pth)
  530. elif line.endswith("not found"):
  531. # On glibc-based linux distributions, missing libraries
  532. # are marked with name.so => not found
  533. tokens = line.split('=>')
  534. if len(tokens) != 2:
  535. continue
  536. name = tokens[0].strip()
  537. if dylib.warn_missing_lib(name):
  538. logger.warning('Cannot find %s (needed by %s)', name, pth)
  539. return rslt
  540. def _getImports_macholib(pth):
  541. """
  542. Find the binary dependencies of PTH.
  543. This implementation is for Mac OS X and uses library macholib.
  544. """
  545. from macholib.MachO import MachO
  546. from macholib.mach_o import LC_RPATH
  547. from macholib.dyld import dyld_find
  548. from macholib.util import in_system_path
  549. rslt = set()
  550. seen = set() # Libraries read from binary headers.
  551. ## Walk through mach binary headers.
  552. m = MachO(pth)
  553. for header in m.headers:
  554. for idx, name, lib in header.walkRelocatables():
  555. # Sometimes some libraries are present multiple times.
  556. if lib not in seen:
  557. seen.add(lib)
  558. # Walk through mach binary headers and look for LC_RPATH.
  559. # macholib can't handle @rpath. LC_RPATH has to be read
  560. # from the MachO header.
  561. # TODO Do we need to remove LC_RPATH from MachO load commands?
  562. # Will it cause any harm to leave them untouched?
  563. # Removing LC_RPATH should be implemented when getting
  564. # files from the bincache if it is necessary.
  565. run_paths = set()
  566. for header in m.headers:
  567. for command in header.commands:
  568. # A command is a tupple like:
  569. # (<macholib.mach_o.load_command object at 0x>,
  570. # <macholib.mach_o.rpath_command object at 0x>,
  571. # '../lib\x00\x00')
  572. cmd_type = command[0].cmd
  573. if cmd_type == LC_RPATH:
  574. rpath = command[2].decode('utf-8')
  575. # Remove trailing '\x00' characters.
  576. # e.g. '../lib\x00\x00'
  577. rpath = rpath.rstrip('\x00')
  578. # Replace the @executable_path and @loader_path keywords
  579. # with the actual path to the binary.
  580. executable_path = os.path.dirname(pth)
  581. rpath = re.sub('^@(executable_path|loader_path|rpath)(/|$)',
  582. executable_path + r'\2', rpath)
  583. # Make rpath absolute. According to Apple doc LC_RPATH
  584. # is always relative to the binary location.
  585. rpath = os.path.normpath(os.path.join(executable_path, rpath))
  586. run_paths.update([rpath])
  587. else:
  588. # Frameworks that have this structure Name.framework/Versions/N/Name
  589. # need to to search at the same level as the framework dir.
  590. # This is specifically needed so that the QtWebEngine dependencies
  591. # can be found.
  592. if '.framework' in pth:
  593. run_paths.update(['../../../'])
  594. # for distributions like Anaconda, all of the dylibs are stored in the lib directory
  595. # of the Python distribution, not alongside of the .so's in each module's subdirectory.
  596. run_paths.add(os.path.join(compat.base_prefix, 'lib'))
  597. ## Try to find files in file system.
  598. # In cases with @loader_path or @executable_path
  599. # try to look in the same directory as the checked binary is.
  600. # This seems to work in most cases.
  601. exec_path = os.path.abspath(os.path.dirname(pth))
  602. for lib in seen:
  603. # Suppose that @rpath is not used for system libraries and
  604. # using macholib can be avoided.
  605. # macholib can't handle @rpath.
  606. if lib.startswith('@rpath'):
  607. lib = lib.replace('@rpath', '.') # Make path relative.
  608. final_lib = None # Absolute path to existing lib on disk.
  609. # Try multiple locations.
  610. for run_path in run_paths:
  611. # @rpath may contain relative value. Use exec_path as
  612. # base path.
  613. if not os.path.isabs(run_path):
  614. run_path = os.path.join(exec_path, run_path)
  615. # Stop looking for lib when found in first location.
  616. if os.path.exists(os.path.join(run_path, lib)):
  617. final_lib = os.path.abspath(os.path.join(run_path, lib))
  618. rslt.add(final_lib)
  619. break
  620. # Log warning if no existing file found.
  621. if not final_lib and dylib.warn_missing_lib(lib):
  622. logger.warning('Cannot find path %s (needed by %s)', lib, pth)
  623. # Macholib has to be used to get absolute path to libraries.
  624. else:
  625. # macholib can't handle @loader_path. It has to be
  626. # handled the same way as @executable_path.
  627. # It is also replaced by 'exec_path'.
  628. if lib.startswith('@loader_path'):
  629. lib = lib.replace('@loader_path', '@executable_path')
  630. try:
  631. lib = dyld_find(lib, executable_path=exec_path)
  632. rslt.add(lib)
  633. except ValueError:
  634. # Starting with Big Sur, system libraries are hidden. And
  635. # we do not collect system libraries on any macOS version
  636. # anyway, so suppress the corresponding error messages.
  637. if not in_system_path(lib) and dylib.warn_missing_lib(lib):
  638. logger.warning('Cannot find path %s (needed by %s)',
  639. lib, pth)
  640. return rslt
  641. def getImports(pth):
  642. """
  643. Forwards to the correct getImports implementation for the platform.
  644. """
  645. if compat.is_win:
  646. if pth.lower().endswith(".manifest"):
  647. return []
  648. try:
  649. return _getImports_pe(pth)
  650. except Exception as exception:
  651. # Assemblies can pull in files which aren't necessarily PE,
  652. # but are still needed by the assembly. Any additional binary
  653. # dependencies should already have been handled by
  654. # selectAssemblies in that case, so just warn, return an empty
  655. # list and continue.
  656. # For less specific errors also log the traceback.
  657. logger.warning('Can not get binary dependencies for file: %s', pth)
  658. logger.warning(
  659. ' Reason: %s', exception,
  660. exc_info=not isinstance(exception, pefile.PEFormatError))
  661. return []
  662. elif compat.is_darwin:
  663. return _getImports_macholib(pth)
  664. else:
  665. return _getImports_ldd(pth)
  666. def findLibrary(name):
  667. """
  668. Look for a library in the system.
  669. Emulate the algorithm used by dlopen.
  670. `name`must include the prefix, e.g. ``libpython2.4.so``
  671. """
  672. assert compat.is_unix, \
  673. "Current implementation for Unix only (Linux, Solaris, AIX, FreeBSD)"
  674. lib = None
  675. # Look in the LD_LIBRARY_PATH according to platform.
  676. if compat.is_aix:
  677. lp = compat.getenv('LIBPATH', '')
  678. elif compat.is_darwin:
  679. lp = compat.getenv('DYLD_LIBRARY_PATH', '')
  680. else:
  681. lp = compat.getenv('LD_LIBRARY_PATH', '')
  682. for path in lp.split(os.pathsep):
  683. libs = glob(os.path.join(path, name + '*'))
  684. if libs:
  685. lib = libs[0]
  686. break
  687. # Look in /etc/ld.so.cache
  688. # Solaris does not have /sbin/ldconfig. Just check if this file exists.
  689. if lib is None:
  690. utils.load_ldconfig_cache()
  691. lib = utils.LDCONFIG_CACHE.get(name)
  692. if lib:
  693. assert os.path.isfile(lib)
  694. # Look in the known safe paths.
  695. if lib is None:
  696. # Architecture independent locations.
  697. paths = ['/lib', '/usr/lib']
  698. # Architecture dependent locations.
  699. if compat.architecture == '32bit':
  700. paths.extend(['/lib32', '/usr/lib32', '/usr/lib/i386-linux-gnu'])
  701. else:
  702. paths.extend(['/lib64', '/usr/lib64', '/usr/lib/x86_64-linux-gnu'])
  703. # On Debian/Ubuntu /usr/bin/python is linked statically with libpython.
  704. # Newer Debian/Ubuntu with multiarch support putsh the libpythonX.Y.so
  705. # To paths like /usr/lib/i386-linux-gnu/.
  706. try:
  707. # Module available only in Python 2.7+
  708. import sysconfig
  709. # 'multiarchsubdir' works on Debian/Ubuntu only in Python 2.7 and 3.3+.
  710. arch_subdir = sysconfig.get_config_var('multiarchsubdir')
  711. # Ignore if None is returned.
  712. if arch_subdir:
  713. arch_subdir = os.path.basename(arch_subdir)
  714. paths.append(os.path.join('/usr/lib', arch_subdir))
  715. else:
  716. logger.debug('Multiarch directory not detected.')
  717. except ImportError:
  718. logger.debug('Multiarch directory not detected.')
  719. if compat.is_aix:
  720. paths.append('/opt/freeware/lib')
  721. elif compat.is_hpux:
  722. if compat.architecture == '32bit':
  723. paths.append('/usr/local/lib/hpux32')
  724. else:
  725. paths.append('/usr/local/lib/hpux64')
  726. elif compat.is_freebsd or compat.is_openbsd:
  727. paths.append('/usr/local/lib')
  728. for path in paths:
  729. libs = glob(os.path.join(path, name + '*'))
  730. if libs:
  731. lib = libs[0]
  732. break
  733. # give up :(
  734. if lib is None:
  735. return None
  736. # Resolve the file name into the soname
  737. if compat.is_freebsd or compat.is_aix or compat.is_openbsd:
  738. # On FreeBSD objdump doesn't show SONAME,
  739. # and on AIX objdump does not exist,
  740. # so we just return the lib we've found
  741. return lib
  742. else:
  743. dir = os.path.dirname(lib)
  744. return os.path.join(dir, _get_so_name(lib))
  745. def _get_so_name(filename):
  746. """
  747. Return the soname of a library.
  748. Soname is usefull whene there are multiple symplinks to one library.
  749. """
  750. # TODO verify that objdump works on other unixes and not Linux only.
  751. cmd = ["objdump", "-p", filename]
  752. pattern = r'\s+SONAME\s+([^\s]+)'
  753. if compat.is_solar:
  754. cmd = ["elfdump", "-d", filename]
  755. pattern = r'\s+SONAME\s+[^\s]+\s+([^\s]+)'
  756. m = re.search(pattern, compat.exec_command(*cmd))
  757. return m.group(1)
  758. def get_python_library_path():
  759. """
  760. Find dynamic Python library that will be bundled with frozen executable.
  761. NOTOE: This is a fallback option when Python library is probably linked
  762. statically with the Python executable and we need to search more for it.
  763. On Debian/Ubuntu this is the case.
  764. Return full path to Python dynamic library or None when not found.
  765. We need to know name of the Python dynamic library for the bootloader.
  766. Bootloader has to know what library to load and not trying to guess.
  767. Some linux distributions (e.g. debian-based) statically build the
  768. Python executable to the libpython, so bindepend doesn't include
  769. it in its output. In this situation let's try to find it.
  770. Darwin custom builds could possibly also have non-framework style libraries,
  771. so this method also checks for that variant as well.
  772. """
  773. def _find_lib_in_libdirs(*libdirs):
  774. for libdir in libdirs:
  775. for name in compat.PYDYLIB_NAMES:
  776. full_path = os.path.join(libdir, name)
  777. if os.path.exists(full_path):
  778. return full_path
  779. return None
  780. # If this is Microsoft App Store Python, check the compat.base_path first.
  781. # While compat.python_executable resolves to actual python.exe file, the
  782. # latter contains relative library reference that does not get properly
  783. # resolved by getfullnameof().
  784. if compat.is_ms_app_store:
  785. python_libname = _find_lib_in_libdirs(compat.base_prefix)
  786. if python_libname:
  787. return python_libname
  788. # Try to get Python library name from the Python executable. It assumes that Python
  789. # library is not statically linked.
  790. dlls = getImports(compat.python_executable)
  791. for filename in dlls:
  792. for name in compat.PYDYLIB_NAMES:
  793. if os.path.basename(filename) == name:
  794. # On Windows filename is just like 'python27.dll'. Convert it
  795. # to absolute path.
  796. if compat.is_win and not os.path.isabs(filename):
  797. filename = getfullnameof(filename)
  798. # Python library found. Return absolute path to it.
  799. return filename
  800. # Python library NOT found. Resume searching using alternative methods.
  801. # Work around for python venv having VERSION.dll rather than pythonXY.dll
  802. if compat.is_win and 'VERSION.dll' in dlls:
  803. pydll = 'python%d%d.dll' % sys.version_info[:2]
  804. return getfullnameof(pydll)
  805. # Applies only to non Windows platforms and conda.
  806. if compat.is_conda:
  807. # Conda needs to be the first here since it overrules the operating
  808. # system specific paths.
  809. python_libname = _find_lib_in_libdirs(
  810. os.path.join(compat.base_prefix, 'lib'))
  811. if python_libname:
  812. return python_libname
  813. elif compat.is_unix:
  814. for name in compat.PYDYLIB_NAMES:
  815. python_libname = findLibrary(name)
  816. if python_libname:
  817. return python_libname
  818. elif compat.is_darwin:
  819. # On MacPython, Analysis.assemble is able to find the libpython with
  820. # no additional help, asking for sys.executable dependencies.
  821. # However, this fails on system python, because the shared library
  822. # is not listed as a dependency of the binary (most probably it's
  823. # opened at runtime using some dlopen trickery).
  824. # This happens on Mac OS X when Python is compiled as Framework.
  825. # Python compiled as Framework contains same values in sys.prefix
  826. # and exec_prefix. That's why we can use just sys.prefix.
  827. # In virtualenv PyInstaller is not able to find Python library.
  828. # We need special care for this case.
  829. python_libname = _find_lib_in_libdirs(
  830. compat.base_prefix,
  831. os.path.join(compat.base_prefix, 'lib'))
  832. if python_libname:
  833. return python_libname
  834. # Python library NOT found. Provide helpful feedback.
  835. msg = """Python library not found: %s
  836. This would mean your Python installation doesn't come with proper library files.
  837. This usually happens by missing development package, or unsuitable build parameters of Python installation.
  838. * On Debian/Ubuntu, you would need to install Python development packages
  839. * apt-get install python3-dev
  840. * apt-get install python-dev
  841. * If you're building Python by yourself, please rebuild your Python with `--enable-shared` (or, `--enable-framework` on Darwin)
  842. """ % (", ".join(compat.PYDYLIB_NAMES),)
  843. raise IOError(msg)
  844. def findSystemLibrary(name):
  845. '''
  846. Given a library name, try to resolve the path to that library. If the
  847. path is already an absolute path, return that without searching.
  848. '''
  849. if os.path.isabs(name):
  850. return name
  851. if compat.is_unix:
  852. return findLibrary(name)
  853. elif compat.is_win:
  854. return getfullnameof(name)
  855. else:
  856. # This seems to work, and is similar to what we have above..
  857. return ctypes.util.find_library(name)