blob: f5c6b266b69e7b9e9c9b11d9109d6ffa6fecd091 [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
70if os.name == "ce":
71 # search path according to MSDN:
72 # - absolute path specified by filename
73 # - The .exe launch directory
74 # - the Windows directory
75 # - ROM dll files (where are they?)
76 # - OEM specified search path: HKLM\Loader\SystemPath
77 def find_library(name):
78 return name
79
80if os.name == "posix" and sys.platform == "darwin":
81 from ctypes.macholib.dyld import dyld_find as _dyld_find
82 def find_library(name):
83 possible = ['lib%s.dylib' % name,
84 '%s.dylib' % name,
85 '%s.framework/%s' % (name, name)]
86 for name in possible:
87 try:
88 return _dyld_find(name)
89 except ValueError:
90 continue
91 return None
92
93elif os.name == "posix":
94 # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
Victor Stinner7fa767e2014-03-20 09:16:38 +010095 import re, tempfile
Thomas Wouters477c8d52006-05-27 19:21:47 +000096
97 def _findLib_gcc(name):
Martin Panterbfb15ab2016-06-14 01:27:11 +000098 # Run GCC's linker with the -t (aka --trace) option and examine the
99 # library name it prints out. The GCC command will fail because we
100 # haven't supplied a proper program with main(), but that does not
101 # matter.
102 expr = os.fsencode(r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name))
103
104 c_compiler = shutil.which('gcc')
105 if not c_compiler:
106 c_compiler = shutil.which('cc')
107 if not c_compiler:
108 # No C compiler available, give up
109 return None
110
111 temp = tempfile.NamedTemporaryFile()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000112 try:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000113 args = [c_compiler, '-Wl,-t', '-o', temp.name, '-l' + name]
114
115 env = dict(os.environ)
116 env['LC_ALL'] = 'C'
117 env['LANG'] = 'C'
Martin Pantere1b34312016-06-14 04:08:30 +0000118 try:
119 proc = subprocess.Popen(args,
120 stdout=subprocess.PIPE,
121 stderr=subprocess.STDOUT,
122 env=env)
123 except OSError: # E.g. bad executable
124 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000125 with proc:
126 trace = proc.stdout.read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000127 finally:
128 try:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000129 temp.close()
Giampaolo Rodola'0166a282013-02-12 15:14:17 +0100130 except FileNotFoundError:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000131 # Raised if the file was already removed, which is the normal
132 # behaviour of GCC if linking fails
Giampaolo Rodola'0166a282013-02-12 15:14:17 +0100133 pass
Thomas Wouters477c8d52006-05-27 19:21:47 +0000134 res = re.search(expr, trace)
135 if not res:
136 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000137 return os.fsdecode(res.group(0))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000138
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000139
140 if sys.platform == "sunos5":
141 # use /usr/ccs/bin/dump on solaris
142 def _get_soname(f):
143 if not f:
144 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000145
Martin Pantere1b34312016-06-14 04:08:30 +0000146 try:
147 proc = subprocess.Popen(("/usr/ccs/bin/dump", "-Lpv", f),
148 stdout=subprocess.PIPE,
149 stderr=subprocess.DEVNULL)
150 except OSError: # E.g. command not found
151 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000152 with proc:
153 data = proc.stdout.read()
154 res = re.search(br'\[.*\]\sSONAME\s+([^\s]+)', data)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000155 if not res:
156 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000157 return os.fsdecode(res.group(1))
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000158 else:
159 def _get_soname(f):
160 # assuming GNU binutils / ELF
161 if not f:
162 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000163 objdump = shutil.which('objdump')
164 if not objdump:
165 # objdump is not available, give up
166 return None
167
Martin Pantere1b34312016-06-14 04:08:30 +0000168 try:
169 proc = subprocess.Popen((objdump, '-p', '-j', '.dynamic', f),
170 stdout=subprocess.PIPE,
171 stderr=subprocess.DEVNULL)
172 except OSError: # E.g. bad executable
173 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000174 with proc:
175 dump = proc.stdout.read()
176 res = re.search(br'\sSONAME\s+([^\s]+)', dump)
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000177 if not res:
178 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000179 return os.fsdecode(res.group(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000180
Victor Stinnere6747472011-08-21 00:39:18 +0200181 if sys.platform.startswith(("freebsd", "openbsd", "dragonfly")):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000182
183 def _num_version(libname):
184 # "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ]
Martin Panterbfb15ab2016-06-14 01:27:11 +0000185 parts = libname.split(b".")
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000186 nums = []
187 try:
188 while parts:
189 nums.insert(0, int(parts.pop()))
190 except ValueError:
191 pass
Martin Panterbfb15ab2016-06-14 01:27:11 +0000192 return nums or [sys.maxsize]
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000193
194 def find_library(name):
195 ename = re.escape(name)
196 expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename)
Martin Panterbfb15ab2016-06-14 01:27:11 +0000197 expr = os.fsencode(expr)
198
Martin Pantere1b34312016-06-14 04:08:30 +0000199 try:
200 proc = subprocess.Popen(('/sbin/ldconfig', '-r'),
201 stdout=subprocess.PIPE,
202 stderr=subprocess.DEVNULL)
203 except OSError: # E.g. command not found
204 data = b''
205 else:
206 with proc:
207 data = proc.stdout.read()
Martin Panterbfb15ab2016-06-14 01:27:11 +0000208
Guido van Rossumf4d4f8b2007-12-12 20:26:00 +0000209 res = re.findall(expr, data)
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000210 if not res:
211 return _get_soname(_findLib_gcc(name))
Raymond Hettingerd4cb56d2008-01-30 02:55:10 +0000212 res.sort(key=_num_version)
Martin Panterbfb15ab2016-06-14 01:27:11 +0000213 return os.fsdecode(res[-1])
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000214
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500215 elif sys.platform == "sunos5":
216
217 def _findLib_crle(name, is64):
218 if not os.path.exists('/usr/bin/crle'):
219 return None
220
Martin Panterbfb15ab2016-06-14 01:27:11 +0000221 env = dict(os.environ)
222 env['LC_ALL'] = 'C'
223
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500224 if is64:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000225 args = ('/usr/bin/crle', '-64')
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500226 else:
Martin Panterbfb15ab2016-06-14 01:27:11 +0000227 args = ('/usr/bin/crle',)
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500228
Meador Inge8988ebf2016-04-30 21:56:59 -0500229 paths = None
Martin Pantere1b34312016-06-14 04:08:30 +0000230 try:
231 proc = subprocess.Popen(args,
232 stdout=subprocess.PIPE,
233 stderr=subprocess.DEVNULL,
234 env=env)
235 except OSError: # E.g. bad executable
236 return None
Martin Panterbfb15ab2016-06-14 01:27:11 +0000237 with proc:
238 for line in proc.stdout:
Nick Coghlan18896232013-11-24 12:53:50 +1000239 line = line.strip()
Martin Panterbfb15ab2016-06-14 01:27:11 +0000240 if line.startswith(b'Default Library Path (ELF):'):
241 paths = os.fsdecode(line).split()[4]
Benjamin Petersoncfe34742013-02-03 19:25:11 -0500242
243 if not paths:
244 return None
245
246 for dir in paths.split(":"):
247 libfile = os.path.join(dir, "lib%s.so" % name)
248 if os.path.exists(libfile):
249 return libfile
250
251 return None
252
253 def find_library(name, is64 = False):
254 return _get_soname(_findLib_crle(name, is64) or _findLib_gcc(name))
255
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000256 else:
257
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000258 def _findSoname_ldconfig(name):
259 import struct
260 if struct.calcsize('l') == 4:
Larry Hastings605a62d2012-06-24 04:33:36 -0700261 machine = os.uname().machine + '-32'
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000262 else:
Larry Hastings605a62d2012-06-24 04:33:36 -0700263 machine = os.uname().machine + '-64'
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000264 mach_map = {
265 'x86_64-64': 'libc6,x86-64',
266 'ppc64-64': 'libc6,64bit',
267 'sparc64-64': 'libc6,64bit',
268 's390x-64': 'libc6,64bit',
269 'ia64-64': 'libc6,IA-64',
270 }
271 abi_type = mach_map.get(machine, 'libc6')
272
273 # XXX assuming GLIBC's ldconfig (with option -p)
Martin Panter32f2eb42016-03-17 07:50:22 +0000274 regex = r'\s+(lib%s\.[^\s]+)\s+\(%s'
Martin Panterb9f31142016-03-10 01:06:23 +0000275 regex = os.fsencode(regex % (re.escape(name), abi_type))
Antoine Pitrou8c520272011-04-23 17:51:04 +0200276 try:
277 with subprocess.Popen(['/sbin/ldconfig', '-p'],
278 stdin=subprocess.DEVNULL,
279 stderr=subprocess.DEVNULL,
280 stdout=subprocess.PIPE,
281 env={'LC_ALL': 'C', 'LANG': 'C'}) as p:
282 res = re.search(regex, p.stdout.read())
283 if res:
284 return os.fsdecode(res.group(1))
285 except OSError:
286 pass
Matthias Klose2c7e3ee2009-01-10 17:08:25 +0000287
Vinay Sajip82df3b32016-08-17 16:20:07 +0100288 def _findLib_ld(name):
289 # See issue #9998 for why this is needed
290 expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
291 cmd = ['ld', '-t']
292 libpath = os.environ.get('LD_LIBRARY_PATH')
293 if libpath:
294 for d in libpath.split(':'):
295 cmd.extend(['-L', d])
296 cmd.extend(['-o', os.devnull, '-l%s' % name])
297 result = None
298 try:
299 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
300 stderr=subprocess.PIPE,
301 universal_newlines=True)
302 out, _ = p.communicate()
303 res = re.search(expr, os.fsdecode(out))
304 if res:
305 result = res.group(0)
306 except Exception as e:
307 pass # result will be None
308 return result
309
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000310 def find_library(name):
Vinay Sajip82df3b32016-08-17 16:20:07 +0100311 # See issue #9998
312 return _findSoname_ldconfig(name) or \
313 _get_soname(_findLib_gcc(name) or _findLib_ld(name))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000314
315################################################################
316# test code
317
318def test():
319 from ctypes import cdll
320 if os.name == "nt":
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000321 print(cdll.msvcrt)
322 print(cdll.load("msvcrt"))
323 print(find_library("msvcrt"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000324
325 if os.name == "posix":
326 # find and load_version
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000327 print(find_library("m"))
328 print(find_library("c"))
329 print(find_library("bz2"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000330
331 # getattr
332## print cdll.m
333## print cdll.bz2
334
335 # load
336 if sys.platform == "darwin":
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000337 print(cdll.LoadLibrary("libm.dylib"))
338 print(cdll.LoadLibrary("libcrypto.dylib"))
339 print(cdll.LoadLibrary("libSystem.dylib"))
340 print(cdll.LoadLibrary("System.framework/System"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000341 else:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000342 print(cdll.LoadLibrary("libm.so"))
343 print(cdll.LoadLibrary("libcrypt.so"))
344 print(find_library("crypt"))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000345
346if __name__ == "__main__":
347 test()