ATLAS Offline Software
Tools/PyUtils/python/Dso.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
2 
3 
6 
7 from __future__ import print_function
8 
9 __author__ = "Sebastien Binet"
10 
11 __all__ = ['DsoDb']
12 
13 import sys
14 import os
15 import re
16 
17 def _libName(lib):
18  import platform
19  if platform.system() == "Linux":
20  if lib[:3] != "lib": lib = "lib"+lib
21  if lib[-3:] != ".so": lib = lib+".so"
22  return lib
23 
24 
25 _aliases = {
26  'ElementLinkInt_p1' : 'ElementLink_p1<unsigned int>',
27  'basic_string<char>' : 'string',
28  'std::basic_string<char>' : 'string',
29  'vector<basic_string<char> >' : 'vector<string>',
30 
31  'INavigable4MomentumCollection' : 'DataVector<INavigable4Momentum>',
32  'IParticleContainer' : 'DataVector<IParticle>',
33  }
34 _typedefs = {
35 
36  'INavigable4MomentumCollection' : 'DataVector<INavigable4Momentum>',
37  'IParticleContainer' : 'DataVector<IParticle>',
38  }
39 
40 _cpp_builtins = (
41  'char', 'unsigned char', 'signed char',
42  'signed',
43  'short int', 'short signed', 'short signed int',
44  'short', 'unsigned short', 'signed short',
45  'int', 'unsigned int',
46 
47  'long int',
48  'long signed int',
49  'signed long int',
50 
51  'long',
52  'long signed', 'signed long',
53  'unsigned long',
54  'unsigned long int',
55  'long unsigned int',
56 
57  'long long',
58  'long long int',
59  'unsigned long long',
60  'longlong',
61 
62  # no clue from where this one comes from, who's requesting it nor who
63  # got the alien naming scheme idea...
64  'ulonglong',
65 
66  'float',
67  'double',
68  'long double',
69  'bool',
70  )
71 
72 _is_stl_sequence = re.compile (r'std::(?P<ContType>.*?)'
73  r'<(?P<TemplateArg>.*?)'
74  r',\s*?std::allocator<\2> >')
75 _is_stl_mapping = re.compile (r'std::map<'
76  r'(?P<TemplateArg1>.*?),\s*?'
77  r'(?P<TemplateArg2>.*?)'
78  r',\s*?std::allocator<\2> >')
79 
80 
81 
82 
83 
84 def _get_native_libname(libname):
85  """ return the OS-native name from an OS-indenpendent one """
86  plat = sys.platform
87  if plat.count('linux')>0:
88  lib_prefix,lib_suffix = 'lib', '.so'
89  elif plat == 'win32':
90  lib_prefix,lib_suffix = '', '.dll'
91  elif plat == 'darwin':
92  lib_prefix,lib_suffix = 'lib','.dylib'
93  else:
94  raise RuntimeError ("sorry platform [%s] is not (yet?) supported"%plat)
95  _sys_libname = libname
96  if not _sys_libname.startswith (lib_prefix):
97  _sys_libname = ''.join([lib_prefix,_sys_libname])
98  if not _sys_libname.endswith (lib_suffix):
99  _sys_libname = ''.join([_sys_libname, lib_suffix])
100  return _sys_libname
101 
102 def load_library (libname):
103  """
104  Helper method to load a library by its natural name, not the OS-native name.
105  But if the OS-native name is given, it is safely handled too.
106  usage:
107  >>> load_library ('AthenaServices')
108  >>> load_library ('AthenaServicesDict')
109  """
110  _sys_libname = _get_native_libname(libname)
111  import ctypes
112  return ctypes.cdll.LoadLibrary (_sys_libname)
113 
114 def find_library(libname):
115  """
116  Helper function to find the (full)path to a library given its natural name.
117  @return None on failure
118 
119  usage:
120  >>> find_library('AthenaServices')
121  '/afs/cern.ch/.../AtlasCore/[release]/InstallArea/.../libAthenaServices.so
122  """
123  import os
124 
130  _sys_libname = _get_native_libname(libname)
131  # FIXME: REALLY not portable...
132  if os.name != 'posix':
133  raise RuntimeError('sorry OS [%s] is not supported' % os.name)
134 
135  if 'LD_LIBRARY_PATH' in os.environ:
136  for d in os.environ['LD_LIBRARY_PATH'].split(os.pathsep):
137  lib = os.path.join(d, _sys_libname)
138  if os.path.exists(lib):
139  return lib
140  return
141 
142 
143 def _is_rootcint_dict (libname):
144  """helper function to reject rootcint libraries entries from rootmap
145  files (which appeared w/ ROOT v21/22)
146  It seems all of them (and on all platforms) are named like:
147  vector<char>: vector.dll
148  """
149  if libname == ".dll": # pathological case...
150  return False
151  pat = re.compile(r'\w*?.dll')
152  return not (libname.startswith("lib")) and \
153  not (pat.match (libname) is None)
154 
156  """
157  The repository of 'rootmap' files (location, content,...)
158  """
159  def __init__(self):
160  import cppyy # noqa: F401
161  # import root
162  import PyUtils.RootUtils as ru
163  ROOT = ru.import_root()
164  self._cxx = ROOT.Ath.DsoDb.instance()
165 
166  def _to_py(self, cxx):
167  dd = {}
168  kk = self._cxx.py_keys_from(cxx)
169  vv = self._cxx.py_vals_from(cxx)
170  for i in range(kk.size()):
171  dd[kk[i]] = list(vv[i])
172  return dd
173 
174  @property
175  def db(self):
176  return self._to_py(self._cxx.db())
177 
178  @property
179  def pf(self):
180  return self._to_py(self._cxx.pf())
181 
182  def has_type(self, typename):
183  return self._cxx.has_type(typename)
184 
185  def load_type(self, typename):
186  return self._cxx.load_type(typename)
187 
188  def capabilities(self, libname):
189  return list(self._cxx.capabilities(libname))
190 
191  def duplicates(self, libname, pedantic=False):
192  return self._to_py(self._cxx.duplicates(libname, pedantic))
193 
194  def dict_duplicates(self, pedantic=False):
195  return self._to_py(self._cxx.dict_duplicates(pedantic))
196 
197  dictDuplicates = dict_duplicates
198 
199  def pf_duplicates(self, pedantic=False):
200  return self._to_py(self._cxx.pf_duplicates(pedantic))
201 
202  pfDuplicates = pf_duplicates
203 
204  def libs(self, detailed=False):
205  return list(self._cxx.libs(detailed))
206 
207  def content(self, pedantic):
208  return self._to_py(self._cxx.content(pedantic))
209 
210  @property
211  def dso_files(self):
212  return list(self._cxx.dso_files())
213 
214  @property
215  def dsoFiles(self):
216  return self.dso_files
217 
218 def _to_rootmap_name(typename):
219  """
220  helper method to massage a typename into something understandable
221  by the rootmap files
222  """
223  global _aliases
224  typename = typename.replace(', ',',')
225  # first the easy case: builtins
226  if typename in _cpp_builtins:
227  return typename
228  # known missing aliases ?
229  if typename in _aliases.keys():
230  t = _aliases[typename]
231  return _to_rootmap_name(t)
232  # handle default template arguments of STL sequences
233  if _is_stl_sequence.match(typename):
234  # rootmap files do not contain the default template arguments
235  # for STL containers... consistency, again.
236  _m = _is_stl_sequence.match(typename)
237  _m_type = _m.group('TemplateArg')
238  # handle the dreaded 'std::Bla<Foo<d> >
239  _m_type = _to_rootmap_name(_m_type.strip())
240  if _m_type.endswith('>'):
241  _m_type += ' '
242  typename = 'std::%s<%s>' % (_m.group('ContType'),
243  _m_type)
244  # need to massage a bit the typename to match ROOT naming convention
245  typename = typename.replace('std::basic_string<char> ',
246  'string ')
247  typename = typename.replace('std::basic_string<char>',
248  'string')
249  typename = typename.replace('std::', '')
250  typename = typename.replace('> >', '>->')
251  typename = typename.replace(' >', '>')
252  typename = typename.replace('>->', '> >')
253  return typename
254 
255 def _to_rflx_name (typename):
256  """helper method to massage a typename into something understandable
257  by reflex (which doesn't understand the same thing than rootmaps).
258  """
259  global _aliases,_typedefs
260  typename = typename.replace(', ',',')
261  # first the easy case: builtins
262  if typename in _cpp_builtins:
263  return typename
264  # known missing typedefs ?
265  if typename in _typedefs.keys():
266  t = _typedefs[typename]
267  return _to_rflx_name(t)
268  # handle default template arguments of STL sequences
269  if _is_stl_sequence.match(typename):
270  # rootmap files do not contain the default template arguments
271  # for STL containers... consistency, again.
272  _m = _is_stl_sequence.match (typename)
273  _m_type = _m.group('TemplateArg')
274  # handle the dreaded 'std::Bla<Foo<d> >
275  _m_type = _to_rflx_name (_m_type.strip())
276  if _m_type.endswith('>'):
277  _m_type += ' '
278  typename = 'std::%s<%s>' % (_m.group('ContType'), _m_type)
279  typename = typename.replace('std::string>',
280  'std::basic_string<char> >')
281  typename = typename.replace('std::string',
282  'std::basic_string<char>')
283  return typename
284 
285 class PyDsoDb( object ):
286  """
287  The repository of 'rootmap' files (location, content,...)
288  """
289  RootMap = "rootmap"
290  DsoMap = "dsomap"
291  PluginNamespace = "__pf__"
292 
293  def __init__(self, name = "DsoDb"):
294  object.__init__(self)
295  self.name = name
296  self.db = { } # repository of components
297  self.pf = { } # repository of known components to the plugin svc
298 
299  import PyUtils.Logging as _L
300  self.msg = _L.logging.getLogger('DsoDb')
301 
302  self.dsoPath = os.environ['LD_LIBRARY_PATH']
303  self.__buildRepository()
304  return
305 
306  def __buildRepository(self):
307  msg = self.msg
308  self.dsoFiles = set()
309  dsoPath = [p for p in self.dsoPath.split( os.pathsep )
310  if not p.startswith(os.environ['ROOTSYS'])]
311  for path in dsoPath:
312  if not os.path.exists(path): continue
313  dir_content = None
314  try:
315  dir_content = os.listdir(path)
316  except Exception:
317  # try again...
318  try:
319  dir_content = os.listdir(path)
320  except Exception as err:
321  msg.warning("caught:\n%s", err)
322  if dir_content is None:
323  msg.warning("could not run os.listdir on [%s]", path)
324  dir_content = []
325  dsoFiles = [ f for f in dir_content
326  if f.endswith(self.RootMap) ]
327  for dsoFile in dsoFiles:
328  dsoFile = os.path.join( path, dsoFile )
329  if os.path.exists(dsoFile):
330  line_nbr = -1
331  self.dsoFiles.add(dsoFile)
332  for line in open(dsoFile, 'r'):
333  line_nbr += 1
334  line = line.strip()
335  if len(line) <= 0 or line[0] == "#":
336  continue
337  line = line.split()
338  # Note that as of LCG-55, rootmaps have the following
339  # format: 'symbol': libDict.so [listOfLinkedLibs.so...]
340  # we are only interested in libDict.so...
341  try:
342  dsoKey, libName = line[0], line[1]
343  except Exception as err:
344  msg.warning(
345  'could not parse %s:%i', dsoFile, line_nbr
346  )
347  msg.warning(
348  '(some) reflex-dicts may fail to be auto-loaded'
349  )
350  msg.warning(err)
351  continue
352  dsoKey = dsoKey\
353  .replace("Library.", "")\
354  .replace( ":", "" )\
355  .replace( "@", ":" )\
356  .replace( "-", " " )
357  if dsoKey.startswith( self.PluginNamespace ):
358  db = self.pf
359  else:
360  db = self.db
361  if dsoKey not in db: db[dsoKey] = list()
362  if _is_rootcint_dict (libName):
363  #print "## discarding [%s]..." % libName
364  continue
365  libName = os.path.join(path, _libName(libName))
366  db[dsoKey].append(libName)
367  pass # loop over dso-lines
368  pass # loop over dsoFiles
369  pass # iter over dsoPath
370  return
371 
372  def __str__(self):
373  s = os.linesep.join( [
374  "+--- %s ---" % self.name,
375  "|nbr of lib components: %i" % len(self.db.keys()),
376  "|nbr of pf components: %i" % len(self.pf.keys()),
377  "|nbr of dso files: %i" % len(self.dsoFiles),
378  "|nbr of known libs: %i" % len(self.libs()),
379  "+-------------------------"
380  ] )
381 
382  return s
383 
384  def __dups(self, db, pedantic):
385  dups = {}
386  for k in db.keys():
387  if len(db[k]) == 1: continue
388  if pedantic: libs = db[k]
389  else:
390  baseLibs = set()
391  libs = []
392  for lib in db[k]:
393  if os.path.basename(lib) not in baseLibs:
394  libs.append(lib)
395  baseLibs.add(os.path.basename(lib))
396  pass
397  pass
398  if len(libs) > 1:
399  dups[k] = [ lib for lib in libs ]
400  return dups
401 
402  def duplicates(self, libName, pedantic = False):
403  caps = self.capabilities(libName)
404  dups = {}
405  for dupDb in [ self.dictDuplicates(pedantic),
406  self.pfDuplicates(pedantic) ]:
407  for k in dupDb:
408  if k in caps:
409  if k not in dups: dups[k] = []
410  dups[k] += [ lib for lib in dupDb[k]
411  if libName not in os.path.basename(lib) ]
412  dups.keys().sort()
413  for k in dups.keys():
414  dups[k].sort()
415  return dups
416 
417  def dictDuplicates(self, pedantic = False):
418  return self.__dups(self.db, pedantic)
419 
420  def pfDuplicates(self, pedantic = False):
421  return self.__dups(self.pf, pedantic)
422 
423  def capabilities(self, libName):
424  libName = _libName(libName)
425  caps = set()
426  for db in [self.db, self.pf]:
427  for k in db.keys():
428  if libName in [ os.path.basename(lib) for lib in db[k] ]:
429  caps.add( k )
430  caps = [ cap for cap in caps ]
431  caps.sort()
432  if len(caps) == 0:
433  print ("::: ERROR: No such library [%s] in dsoDb !!" % libName)
434  raise ValueError ("")
435  return caps
436 
437  def libs(self, detailedDump = False):
438  if detailedDump: fct = lambda x : x
439  else: fct = os.path.basename
440  libs = set()
441  for db in [self.pf, self.db]:
442  for k in db.keys():
443  for lib in db[k]:
444  libs.add(fct(lib))
445  libs = [ lib for lib in libs ]
446  libs.sort()
447  return libs
448 
449  def content(self, pedantic):
450  d = {}
451  for db in [self.pf, self.db]:
452  for k in db.keys():
453  if pedantic: libs = db[k]
454  else:
455  baseLibs = set()
456  libs = []
457  for lib in db[k]:
458  if os.path.basename(lib) not in baseLibs:
459  libs.append(lib)
460  baseLibs.add(os.path.basename(lib))
461  pass
462  pass
463  d[k] = [ lib for lib in libs ]
464  return d
465 
466  def _to_rootmap_name(self, typename):
467  """
468  helper method to massage a typename into something understandable
469  by the rootmap files
470  """
471  return _to_rootmap_name(typename)
472 
473  def _to_rflx_name (self, typename):
474  """helper method to massage a typename into something understandable
475  by reflex (which doesn't understand the same thing than rootmaps).
476  """
477  return _to_rflx_name(typename)
478 
479 DsoDb = CxxDsoDb
480 #DsoDb = PyDsoDb
python.Dso.CxxDsoDb.dict_duplicates
def dict_duplicates(self, pedantic=False)
Definition: Tools/PyUtils/python/Dso.py:194
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
python.Dso.PyDsoDb.RootMap
RootMap
Definition: Tools/PyUtils/python/Dso.py:289
python.Dso.PyDsoDb.__str__
def __str__(self)
Definition: Tools/PyUtils/python/Dso.py:372
python.Dso.PyDsoDb.libs
def libs(self, detailedDump=False)
Definition: Tools/PyUtils/python/Dso.py:437
python.Dso.PyDsoDb.__buildRepository
def __buildRepository(self)
Definition: Tools/PyUtils/python/Dso.py:306
python.Dso.find_library
def find_library(libname)
Definition: Tools/PyUtils/python/Dso.py:114
python.Dso.CxxDsoDb.load_type
def load_type(self, typename)
Definition: Tools/PyUtils/python/Dso.py:185
python.Dso.PyDsoDb.dsoFiles
dsoFiles
Definition: Tools/PyUtils/python/Dso.py:308
python.Dso.CxxDsoDb._to_py
def _to_py(self, cxx)
Definition: Tools/PyUtils/python/Dso.py:166
python.Dso.load_library
def load_library(libname)
Definition: Tools/PyUtils/python/Dso.py:102
python.AthDsoLogger.fct
fct
Definition: AthDsoLogger.py:43
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.Dso.CxxDsoDb.capabilities
def capabilities(self, libname)
Definition: Tools/PyUtils/python/Dso.py:188
python.Dso.CxxDsoDb.db
def db(self)
Definition: Tools/PyUtils/python/Dso.py:175
python.Dso.CxxDsoDb.duplicates
def duplicates(self, libname, pedantic=False)
Definition: Tools/PyUtils/python/Dso.py:191
python.Dso.CxxDsoDb.has_type
def has_type(self, typename)
Definition: Tools/PyUtils/python/Dso.py:182
python.Dso.PyDsoDb._to_rootmap_name
def _to_rootmap_name(self, typename)
Definition: Tools/PyUtils/python/Dso.py:466
python.Dso._to_rootmap_name
def _to_rootmap_name(typename)
Definition: Tools/PyUtils/python/Dso.py:218
python.Dso.PyDsoDb.dictDuplicates
def dictDuplicates(self, pedantic=False)
Definition: Tools/PyUtils/python/Dso.py:417
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
python.Dso.CxxDsoDb.dsoFiles
def dsoFiles(self)
Definition: Tools/PyUtils/python/Dso.py:215
add
bool add(const std::string &hname, TKey *tobj)
Definition: fastadd.cxx:55
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.Dso.CxxDsoDb.libs
def libs(self, detailed=False)
Definition: Tools/PyUtils/python/Dso.py:204
python.Dso.PyDsoDb.__init__
def __init__(self, name="DsoDb")
Definition: Tools/PyUtils/python/Dso.py:293
python.Dso.PyDsoDb.pfDuplicates
def pfDuplicates(self, pedantic=False)
Definition: Tools/PyUtils/python/Dso.py:420
python.Dso.PyDsoDb._to_rflx_name
def _to_rflx_name(self, typename)
Definition: Tools/PyUtils/python/Dso.py:473
python.Dso.PyDsoDb.dsoPath
dsoPath
Definition: Tools/PyUtils/python/Dso.py:302
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:224
python.Dso.CxxDsoDb
Definition: Tools/PyUtils/python/Dso.py:155
python.Dso.CxxDsoDb._cxx
_cxx
Definition: Tools/PyUtils/python/Dso.py:164
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.Dso._to_rflx_name
def _to_rflx_name(typename)
Definition: Tools/PyUtils/python/Dso.py:255
python.Dso._libName
def _libName(lib)
Definition: Tools/PyUtils/python/Dso.py:17
python.Dso.PyDsoDb.duplicates
def duplicates(self, libName, pedantic=False)
Definition: Tools/PyUtils/python/Dso.py:402
python.Dso.PyDsoDb.pf
pf
Definition: Tools/PyUtils/python/Dso.py:297
python.Dso.PyDsoDb.db
db
Definition: Tools/PyUtils/python/Dso.py:296
Trk::open
@ open
Definition: BinningType.h:40
python.Dso._is_rootcint_dict
def _is_rootcint_dict(libname)
Definition: Tools/PyUtils/python/Dso.py:143
python.Dso.CxxDsoDb.pf_duplicates
def pf_duplicates(self, pedantic=False)
Definition: Tools/PyUtils/python/Dso.py:199
python.Dso.PyDsoDb.content
def content(self, pedantic)
Definition: Tools/PyUtils/python/Dso.py:449
python.Dso.PyDsoDb.capabilities
def capabilities(self, libName)
Definition: Tools/PyUtils/python/Dso.py:423
python.Dso.CxxDsoDb.dso_files
def dso_files(self)
Definition: Tools/PyUtils/python/Dso.py:211
python.Dso.PyDsoDb
Definition: Tools/PyUtils/python/Dso.py:285
pickleTool.object
object
Definition: pickleTool.py:30
python.Dso.PyDsoDb.msg
msg
Definition: Tools/PyUtils/python/Dso.py:300
python.Dso.PyDsoDb.__dups
def __dups(self, db, pedantic)
Definition: Tools/PyUtils/python/Dso.py:384
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
python.Dso.CxxDsoDb.pf
def pf(self)
Definition: Tools/PyUtils/python/Dso.py:179
python.Dso._get_native_libname
def _get_native_libname(libname)
functions --------------------------------------------------------------—
Definition: Tools/PyUtils/python/Dso.py:84
python.Dso.PyDsoDb.name
name
Definition: Tools/PyUtils/python/Dso.py:295
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.Dso.CxxDsoDb.content
def content(self, pedantic)
Definition: Tools/PyUtils/python/Dso.py:207
python.Dso.CxxDsoDb.__init__
def __init__(self)
Definition: Tools/PyUtils/python/Dso.py:159
python.Dso.PyDsoDb.PluginNamespace
PluginNamespace
Definition: Tools/PyUtils/python/Dso.py:291