1"""
2dyld emulation
3"""
4
5__all__ = [
6    'dyld_framework', 'dyld_library', 'dyld_find', 'pathForFramework',
7    'infoForFramework',
8]
9
10import os, sys
11from _framework import infoForFramework
12
13
14# These are the defaults as per man dyld(1)
15#
16DEFAULT_FRAMEWORK_FALLBACK = u':'.join([
17    os.path.expanduser(u"~/Library/Frameworks"),
18    u"/Library/Frameworks",
19    u"/Network/Library/Frameworks",
20    u"/System/Library/Frameworks",
21])
22
23DEFAULT_LIBRARY_FALLBACK = u':'.join([
24    os.path.expanduser(u"~/lib"),
25    u"/usr/local/lib",
26    u"/lib",
27    u"/usr/lib",
28])
29
30def ensure_unicode(s):
31    """Not all of PyObjC understands unicode paths very well yet"""
32    if isinstance(s, str):
33        return unicode(s, 'utf8')
34    return s
35
36def injectSuffixes(iterator):
37    suffix = ensure_unicode(os.environ.get('DYLD_IMAGE_SUFFIX', None))
38    if suffix is None:
39        return iterator
40    def _inject(iterator=iterator,suffix=suffix):
41        for path in iterator:
42            if path.endswith(u'.dylib'):
43                yield path[:-6] + suffix + u'.dylib'
44            else:
45                yield path + suffix
46            yield path
47    return _inject()
48
49def dyld_framework(filename, framework_name, version=None):
50    """Find a framework using dyld semantics"""
51    filename = ensure_unicode(filename)
52    framework_name = ensure_unicode(framework_name)
53    version = ensure_unicode(version)
54
55    def _search():
56        spath = ensure_unicode(os.environ.get('DYLD_FRAMEWORK_PATH', None))
57        if spath is not None:
58            for path in spath.split(u':'):
59                if version:
60                    yield os.path.join(
61                        path, framework_name + u'.framework',
62                        u'Versions', version, framework_name
63                    )
64                else:
65                    yield os.path.join(
66                        path, framework_name + u'.framework', framework_name
67                    )
68        yield filename
69        spath = ensure_unicode(os.environ.get(
70            'DYLD_FALLBACK_FRAMEWORK_PATH', DEFAULT_FRAMEWORK_FALLBACK
71        ))
72        for path in spath.split(u':'):
73            if version:
74                yield os.path.join(
75                    path, framework_name + u'.framework', u'Versions',
76                    version, framework_name
77                )
78            else:
79                yield os.path.join(
80                    path, framework_name + u'.framework', framework_name
81                )
82
83
84    for f in injectSuffixes(_search()):
85        if os.path.exists(f):
86            return f
87    # raise ..
88    raise ImportError("Framework %s could not be found" % (framework_name,))
89
90def dyld_library(filename, libname):
91    """Find a dylib using dyld semantics"""
92    filename = ensure_unicode(filename)
93    libname = ensure_unicode(libname)
94    def _search():
95        spath = ensure_unicode(os.environ.get('DYLD_LIBRARY_PATH', None))
96        if spath is not None:
97            for path in spath.split(u':'):
98                yield os.path.join(path, libname)
99        yield filename
100        spath = ensure_unicode(os.environ.get(
101            'DYLD_FALLBACK_LIBRARY_PATH', DEFAULT_LIBRARY_FALLBACK
102        ))
103        for path in spath.split(u':'):
104            yield os.path.join(path, libname)
105    for f in injectSuffixes(_search()):
106        if os.path.exists(f):
107            return f
108    raise ValueError, "dylib %s could not be found" % (filename,)
109
110# Python version upto (at least) 2.5 do not propertly convert unicode
111# arguments to os.readlink, the code below works around that.
112if sys.version_info[:3] >= (2,6,0):
113    _realpath = os.path.realpath
114
115else:
116    def _realpath(path):
117        """
118        Unicode-safe version of os.path.realpath.
119        """
120        if isinstance(path, unicode):
121            fsenc = sys.getfilesystemencoding()
122            return os.path.realpath(path.encode(fsenc)).decode(fsenc)
123
124        return os.path.realpath(path)
125
126
127def dyld_find(filename):
128    """Generic way to locate a dyld framework or dyld"""
129    # if they passed in a framework directory, not a mach-o file
130    # then go ahead and look where one would expect to find the mach-o
131#    filename = ensure_unicode(filename)
132#    if os.path.isdir(filename):
133#        filename = os.path.join(
134#            filename,
135#            os.path.basename(filename)[:-len(os.path.splitext(filename)[-1])]
136#        )
137    filename = _realpath(filename)
138    res = infoForFramework(filename)
139    if res:
140        framework_loc, framework_name, version = res
141        return dyld_framework(filename, framework_name, version)
142    else:
143        return dyld_library(filename, os.path.basename(filename))
144
145def pathForFramework(path):
146    fpath, name, version = infoForFramework(dyld_find(path))
147    return os.path.join(fpath, name + u'.framework')
148