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