Closes #9998: Allowed find_library to search additional locations for libraries.

This commit is contained in:
Vinay Sajip 2016-08-17 16:20:07 +01:00
parent 48e4bd6a02
commit 82df3b3071
4 changed files with 82 additions and 6 deletions

View file

@ -1239,9 +1239,10 @@ When programming in a compiled language, shared libraries are accessed when
compiling/linking a program, and when the program is run.
The purpose of the :func:`find_library` function is to locate a library in a way
similar to what the compiler does (on platforms with several versions of a
shared library the most recent should be loaded), while the ctypes library
loaders act like when a program is run, and call the runtime loader directly.
similar to what the compiler or runtime loader does (on platforms with several
versions of a shared library the most recent should be loaded), while the ctypes
library loaders act like when a program is run, and call the runtime loader
directly.
The :mod:`ctypes.util` module provides a function which can help to determine
the library to load.
@ -1259,8 +1260,14 @@ the library to load.
The exact functionality is system dependent.
On Linux, :func:`find_library` tries to run external programs
(``/sbin/ldconfig``, ``gcc``, and ``objdump``) to find the library file. It
returns the filename of the library file. Here are some examples::
(``/sbin/ldconfig``, ``gcc``, ``objdump`` and ``ld``) to find the library file.
It returns the filename of the library file.
.. versionchanged:: 3.6
On Linux, the value of the environment variable ``LD_LIBRARY_PATH`` is used
when searching for libraries, if a library cannot be found by any other means.
Here are some examples::
>>> from ctypes.util import find_library
>>> find_library("m")

View file

@ -69,6 +69,49 @@ class Test_OpenGL_libs(unittest.TestCase):
self.assertFalse(os.path.lexists(test.support.TESTFN))
self.assertIsNone(result)
@unittest.skipUnless(sys.platform.startswith('linux'),
'Test only valid for Linux')
class LibPathFindTest(unittest.TestCase):
def test_find_on_libpath(self):
import subprocess
import tempfile
try:
p = subprocess.Popen(['gcc', '--version'], stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL)
out, _ = p.communicate()
except OSError:
raise unittest.SkipTest('gcc, needed for test, not available')
with tempfile.TemporaryDirectory() as d:
# create an empty temporary file
srcname = os.path.join(d, 'dummy.c')
libname = 'py_ctypes_test_dummy'
dstname = os.path.join(d, 'lib%s.so' % libname)
with open(srcname, 'w') as f:
pass
self.assertTrue(os.path.exists(srcname))
# compile the file to a shared library
cmd = ['gcc', '-o', dstname, '--shared',
'-Wl,-soname,lib%s.so' % libname, srcname]
out = subprocess.check_output(cmd)
self.assertTrue(os.path.exists(dstname))
# now check that the .so can't be found (since not in
# LD_LIBRARY_PATH)
self.assertIsNone(find_library(libname))
# now add the location to LD_LIBRARY_PATH
with test.support.EnvironmentVarGuard() as env:
KEY = 'LD_LIBRARY_PATH'
if KEY not in env:
v = d
else:
v = '%s:%s' % (env[KEY], d)
env.set(KEY, v)
# now check that the .so can be found (since in
# LD_LIBRARY_PATH)
self.assertEqual(find_library(libname), 'lib%s.so' % libname)
# On platforms where the default shared library suffix is '.so',
# at least some libraries can be loaded as attributes of the cdll
# object, since ctypes now tries loading the lib again

View file

@ -285,8 +285,32 @@ elif os.name == "posix":
except OSError:
pass
def _findLib_ld(name):
# See issue #9998 for why this is needed
expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
cmd = ['ld', '-t']
libpath = os.environ.get('LD_LIBRARY_PATH')
if libpath:
for d in libpath.split(':'):
cmd.extend(['-L', d])
cmd.extend(['-o', os.devnull, '-l%s' % name])
result = None
try:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
out, _ = p.communicate()
res = re.search(expr, os.fsdecode(out))
if res:
result = res.group(0)
except Exception as e:
pass # result will be None
return result
def find_library(name):
return _findSoname_ldconfig(name) or _get_soname(_findLib_gcc(name))
# See issue #9998
return _findSoname_ldconfig(name) or \
_get_soname(_findLib_gcc(name) or _findLib_ld(name))
################################################################
# test code

View file

@ -13,6 +13,8 @@ Core and Builtins
Library
-------
- Issue #9998: On Linux, ctypes.util.find_library now looks in LD_LIBRARY_PATH
for shared libraries.
What's New in Python 3.6.0 alpha 4
==================================