blob: 97973bce001d9a539bb5e3d2afda194cdc4f2184 [file] [log] [blame]
Martin Panterbfb15ab2016-06-14 01:27:11 +00001import os
2import shutil
Antoine Pitrou8c520272011-04-23 17:51:04 +02003import subprocess
Martin Panterbfb15ab2016-06-14 01:27:11 +00004import sys
Thomas Wouters477c8d52006-05-27 19:21:47 +00005
6# find_library(name) returns the pathname of a library, or None.
7if os.name == "nt":
Thomas Heller3eaaeb42008-05-23 17:26:46 +00008
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
Steve Dower65e4cb12014-11-22 12:54:57 -080023 if majorVersion >= 13:
24 majorVersion += 1
Thomas Heller3eaaeb42008-05-23 17:26:46 +000025 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'
Steve Dower65e4cb12014-11-22 12:54:57 -080042 elif version <= 13:
Thomas Heller3eaaeb42008-05-23 17:26:46 +000043 clibname = 'msvcr%d' % (version * 10)
Steve Dower65e4cb12014-11-22 12:54:57 -080044 else:
Steve Dower959ee7c2015-03-10 09:56:38 -070045 # CRT is no longer directly loadable. See issue23606 for the
46 # discussion about alternative approaches.
47 return None
Thomas Heller3eaaeb42008-05-23 17:26:46 +000048
49 # If python was built with in debug mode
Brett Cannoncb66eb02012-05-11 12:58:42 -040050 import importlib.machinery
51 if '_d.pyd' in importlib.machinery.EXTENSION_SUFFIXES:
Thomas Heller3eaaeb42008-05-23 17:26:46 +000052 clibname += 'd'
53 return clibname+'.dll'
54
Thomas Wouters477c8d52006-05-27 19:21:47 +000055 def find_library(name):
Thomas Heller3eaaeb42008-05-23 17:26:46 +000056 if name in ('c', 'm'):
57 return find_msvcrt()
Thomas Wouters477c8d52006-05-27 19:21:47 +000058 # See MSDN for the REAL search order.
59 for directory in os.environ['PATH'].split(os.pathsep):
60 fname = os.path.join(directory, name)
Thomas Heller00cfc372009-05-05 19:04:40 +000061 if os.path.isfile(fname):
Thomas Wouters477c8d52006-05-27 19:21:47 +000062 return fname
63 if fname.lower().endswith(".dll"):
64 continue
65 fname = fname + ".dll"
Thomas Heller00cfc372009-05-05 19:04:40 +000066 if os.path.isfile(fname):
Thomas Wouters477c8d52006-05-27 19:21:47 +000067 return fname
68 return None
69
Miss Islington (bot)c74ca532018-05-01 19:51:31 -070070elif os.name == "posix" and sys.platform == "darwin":
Thomas Wouters477c8d52006-05-27 19:21:47 +000071 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
Miss Islington (bot)c74ca532018-05-01 19:51:31 -070083elif sys.platform.startswith("aix"):
Michael Feltc5ae1692017-12-19 13:58:49 +010084 # AIX has two styles of storing shared libraries
85 # GNU auto_tools refer to these as svr4 and aix
86 # svr4 (System V Release 4) is a regular file, often with .so as suffix
87 # AIX style uses an archive (suffix .a) with members (e.g., shr.o, libssl.so)
88 # see issue#26439 and _aix.py for more details
89
90 from ctypes._aix import find_library
91
Thomas Wouters477c8d52006-05-27 19:21:47 +000092elif os.name == "posix":
93 # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
Victor Stinner7fa767e2014-03-20 09:16:38 +010094 import re, tempfile
Thomas Wouters477c8d52006-05-27 19:21:47 +000095
96 def _findLib_gcc(name):
Martin Panterbfb15ab2016-06-14 01:27:11 +000097 # Run GCC's linker with the -t (aka --trace) option and examine the
98 # library name it prints out. The GCC command will fail because we
99 # haven't supplied a proper program with main(), but that does not
100 # matter.
101 expr = os.fsencode(r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name))
102
103 c_compiler = shutil.which('gcc')
104 if not c_compiler:
105 c_compiler = shutil.which('cc')
106 if not c_compiler:
107 # No C compiler available, give up
108 return None
109
110 temp = tempfile.NamedTemporaryFile()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000111 try:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000112 args = [c_compiler, '-Wl,-t', '-o', temp.name, '-l' + name]
113
114 env = dict(os.environ)
115 env['LC_ALL'] = 'C'
116 env['LANG'] = 'C'
Martin Pantere1b34312016-06-14 04:08:30 +0000117 try:
118 proc = subprocess.Popen(args,
119 stdout=subprocess.PIPE,
120 stderr=subprocess.STDOUT,
121 env=env)
122 except OSError: # E.g. bad executable
123 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000124 with proc:
125 trace = proc.stdout.read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000126 finally:
127 try:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000128 temp.close()
Giampaolo Rodola'0166a282013-02-12 15:14:17 +0100129 except FileNotFoundError:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000130 # Raised if the file was already removed, which is the normal
131 # behaviour of GCC if linking fails
Giampaolo Rodola'0166a282013-02-12 15:14:17 +0100132 pass
Thomas Wouters477c8d52006-05-27 19:21:47 +0000133 res = re.search(expr, trace)
134 if not res:
135 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000136 return os.fsdecode(res.group(0))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000137
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000138
139 if sys.platform == "sunos5":
140 # use /usr/ccs/bin/dump on solaris
141 def _get_soname(f):
142 if not f:
143 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000144
Martin Pantere1b34312016-06-14 04:08:30 +0000145 try:
146 proc = subprocess.Popen(("/usr/ccs/bin/dump", "-Lpv", f),
147 stdout=subprocess.PIPE,
148 stderr=subprocess.DEVNULL)
149 except OSError: # E.g. command not found
150 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000151 with proc:
152 data = proc.stdout.read()
153 res = re.search(br'\[.*\]\sSONAME\s+([^\s]+)', data)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000154 if not res:
155 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000156 return os.fsdecode(res.group(1))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000157 else:
158 def _get_soname(f):
159 # assuming GNU binutils / ELF
160 if not f:
161 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000162 objdump = shutil.which('objdump')
163 if not objdump:
164 # objdump is not available, give up
165 return None
166
Martin Pantere1b34312016-06-14 04:08:30 +0000167 try:
168 proc = subprocess.Popen((objdump, '-p', '-j', '.dynamic', f),
169 stdout=subprocess.PIPE,
170 stderr=subprocess.DEVNULL)
171 except OSError: # E.g. bad executable
172 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000173 with proc:
174 dump = proc.stdout.read()
175 res = re.search(br'\sSONAME\s+([^\s]+)', dump)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000176 if not res:
177 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000178 return os.fsdecode(res.group(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000179
Victor Stinnere6747472011-08-21 00:39:18 +0200180 if sys.platform.startswith(("freebsd", "openbsd", "dragonfly")):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000181
182 def _num_version(libname):
183 # "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ]
Martin Panterbfb15ab2016-06-14 01:27:11 +0000184 parts = libname.split(b".")
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000185 nums = []
186 try:
187 while parts:
188 nums.insert(0, int(parts.pop()))
189 except ValueError:
190 pass
Martin Panterbfb15ab2016-06-14 01:27:11 +0000191 return nums or [sys.maxsize]
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000192
193 def find_library(name):
194 ename = re.escape(name)
195 expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename)
Martin Panterbfb15ab2016-06-14 01:27:11 +0000196 expr = os.fsencode(expr)
197
Martin Pantere1b34312016-06-14 04:08:30 +0000198 try:
199 proc = subprocess.Popen(('/sbin/ldconfig', '-r'),
200 stdout=subprocess.PIPE,
201 stderr=subprocess.DEVNULL)
202 except OSError: # E.g. command not found
203 data = b''
204 else:
205 with proc:
206 data = proc.stdout.read()
Martin Panterbfb15ab2016-06-14 01:27:11 +0000207
Guido van Rossumf4d4f8b2007-12-12 20:26:00 +0000208 res = re.findall(expr, data)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000209 if not res:
210 return _get_soname(_findLib_gcc(name))
Raymond Hettingerd4cb56d2008-01-30 02:55:10 +0000211 res.sort(key=_num_version)
Martin Panterbfb15ab2016-06-14 01:27:11 +0000212 return os.fsdecode(res[-1])
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000213
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500214 elif sys.platform == "sunos5":
215
216 def _findLib_crle(name, is64):
217 if not os.path.exists('/usr/bin/crle'):
218 return None
219
Martin Panterbfb15ab2016-06-14 01:27:11 +0000220 env = dict(os.environ)
221 env['LC_ALL'] = 'C'
222
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500223 if is64:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000224 args = ('/usr/bin/crle', '-64')
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500225 else:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000226 args = ('/usr/bin/crle',)
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500227
Meador Inge8988ebf2016-04-30 21:56:59 -0500228 paths = None
Martin Pantere1b34312016-06-14 04:08:30 +0000229 try:
230 proc = subprocess.Popen(args,
231 stdout=subprocess.PIPE,
232 stderr=subprocess.DEVNULL,
233 env=env)
234 except OSError: # E.g. bad executable
235 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000236 with proc:
237 for line in proc.stdout:
Nick Coghlan18896232013-11-24 12:53:50 +1000238 line = line.strip()
Martin Panterbfb15ab2016-06-14 01:27:11 +0000239 if line.startswith(b'Default Library Path (ELF):'):
240 paths = os.fsdecode(line).split()[4]
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500241
242 if not paths:
243 return None
244
245 for dir in paths.split(":"):
246 libfile = os.path.join(dir, "lib%s.so" % name)
247 if os.path.exists(libfile):
248 return libfile
249
250 return None
251
252 def find_library(name, is64 = False):
253 return _get_soname(_findLib_crle(name, is64) or _findLib_gcc(name))
254
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000255 else:
256
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000257 def _findSoname_ldconfig(name):
258 import struct
259 if struct.calcsize('l') == 4:
Larry Hastings605a62d2012-06-24 04:33:36 -0700260 machine = os.uname().machine + '-32'
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000261 else:
Larry Hastings605a62d2012-06-24 04:33:36 -0700262 machine = os.uname().machine + '-64'
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000263 mach_map = {
264 'x86_64-64': 'libc6,x86-64',
265 'ppc64-64': 'libc6,64bit',
266 'sparc64-64': 'libc6,64bit',
267 's390x-64': 'libc6,64bit',
268 'ia64-64': 'libc6,IA-64',
269 }
270 abi_type = mach_map.get(machine, 'libc6')
271
272 # XXX assuming GLIBC's ldconfig (with option -p)
Martin Panter32f2eb42016-03-17 07:50:22 +0000273 regex = r'\s+(lib%s\.[^\s]+)\s+\(%s'
Martin Panterb9f31142016-03-10 01:06:23 +0000274 regex = os.fsencode(regex % (re.escape(name), abi_type))
Antoine Pitrou8c520272011-04-23 17:51:04 +0200275 try:
276 with subprocess.Popen(['/sbin/ldconfig', '-p'],
277 stdin=subprocess.DEVNULL,
278 stderr=subprocess.DEVNULL,
279 stdout=subprocess.PIPE,
280 env={'LC_ALL': 'C', 'LANG': 'C'}) as p:
281 res = re.search(regex, p.stdout.read())
282 if res:
283 return os.fsdecode(res.group(1))
284 except OSError:
285 pass
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000286
Vinay Sajip82df3b32016-08-17 16:20:07 +0100287 def _findLib_ld(name):
288 # See issue #9998 for why this is needed
289 expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
290 cmd = ['ld', '-t']
291 libpath = os.environ.get('LD_LIBRARY_PATH')
292 if libpath:
293 for d in libpath.split(':'):
294 cmd.extend(['-L', d])
295 cmd.extend(['-o', os.devnull, '-l%s' % name])
296 result = None
297 try:
298 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
299 stderr=subprocess.PIPE,
300 universal_newlines=True)
301 out, _ = p.communicate()
302 res = re.search(expr, os.fsdecode(out))
303 if res:
304 result = res.group(0)
305 except Exception as e:
306 pass # result will be None
307 return result
308
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000309 def find_library(name):
Vinay Sajip82df3b32016-08-17 16:20:07 +0100310 # See issue #9998
311 return _findSoname_ldconfig(name) or \
312 _get_soname(_findLib_gcc(name) or _findLib_ld(name))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000313
314################################################################
315# test code
316
317def test():
318 from ctypes import cdll
319 if os.name == "nt":
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000320 print(cdll.msvcrt)
321 print(cdll.load("msvcrt"))
322 print(find_library("msvcrt"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000323
324 if os.name == "posix":
325 # find and load_version
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000326 print(find_library("m"))
327 print(find_library("c"))
328 print(find_library("bz2"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000329
Thomas Wouters477c8d52006-05-27 19:21:47 +0000330 # load
331 if sys.platform == "darwin":
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000332 print(cdll.LoadLibrary("libm.dylib"))
333 print(cdll.LoadLibrary("libcrypto.dylib"))
334 print(cdll.LoadLibrary("libSystem.dylib"))
335 print(cdll.LoadLibrary("System.framework/System"))
Michael Feltc5ae1692017-12-19 13:58:49 +0100336 # issue-26439 - fix broken test call for AIX
337 elif sys.platform.startswith("aix"):
338 from ctypes import CDLL
339 if sys.maxsize < 2**32:
Mariattac0919c22017-12-22 23:39:03 -0800340 print(f"Using CDLL(name, os.RTLD_MEMBER): {CDLL('libc.a(shr.o)', os.RTLD_MEMBER)}")
341 print(f"Using cdll.LoadLibrary(): {cdll.LoadLibrary('libc.a(shr.o)')}")
Michael Feltc5ae1692017-12-19 13:58:49 +0100342 # librpm.so is only available as 32-bit shared library
343 print(find_library("rpm"))
344 print(cdll.LoadLibrary("librpm.so"))
345 else:
Mariattac0919c22017-12-22 23:39:03 -0800346 print(f"Using CDLL(name, os.RTLD_MEMBER): {CDLL('libc.a(shr_64.o)', os.RTLD_MEMBER)}")
347 print(f"Using cdll.LoadLibrary(): {cdll.LoadLibrary('libc.a(shr_64.o)')}")
348 print(f"crypt\t:: {find_library('crypt')}")
349 print(f"crypt\t:: {cdll.LoadLibrary(find_library('crypt'))}")
350 print(f"crypto\t:: {find_library('crypto')}")
351 print(f"crypto\t:: {cdll.LoadLibrary(find_library('crypto'))}")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000352 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000353 print(cdll.LoadLibrary("libm.so"))
354 print(cdll.LoadLibrary("libcrypt.so"))
355 print(find_library("crypt"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000356
357if __name__ == "__main__":
358 test()