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