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