Home | History | Annotate | Download | only in macholib
      1 """
      2 dyld emulation
      3 """
      4 
      5 import os
      6 from ctypes.macholib.framework import framework_info
      7 from ctypes.macholib.dylib import dylib_info
      8 from itertools import *
      9 
     10 __all__ = [
     11     'dyld_find', 'framework_find',
     12     'framework_info', 'dylib_info',
     13 ]
     14 
     15 # These are the defaults as per man dyld(1)
     16 #
     17 DEFAULT_FRAMEWORK_FALLBACK = [
     18     os.path.expanduser("~/Library/Frameworks"),
     19     "/Library/Frameworks",
     20     "/Network/Library/Frameworks",
     21     "/System/Library/Frameworks",
     22 ]
     23 
     24 DEFAULT_LIBRARY_FALLBACK = [
     25     os.path.expanduser("~/lib"),
     26     "/usr/local/lib",
     27     "/lib",
     28     "/usr/lib",
     29 ]
     30 
     31 def dyld_env(env, var):
     32     if env is None:
     33         env = os.environ
     34     rval = env.get(var)
     35     if rval is None:
     36         return []
     37     return rval.split(':')
     38 
     39 def dyld_image_suffix(env=None):
     40     if env is None:
     41         env = os.environ
     42     return env.get('DYLD_IMAGE_SUFFIX')
     43 
     44 def dyld_framework_path(env=None):
     45     return dyld_env(env, 'DYLD_FRAMEWORK_PATH')
     46 
     47 def dyld_library_path(env=None):
     48     return dyld_env(env, 'DYLD_LIBRARY_PATH')
     49 
     50 def dyld_fallback_framework_path(env=None):
     51     return dyld_env(env, 'DYLD_FALLBACK_FRAMEWORK_PATH')
     52 
     53 def dyld_fallback_library_path(env=None):
     54     return dyld_env(env, 'DYLD_FALLBACK_LIBRARY_PATH')
     55 
     56 def dyld_image_suffix_search(iterator, env=None):
     57     """For a potential path iterator, add DYLD_IMAGE_SUFFIX semantics"""
     58     suffix = dyld_image_suffix(env)
     59     if suffix is None:
     60         return iterator
     61     def _inject(iterator=iterator, suffix=suffix):
     62         for path in iterator:
     63             if path.endswith('.dylib'):
     64                 yield path[:-len('.dylib')] + suffix + '.dylib'
     65             else:
     66                 yield path + suffix
     67             yield path
     68     return _inject()
     69 
     70 def dyld_override_search(name, env=None):
     71     # If DYLD_FRAMEWORK_PATH is set and this dylib_name is a
     72     # framework name, use the first file that exists in the framework
     73     # path if any.  If there is none go on to search the DYLD_LIBRARY_PATH
     74     # if any.
     75 
     76     framework = framework_info(name)
     77 
     78     if framework is not None:
     79         for path in dyld_framework_path(env):
     80             yield os.path.join(path, framework['name'])
     81 
     82     # If DYLD_LIBRARY_PATH is set then use the first file that exists
     83     # in the path.  If none use the original name.
     84     for path in dyld_library_path(env):
     85         yield os.path.join(path, os.path.basename(name))
     86 
     87 def dyld_executable_path_search(name, executable_path=None):
     88     # If we haven't done any searching and found a library and the
     89     # dylib_name starts with "@executable_path/" then construct the
     90     # library name.
     91     if name.startswith('@executable_path/') and executable_path is not None:
     92         yield os.path.join(executable_path, name[len('@executable_path/'):])
     93 
     94 def dyld_default_search(name, env=None):
     95     yield name
     96 
     97     framework = framework_info(name)
     98 
     99     if framework is not None:
    100         fallback_framework_path = dyld_fallback_framework_path(env)
    101         for path in fallback_framework_path:
    102             yield os.path.join(path, framework['name'])
    103 
    104     fallback_library_path = dyld_fallback_library_path(env)
    105     for path in fallback_library_path:
    106         yield os.path.join(path, os.path.basename(name))
    107 
    108     if framework is not None and not fallback_framework_path:
    109         for path in DEFAULT_FRAMEWORK_FALLBACK:
    110             yield os.path.join(path, framework['name'])
    111 
    112     if not fallback_library_path:
    113         for path in DEFAULT_LIBRARY_FALLBACK:
    114             yield os.path.join(path, os.path.basename(name))
    115 
    116 def dyld_find(name, executable_path=None, env=None):
    117     """
    118     Find a library or framework using dyld semantics
    119     """
    120     for path in dyld_image_suffix_search(chain(
    121                 dyld_override_search(name, env),
    122                 dyld_executable_path_search(name, executable_path),
    123                 dyld_default_search(name, env),
    124             ), env):
    125         if os.path.isfile(path):
    126             return path
    127     raise ValueError("dylib %s could not be found" % (name,))
    128 
    129 def framework_find(fn, executable_path=None, env=None):
    130     """
    131     Find a framework using dyld semantics in a very loose manner.
    132 
    133     Will take input such as:
    134         Python
    135         Python.framework
    136         Python.framework/Versions/Current
    137     """
    138     error = None
    139     try:
    140         return dyld_find(fn, executable_path=executable_path, env=env)
    141     except ValueError as e:
    142         error = e
    143     fmwk_index = fn.rfind('.framework')
    144     if fmwk_index == -1:
    145         fmwk_index = len(fn)
    146         fn += '.framework'
    147     fn = os.path.join(fn, os.path.basename(fn[:fmwk_index]))
    148     try:
    149         return dyld_find(fn, executable_path=executable_path, env=env)
    150     except ValueError:
    151         raise error
    152 
    153 def test_dyld_find():
    154     env = {}
    155     assert dyld_find('libSystem.dylib') == '/usr/lib/libSystem.dylib'
    156     assert dyld_find('System.framework/System') == '/System/Library/Frameworks/System.framework/System'
    157 
    158 if __name__ == '__main__':
    159     test_dyld_find()
    160