summaryrefslogtreecommitdiff
path: root/Lib/ctypes
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2016-08-17 16:20:07 +0100
committerVinay Sajip <vinay_sajip@yahoo.co.uk>2016-08-17 16:20:07 +0100
commit569ff74dd4ca3942bf31724602788a5d388e73bb (patch)
treeb176e03c0f0a81fc7de27591547829c9dbc28935 /Lib/ctypes
parent372e0d539400c76a48b7cbd6366e2bb3fc2f5813 (diff)
downloadcpython-569ff74dd4ca3942bf31724602788a5d388e73bb.tar.gz
Closes #9998: Allowed find_library to search additional locations for libraries.
Diffstat (limited to 'Lib/ctypes')
-rw-r--r--Lib/ctypes/test/test_find.py43
-rw-r--r--Lib/ctypes/util.py26
2 files changed, 68 insertions, 1 deletions
diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py
index 20c5337a8b..c7205f5ddf 100644
--- a/Lib/ctypes/test/test_find.py
+++ b/Lib/ctypes/test/test_find.py
@@ -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
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
index e25a886a05..f5c6b266b6 100644
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -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