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