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