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