logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

util.py (13879B)


  1. import os
  2. import shutil
  3. import subprocess
  4. import sys
  5. # find_library(name) returns the pathname of a library, or None.
  6. if os.name == "nt":
  7. def _get_build_version():
  8. """Return the version of MSVC that was used to build Python.
  9. For Python 2.3 and up, the version number is included in
  10. sys.version. For earlier versions, assume the compiler is MSVC 6.
  11. """
  12. # This function was copied from Lib/distutils/msvccompiler.py
  13. prefix = "MSC v."
  14. i = sys.version.find(prefix)
  15. if i == -1:
  16. return 6
  17. i = i + len(prefix)
  18. s, rest = sys.version[i:].split(" ", 1)
  19. majorVersion = int(s[:-2]) - 6
  20. if majorVersion >= 13:
  21. majorVersion += 1
  22. minorVersion = int(s[2:3]) / 10.0
  23. # I don't think paths are affected by minor version in version 6
  24. if majorVersion == 6:
  25. minorVersion = 0
  26. if majorVersion >= 6:
  27. return majorVersion + minorVersion
  28. # else we don't know what version of the compiler this is
  29. return None
  30. def find_msvcrt():
  31. """Return the name of the VC runtime dll"""
  32. version = _get_build_version()
  33. if version is None:
  34. # better be safe than sorry
  35. return None
  36. if version <= 6:
  37. clibname = 'msvcrt'
  38. elif version <= 13:
  39. clibname = 'msvcr%d' % (version * 10)
  40. else:
  41. # CRT is no longer directly loadable. See issue23606 for the
  42. # discussion about alternative approaches.
  43. return None
  44. # If python was built with in debug mode
  45. import importlib.machinery
  46. if '_d.pyd' in importlib.machinery.EXTENSION_SUFFIXES:
  47. clibname += 'd'
  48. return clibname+'.dll'
  49. def find_library(name):
  50. if name in ('c', 'm'):
  51. return find_msvcrt()
  52. # See MSDN for the REAL search order.
  53. for directory in os.environ['PATH'].split(os.pathsep):
  54. fname = os.path.join(directory, name)
  55. if os.path.isfile(fname):
  56. return fname
  57. if fname.lower().endswith(".dll"):
  58. continue
  59. fname = fname + ".dll"
  60. if os.path.isfile(fname):
  61. return fname
  62. return None
  63. elif os.name == "posix" and sys.platform == "darwin":
  64. from ctypes.macholib.dyld import dyld_find as _dyld_find
  65. def find_library(name):
  66. possible = ['lib%s.dylib' % name,
  67. '%s.dylib' % name,
  68. '%s.framework/%s' % (name, name)]
  69. for name in possible:
  70. try:
  71. return _dyld_find(name)
  72. except ValueError:
  73. continue
  74. return None
  75. elif sys.platform.startswith("aix"):
  76. # AIX has two styles of storing shared libraries
  77. # GNU auto_tools refer to these as svr4 and aix
  78. # svr4 (System V Release 4) is a regular file, often with .so as suffix
  79. # AIX style uses an archive (suffix .a) with members (e.g., shr.o, libssl.so)
  80. # see issue#26439 and _aix.py for more details
  81. from ctypes._aix import find_library
  82. elif os.name == "posix":
  83. # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
  84. import re, tempfile
  85. def _is_elf(filename):
  86. "Return True if the given file is an ELF file"
  87. elf_header = b'\x7fELF'
  88. with open(filename, 'br') as thefile:
  89. return thefile.read(4) == elf_header
  90. def _findLib_gcc(name):
  91. # Run GCC's linker with the -t (aka --trace) option and examine the
  92. # library name it prints out. The GCC command will fail because we
  93. # haven't supplied a proper program with main(), but that does not
  94. # matter.
  95. expr = os.fsencode(r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name))
  96. c_compiler = shutil.which('gcc')
  97. if not c_compiler:
  98. c_compiler = shutil.which('cc')
  99. if not c_compiler:
  100. # No C compiler available, give up
  101. return None
  102. temp = tempfile.NamedTemporaryFile()
  103. try:
  104. args = [c_compiler, '-Wl,-t', '-o', temp.name, '-l' + name]
  105. env = dict(os.environ)
  106. env['LC_ALL'] = 'C'
  107. env['LANG'] = 'C'
  108. try:
  109. proc = subprocess.Popen(args,
  110. stdout=subprocess.PIPE,
  111. stderr=subprocess.STDOUT,
  112. env=env)
  113. except OSError: # E.g. bad executable
  114. return None
  115. with proc:
  116. trace = proc.stdout.read()
  117. finally:
  118. try:
  119. temp.close()
  120. except FileNotFoundError:
  121. # Raised if the file was already removed, which is the normal
  122. # behaviour of GCC if linking fails
  123. pass
  124. res = re.findall(expr, trace)
  125. if not res:
  126. return None
  127. for file in res:
  128. # Check if the given file is an elf file: gcc can report
  129. # some files that are linker scripts and not actual
  130. # shared objects. See bpo-41976 for more details
  131. if not _is_elf(file):
  132. continue
  133. return os.fsdecode(file)
  134. if sys.platform == "sunos5":
  135. # use /usr/ccs/bin/dump on solaris
  136. def _get_soname(f):
  137. if not f:
  138. return None
  139. try:
  140. proc = subprocess.Popen(("/usr/ccs/bin/dump", "-Lpv", f),
  141. stdout=subprocess.PIPE,
  142. stderr=subprocess.DEVNULL)
  143. except OSError: # E.g. command not found
  144. return None
  145. with proc:
  146. data = proc.stdout.read()
  147. res = re.search(br'\[.*\]\sSONAME\s+([^\s]+)', data)
  148. if not res:
  149. return None
  150. return os.fsdecode(res.group(1))
  151. else:
  152. def _get_soname(f):
  153. # assuming GNU binutils / ELF
  154. if not f:
  155. return None
  156. objdump = shutil.which('objdump')
  157. if not objdump:
  158. # objdump is not available, give up
  159. return None
  160. try:
  161. proc = subprocess.Popen((objdump, '-p', '-j', '.dynamic', f),
  162. stdout=subprocess.PIPE,
  163. stderr=subprocess.DEVNULL)
  164. except OSError: # E.g. bad executable
  165. return None
  166. with proc:
  167. dump = proc.stdout.read()
  168. res = re.search(br'\sSONAME\s+([^\s]+)', dump)
  169. if not res:
  170. return None
  171. return os.fsdecode(res.group(1))
  172. if sys.platform.startswith(("freebsd", "openbsd", "dragonfly")):
  173. def _num_version(libname):
  174. # "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ]
  175. parts = libname.split(b".")
  176. nums = []
  177. try:
  178. while parts:
  179. nums.insert(0, int(parts.pop()))
  180. except ValueError:
  181. pass
  182. return nums or [sys.maxsize]
  183. def find_library(name):
  184. ename = re.escape(name)
  185. expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename)
  186. expr = os.fsencode(expr)
  187. try:
  188. proc = subprocess.Popen(('/sbin/ldconfig', '-r'),
  189. stdout=subprocess.PIPE,
  190. stderr=subprocess.DEVNULL)
  191. except OSError: # E.g. command not found
  192. data = b''
  193. else:
  194. with proc:
  195. data = proc.stdout.read()
  196. res = re.findall(expr, data)
  197. if not res:
  198. return _get_soname(_findLib_gcc(name))
  199. res.sort(key=_num_version)
  200. return os.fsdecode(res[-1])
  201. elif sys.platform == "sunos5":
  202. def _findLib_crle(name, is64):
  203. if not os.path.exists('/usr/bin/crle'):
  204. return None
  205. env = dict(os.environ)
  206. env['LC_ALL'] = 'C'
  207. if is64:
  208. args = ('/usr/bin/crle', '-64')
  209. else:
  210. args = ('/usr/bin/crle',)
  211. paths = None
  212. try:
  213. proc = subprocess.Popen(args,
  214. stdout=subprocess.PIPE,
  215. stderr=subprocess.DEVNULL,
  216. env=env)
  217. except OSError: # E.g. bad executable
  218. return None
  219. with proc:
  220. for line in proc.stdout:
  221. line = line.strip()
  222. if line.startswith(b'Default Library Path (ELF):'):
  223. paths = os.fsdecode(line).split()[4]
  224. if not paths:
  225. return None
  226. for dir in paths.split(":"):
  227. libfile = os.path.join(dir, "lib%s.so" % name)
  228. if os.path.exists(libfile):
  229. return libfile
  230. return None
  231. def find_library(name, is64 = False):
  232. return _get_soname(_findLib_crle(name, is64) or _findLib_gcc(name))
  233. else:
  234. def _findSoname_ldconfig(name):
  235. import struct
  236. if struct.calcsize('l') == 4:
  237. machine = os.uname().machine + '-32'
  238. else:
  239. machine = os.uname().machine + '-64'
  240. mach_map = {
  241. 'x86_64-64': 'libc6,x86-64',
  242. 'ppc64-64': 'libc6,64bit',
  243. 'sparc64-64': 'libc6,64bit',
  244. 's390x-64': 'libc6,64bit',
  245. 'ia64-64': 'libc6,IA-64',
  246. }
  247. abi_type = mach_map.get(machine, 'libc6')
  248. # XXX assuming GLIBC's ldconfig (with option -p)
  249. regex = r'\s+(lib%s\.[^\s]+)\s+\(%s'
  250. regex = os.fsencode(regex % (re.escape(name), abi_type))
  251. try:
  252. with subprocess.Popen(['/sbin/ldconfig', '-p'],
  253. stdin=subprocess.DEVNULL,
  254. stderr=subprocess.DEVNULL,
  255. stdout=subprocess.PIPE,
  256. env={'LC_ALL': 'C', 'LANG': 'C'}) as p:
  257. res = re.search(regex, p.stdout.read())
  258. if res:
  259. return os.fsdecode(res.group(1))
  260. except OSError:
  261. pass
  262. def _findLib_ld(name):
  263. # See issue #9998 for why this is needed
  264. expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
  265. cmd = ['ld', '-t']
  266. libpath = os.environ.get('LD_LIBRARY_PATH')
  267. if libpath:
  268. for d in libpath.split(':'):
  269. cmd.extend(['-L', d])
  270. cmd.extend(['-o', os.devnull, '-l%s' % name])
  271. result = None
  272. try:
  273. p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
  274. stderr=subprocess.PIPE,
  275. universal_newlines=True)
  276. out, _ = p.communicate()
  277. res = re.findall(expr, os.fsdecode(out))
  278. for file in res:
  279. # Check if the given file is an elf file: gcc can report
  280. # some files that are linker scripts and not actual
  281. # shared objects. See bpo-41976 for more details
  282. if not _is_elf(file):
  283. continue
  284. return os.fsdecode(file)
  285. except Exception:
  286. pass # result will be None
  287. return result
  288. def find_library(name):
  289. # See issue #9998
  290. return _findSoname_ldconfig(name) or \
  291. _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name))
  292. ################################################################
  293. # test code
  294. def test():
  295. from ctypes import cdll
  296. if os.name == "nt":
  297. print(cdll.msvcrt)
  298. print(cdll.load("msvcrt"))
  299. print(find_library("msvcrt"))
  300. if os.name == "posix":
  301. # find and load_version
  302. print(find_library("m"))
  303. print(find_library("c"))
  304. print(find_library("bz2"))
  305. # load
  306. if sys.platform == "darwin":
  307. print(cdll.LoadLibrary("libm.dylib"))
  308. print(cdll.LoadLibrary("libcrypto.dylib"))
  309. print(cdll.LoadLibrary("libSystem.dylib"))
  310. print(cdll.LoadLibrary("System.framework/System"))
  311. # issue-26439 - fix broken test call for AIX
  312. elif sys.platform.startswith("aix"):
  313. from ctypes import CDLL
  314. if sys.maxsize < 2**32:
  315. print(f"Using CDLL(name, os.RTLD_MEMBER): {CDLL('libc.a(shr.o)', os.RTLD_MEMBER)}")
  316. print(f"Using cdll.LoadLibrary(): {cdll.LoadLibrary('libc.a(shr.o)')}")
  317. # librpm.so is only available as 32-bit shared library
  318. print(find_library("rpm"))
  319. print(cdll.LoadLibrary("librpm.so"))
  320. else:
  321. print(f"Using CDLL(name, os.RTLD_MEMBER): {CDLL('libc.a(shr_64.o)', os.RTLD_MEMBER)}")
  322. print(f"Using cdll.LoadLibrary(): {cdll.LoadLibrary('libc.a(shr_64.o)')}")
  323. print(f"crypt\t:: {find_library('crypt')}")
  324. print(f"crypt\t:: {cdll.LoadLibrary(find_library('crypt'))}")
  325. print(f"crypto\t:: {find_library('crypto')}")
  326. print(f"crypto\t:: {cdll.LoadLibrary(find_library('crypto'))}")
  327. else:
  328. print(cdll.LoadLibrary("libm.so"))
  329. print(cdll.LoadLibrary("libcrypt.so"))
  330. print(find_library("crypt"))
  331. if __name__ == "__main__":
  332. test()