blob: 011a3717cf4952271e9ccd9863b904f82af76a59 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001######################################################################
2# This file should be kept compatible with Python 2.3, see PEP 291. #
3######################################################################
Thomas Hellerbabddfc2006-03-08 19:56:54 +00004"""
5dyld emulation
6"""
7
8import os
Guido van Rossumbf12cdb2006-08-17 20:24:18 +00009from ctypes.macholib.framework import framework_info
10from ctypes.macholib.dylib import dylib_info
Thomas Hellerbabddfc2006-03-08 19:56:54 +000011from 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#
20DEFAULT_FRAMEWORK_FALLBACK = [
21 os.path.expanduser("~/Library/Frameworks"),
22 "/Library/Frameworks",
23 "/Network/Library/Frameworks",
24 "/System/Library/Frameworks",
25]
26
27DEFAULT_LIBRARY_FALLBACK = [
28 os.path.expanduser("~/lib"),
29 "/usr/local/lib",
30 "/lib",
31 "/usr/lib",
32]
33
34def 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
40def 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
48def dyld_image_suffix(env=None):
49 if env is None:
50 env = os.environ
51 return env.get('DYLD_IMAGE_SUFFIX')
52
53def dyld_framework_path(env=None):
54 return dyld_env(env, 'DYLD_FRAMEWORK_PATH')
55
56def dyld_library_path(env=None):
57 return dyld_env(env, 'DYLD_LIBRARY_PATH')
58
59def dyld_fallback_framework_path(env=None):
60 return dyld_env(env, 'DYLD_FALLBACK_FRAMEWORK_PATH')
61
62def dyld_fallback_library_path(env=None):
63 return dyld_env(env, 'DYLD_FALLBACK_LIBRARY_PATH')
64
65def 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
79def 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
96def 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
103def 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
125def 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
140def 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)
Guido van Rossumb940e112007-01-10 16:19:56 +0000151 except ValueError as e:
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000152 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
163def 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
168if __name__ == '__main__':
169 test_dyld_find()