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