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