1######################################################################
2#  This file should be kept compatible with Python 2.3, see PEP 291. #
3######################################################################
4import sys, os
5
6# find_library(name) returns the pathname of a library, or None.
7if os.name == "nt":
8
9    def _get_build_version():
10        #***********************************************************
11        # NOTE: As example for GCC(mingw) build sys.version return:
12        # '2.7a0 (trunk:<REVISION>M, <DATE>, <TIME>) \n[GCC 3.4.5 (mingw special)]'
13        # '2.7a0 (trunk:<REVISION>M, <DATE>, <TIME>) \n[GCC 4.4.0]'
14        #***********************************************************
15        """Return the version of MSVC that was used to build Python.
16
17        For Python 2.3 and up, the version number is included in
18        sys.version.  For earlier versions, assume the compiler is MSVC 6.
19        """
20        # This function was copied from Lib/distutils/msvccompiler.py
21        prefix = "MSC v."
22        i = sys.version.find(prefix)
23        if i == -1:
24            return 6
25        i = i + len(prefix)
26        s, rest = sys.version[i:].split(" ", 1)
27        majorVersion = int(s[:-2]) - 6
28        minorVersion = int(s[2:3]) / 10.0
29        # I don't think paths are affected by minor version in version 6
30        if majorVersion == 6:
31            minorVersion = 0
32        if majorVersion >= 6:
33            return majorVersion + minorVersion
34        # else we don't know what version of the compiler this is
35        return None
36
37    def find_msvcrt():
38        #************************************************************
39        # FIXME: For GCC(mingw) runtime don't depend from compiler
40        # version ;). We may use -D__MSVCRT_VERSION__ to detect which
41        # verion is requested by user, but the name of the library
42        # to be default.
43        # As example WXP is with version 7.0 of msvcrt.dll.
44        # Anyway since _get_build_version return 6 in most(standard)
45        # cases this method will return msvcrt{d}. May be not so bad.
46        #************************************************************
47        """Return the name of the VC runtime dll"""
48        version = _get_build_version()
49        if version is None:
50            # better be safe than sorry
51            return None
52        if version <= 6:
53            clibname = 'msvcrt'
54        else:
55            clibname = 'msvcr%d' % (version * 10)
56
57        # If python was built with in debug mode
58        import imp
59        if imp.get_suffixes()[0][0] == '_d.pyd':
60            clibname += 'd'
61        return clibname+'.dll'
62
63    def find_library(name):
64        if name in ('c', 'm'):
65            return find_msvcrt()
66        # See MSDN for the REAL search order.
67        for directory in os.environ['PATH'].split(os.pathsep):
68            fname = os.path.join(directory, name)
69            if os.path.isfile(fname):
70                return fname
71            if fname.lower().endswith(".dll"):
72                continue
73            fname = fname + ".dll"
74            if os.path.isfile(fname):
75                return fname
76        return None
77
78if os.name == "ce":
79    # search path according to MSDN:
80    # - absolute path specified by filename
81    # - The .exe launch directory
82    # - the Windows directory
83    # - ROM dll files (where are they?)
84    # - OEM specified search path: HKLM\Loader\SystemPath
85    def find_library(name):
86        return name
87
88if os.name == "posix" and sys.platform == "darwin":
89    from ctypes.macholib.dyld import dyld_find as _dyld_find
90    def find_library(name):
91        possible = ['lib%s.dylib' % name,
92                    '%s.dylib' % name,
93                    '%s.framework/%s' % (name, name)]
94        for name in possible:
95            try:
96                return _dyld_find(name)
97            except ValueError:
98                continue
99        return None
100
101elif os.name == "posix":
102    # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
103    import re, tempfile, errno
104
105    def _findLib_gcc(name):
106        expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
107        fdout, ccout = tempfile.mkstemp()
108        os.close(fdout)
109        cmd = 'if type gcc >/dev/null 2>&1; then CC=gcc; elif type cc >/dev/null 2>&1; then CC=cc;else exit 10; fi;' \
110              '$CC -Wl,-t -o ' + ccout + ' 2>&1 -l' + name
111        try:
112            f = os.popen(cmd)
113            try:
114                trace = f.read()
115            finally:
116                rv = f.close()
117        finally:
118            try:
119                os.unlink(ccout)
120            except OSError, e:
121                if e.errno != errno.ENOENT:
122                    raise
123        if rv == 10:
124            raise OSError, 'gcc or cc command not found'
125        res = re.search(expr, trace)
126        if not res:
127            return None
128        return res.group(0)
129
130
131    if sys.platform == "sunos5":
132        # use /usr/ccs/bin/dump on solaris
133        def _get_soname(f):
134            if not f:
135                return None
136            cmd = "/usr/ccs/bin/dump -Lpv 2>/dev/null " + f
137            f = os.popen(cmd)
138            try:
139                data = f.read()
140            finally:
141                f.close()
142            res = re.search(r'\[.*\]\sSONAME\s+([^\s]+)', data)
143            if not res:
144                return None
145            return res.group(1)
146    else:
147        def _get_soname(f):
148            # assuming GNU binutils / ELF
149            if not f:
150                return None
151            cmd = 'if ! type objdump >/dev/null 2>&1; then exit 10; fi;' \
152                  "objdump -p -j .dynamic 2>/dev/null " + f
153            f = os.popen(cmd)
154            dump = f.read()
155            rv = f.close()
156            if rv == 10:
157                raise OSError, 'objdump command not found'
158            f = os.popen(cmd)
159            try:
160                data = f.read()
161            finally:
162                f.close()
163            res = re.search(r'\sSONAME\s+([^\s]+)', data)
164            if not res:
165                return None
166            return res.group(1)
167
168    if (sys.platform.startswith("freebsd")
169        or sys.platform.startswith("openbsd")
170        or sys.platform.startswith("dragonfly")):
171
172        def _num_version(libname):
173            # "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ]
174            parts = libname.split(".")
175            nums = []
176            try:
177                while parts:
178                    nums.insert(0, int(parts.pop()))
179            except ValueError:
180                pass
181            return nums or [ sys.maxint ]
182
183        def find_library(name):
184            ename = re.escape(name)
185            expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename)
186            f = os.popen('/sbin/ldconfig -r 2>/dev/null')
187            try:
188                data = f.read()
189            finally:
190                f.close()
191            res = re.findall(expr, data)
192            if not res:
193                return _get_soname(_findLib_gcc(name))
194            res.sort(cmp= lambda x,y: cmp(_num_version(x), _num_version(y)))
195            return res[-1]
196
197    elif sys.platform == "sunos5":
198
199        def _findLib_crle(name, is64):
200            if not os.path.exists('/usr/bin/crle'):
201                return None
202
203            if is64:
204                cmd = 'env LC_ALL=C /usr/bin/crle -64 2>/dev/null'
205            else:
206                cmd = 'env LC_ALL=C /usr/bin/crle 2>/dev/null'
207
208            for line in os.popen(cmd).readlines():
209                line = line.strip()
210                if line.startswith('Default Library Path (ELF):'):
211                    paths = line.split()[4]
212
213            if not paths:
214                return None
215
216            for dir in paths.split(":"):
217                libfile = os.path.join(dir, "lib%s.so" % name)
218                if os.path.exists(libfile):
219                    return libfile
220
221            return None
222
223        def find_library(name, is64 = False):
224            return _get_soname(_findLib_crle(name, is64) or _findLib_gcc(name))
225
226    else:
227
228        def _findSoname_ldconfig(name):
229            import struct
230            if struct.calcsize('l') == 4:
231                machine = os.uname()[4] + '-32'
232            else:
233                machine = os.uname()[4] + '-64'
234            mach_map = {
235                'x86_64-64': 'libc6,x86-64',
236                'ppc64-64': 'libc6,64bit',
237                'sparc64-64': 'libc6,64bit',
238                's390x-64': 'libc6,64bit',
239                'ia64-64': 'libc6,IA-64',
240                }
241            abi_type = mach_map.get(machine, 'libc6')
242
243            # XXX assuming GLIBC's ldconfig (with option -p)
244            expr = r'\s+(lib%s\.[^\s]+)\s+\(%s' % (re.escape(name), abi_type)
245            f = os.popen('/sbin/ldconfig -p 2>/dev/null')
246            try:
247                data = f.read()
248            finally:
249                f.close()
250            res = re.search(expr, data)
251            if not res:
252                return None
253            return res.group(1)
254
255        def find_library(name):
256            return _findSoname_ldconfig(name) or _get_soname(_findLib_gcc(name))
257
258################################################################
259# test code
260
261def test():
262    from ctypes import cdll
263    if os.name == "nt":
264        print cdll.msvcrt
265        print cdll.load("msvcrt")
266        print find_library("msvcrt")
267
268    if os.name == "posix":
269        # find and load_version
270        print find_library("m")
271        print find_library("c")
272        print find_library("bz2")
273
274        # getattr
275##        print cdll.m
276##        print cdll.bz2
277
278        # load
279        if sys.platform == "darwin":
280            print cdll.LoadLibrary("libm.dylib")
281            print cdll.LoadLibrary("libcrypto.dylib")
282            print cdll.LoadLibrary("libSystem.dylib")
283            print cdll.LoadLibrary("System.framework/System")
284        else:
285            print cdll.LoadLibrary("libm.so")
286            print cdll.LoadLibrary("libcrypt.so")
287            print find_library("crypt")
288
289if __name__ == "__main__":
290    test()
291