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