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 try:
10 from _ctypes import _dyld_shared_cache_contains_path
11 except 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 #
22 DEFAULT_FRAMEWORK_FALLBACK = [
23 os.path.expanduser("~/Library/Frameworks"),
24 "/Library/Frameworks",
25 "/Network/Library/Frameworks",
26 "/System/Library/Frameworks",
27 ]
28
29 DEFAULT_LIBRARY_FALLBACK = [
30 os.path.expanduser("~/lib"),
31 "/usr/local/lib",
32 "/lib",
33 "/usr/lib",
34 ]
35
36 def 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
44 def dyld_image_suffix(env=None):
45 if env is None:
46 env = os.environ
47 return env.get('DYLD_IMAGE_SUFFIX')
48
49 def dyld_framework_path(env=None):
50 return dyld_env(env, 'DYLD_FRAMEWORK_PATH')
51
52 def dyld_library_path(env=None):
53 return dyld_env(env, 'DYLD_LIBRARY_PATH')
54
55 def dyld_fallback_framework_path(env=None):
56 return dyld_env(env, 'DYLD_FALLBACK_FRAMEWORK_PATH')
57
58 def dyld_fallback_library_path(env=None):
59 return dyld_env(env, 'DYLD_FALLBACK_LIBRARY_PATH')
60
61 def 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
75 def 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
92 def 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
99 def 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
121 def 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
141 def 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