• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2dyld emulation
3"""
4
5import os
6from ctypes.macholib.framework import framework_info
7from ctypes.macholib.dylib import dylib_info
8from itertools import *
9try:
10    from _ctypes import _dyld_shared_cache_contains_path
11except 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#
22DEFAULT_FRAMEWORK_FALLBACK = [
23    os.path.expanduser("~/Library/Frameworks"),
24    "/Library/Frameworks",
25    "/Network/Library/Frameworks",
26    "/System/Library/Frameworks",
27]
28
29DEFAULT_LIBRARY_FALLBACK = [
30    os.path.expanduser("~/lib"),
31    "/usr/local/lib",
32    "/lib",
33    "/usr/lib",
34]
35
36def 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
44def dyld_image_suffix(env=None):
45    if env is None:
46        env = os.environ
47    return env.get('DYLD_IMAGE_SUFFIX')
48
49def dyld_framework_path(env=None):
50    return dyld_env(env, 'DYLD_FRAMEWORK_PATH')
51
52def dyld_library_path(env=None):
53    return dyld_env(env, 'DYLD_LIBRARY_PATH')
54
55def dyld_fallback_framework_path(env=None):
56    return dyld_env(env, 'DYLD_FALLBACK_FRAMEWORK_PATH')
57
58def dyld_fallback_library_path(env=None):
59    return dyld_env(env, 'DYLD_FALLBACK_LIBRARY_PATH')
60
61def 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
75def 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
92def 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
99def 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
121def 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
141def 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
166
167def test_dyld_find():
168    env = {}
169    assert dyld_find('libSystem.dylib') == '/usr/lib/libSystem.dylib'
170    assert dyld_find('System.framework/System') == '/System/Library/Frameworks/System.framework/System'
171
172if __name__ == '__main__':
173    test_dyld_find()
174