diff options
author | Vinay Sajip <vinay_sajip@yahoo.co.uk> | 2016-08-17 16:20:07 +0100 |
---|---|---|
committer | Vinay Sajip <vinay_sajip@yahoo.co.uk> | 2016-08-17 16:20:07 +0100 |
commit | 569ff74dd4ca3942bf31724602788a5d388e73bb (patch) | |
tree | b176e03c0f0a81fc7de27591547829c9dbc28935 /Lib/ctypes | |
parent | 372e0d539400c76a48b7cbd6366e2bb3fc2f5813 (diff) | |
download | cpython-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.py | 43 | ||||
-rw-r--r-- | Lib/ctypes/util.py | 26 |
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 |