Gaudi Framework, version v23r5

Home   Generated: Wed Nov 28 2012
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Public Member Functions | Public Attributes | Private Member Functions | List of all members
cmt2cmake.Package Class Reference
Inheritance diagram for cmt2cmake.Package:
Inheritance graph
[legend]
Collaboration diagram for cmt2cmake.Package:
Collaboration graph
[legend]

Public Member Functions

def __init__
 
def generate
 
def data_packages
 
def process
 

Public Attributes

 path
 
 name
 
 requirements
 
 project
 
 uses
 
 version
 
 libraries
 
 applications
 
 documents
 
 macros
 
 sets
 
 paths
 
 singleton_patterns
 
 install_more_includes
 
 install_python_modules
 
 install_scripts
 
 QMTest
 
 god_headers
 
 god_dictionary
 
 PyQtResource
 
 PyQtUIC
 
 multi_patterns
 
 reflex_dictionary
 
 component_library
 
 linker_library
 
 copy_relax_rootmap
 
 reflex_dictionaries
 
 component_libraries
 
 linker_libraries
 
 log
 
 CMTParser
 

Private Member Functions

def _parseRequirements
 

Detailed Description

Definition at line 178 of file cmt2cmake.py.

Constructor & Destructor Documentation

def cmt2cmake.Package.__init__ (   self,
  path,
  project = None 
)

Definition at line 179 of file cmt2cmake.py.

180  def __init__(self, path, project=None):
181  self.path = os.path.realpath(path)
182  if not isPackage(self.path):
183  raise ValueError("%s is not a package" % self.path)
185  self.name = os.path.basename(self.path)
186  self.requirements = os.path.join(self.path, "cmt", "requirements")
187  self.project = project
188 
189  # prepare attributes filled during parsing of requirements
190  self.uses = {}
191  self.version = None
192  self.libraries = []
193  self.applications = []
194  self.documents = []
195  self.macros = {}
196  self.sets = {}
197  self.paths = {}
198 
199  # These are patterns that can appear only once per package.
200  # The corresponding dictionary will contain the arguments passed to the
201  # pattern.
202  self.singleton_patterns = set(["QMTest", "install_python_modules", "install_scripts",
203  "install_more_includes", "god_headers", "god_dictionary",
204  "PyQtResource", "PyQtUIC"])
206  self.install_python_modules = self.install_scripts = self.QMTest = False
207  self.god_headers = {}
208  self.god_dictionary = {}
209  self.PyQtResource = {}
210  self.PyQtUIC = {}
211 
212  # These are patterns that can be repeated in the requirements.
213  # The corresponding data members will contain the list of dictionaries
214  # corresponding to the various calls.
215  self.multi_patterns = set(['reflex_dictionary', 'component_library', 'linker_library',
216  'copy_relax_rootmap'])
219  self.linker_library = []
220  self.copy_relax_rootmap = []
223  self.component_libraries = set()
224  self.linker_libraries = set()
226  self.log = logging.getLogger('Package(%s)' % self.name)
228  try:
229  self._parseRequirements()
230  except:
231  print "Processing %s" % self.requirements
232  raise
233  # update the known subdirs
234  cache[self.name] = {# list of linker libraries provided by the package
235  'libraries': list(self.linker_libraries),
236  # true if it's a headers-only package
237  'includes': bool(self.install_more_includes and
238  not self.linker_libraries)}

Member Function Documentation

def cmt2cmake.Package._parseRequirements (   self)
private

Definition at line 573 of file cmt2cmake.py.

574  def _parseRequirements(self):
575  def requirements():
576  statement = ""
577  for l in open(self.requirements):
578  if '#' in l:
579  l = l[:l.find('#')]
580  l = l.strip()
581  # if we have something in the line, extend the statement
582  if l:
583  statement += l
584  if statement.endswith('\\'):
585  # if the statement requires another line, get the next
586  statement = statement[:-1] + ' '
587  continue
588  # either we got something more in the statement or not, but
589  # an empty line after a '\' means ending the statement
590  if statement:
591  try:
592  yield list(self.CMTParser.parseString(statement))
593  except:
594  # ignore not know statements
595  self.log.debug("Failed to parse statement: %r", statement)
596  statement = ""
597 
598  for args in requirements():
599  cmd = args.pop(0)
600  if cmd == 'version':
601  self.version = args[0]
602  elif cmd == "use":
603  if "-no_auto_imports" in args:
604  imp = False
605  args.remove("-no_auto_imports")
606  else:
607  imp = True
608  if len(args) > 1: # only one argument means usually a conditional use
609  if len(args) > 2:
610  name = "%s/%s" % (args[2], args[0])
611  else:
612  name = args[0]
613  self.uses[name] = (args[1], imp)
614 
615  elif cmd == "apply_pattern":
616  pattern = args.pop(0)
617  args = dict([x.split('=', 1) for x in args])
618  if pattern in self.singleton_patterns:
619  setattr(self, pattern, args or True)
620  elif pattern in self.multi_patterns:
621  getattr(self, pattern).append(args)
622 
623  elif cmd == 'library':
624  name = args.pop(0)
625  # digest arguments (options, variables, sources)
626  imports = []
627  group = None
628  sources = []
629  for a in args:
630  if a.startswith('-'): # options
631  if a.startswith('-import='):
632  imports.append(a[8:])
633  elif a.startswith('-group='):
634  group = a[7:]
635  elif '=' in a: # variable
636  pass
637  else: # source
638  sources.append(a)
639  self.libraries.append((name, sources, group, imports))
640 
641  elif cmd == 'application':
642  name = args.pop(0)
643  # digest arguments (options, variables, sources)
644  imports = []
645  group = None
646  sources = []
647  for a in args:
648  if a.startswith('-'): # options
649  if a.startswith('-import='):
650  imports.append(a[8:])
651  elif a.startswith('-group='):
652  group = a[7:]
653  elif a == '-check': # used for test applications
654  group = 'tests'
655  elif '=' in a: # variable
656  pass
657  else: # source
658  sources.append(a)
659  if 'test' in name.lower() or [s for s in sources if 'test' in s.lower()]:
660  # usually, developers do not put tests in the right group
661  group = 'tests'
662  self.applications.append((name, sources, group, imports))
663 
664  elif cmd == 'document':
665  name = args.pop(0)
666  constituent = args.pop(0)
667  sources = args
668  self.documents.append((name, constituent, sources))
669 
670  elif cmd == 'macro':
671  # FIXME: should handle macro tags
672  name = args.pop(0)
673  value = args[0].strip('"').strip("'")
674  self.macros[name] = value
675 
676  elif cmd == 'macro_append':
677  # FIXME: should handle macro tags
678  name = args.pop(0)
679  value = args[0].strip('"').strip("'")
680  self.macros[name] = self.macros.get(name, "") + value
681 
682  elif cmd == 'set':
683  name = args.pop(0)
684  if name not in ignore_env:
685  value = args[0].strip('"').strip("'")
686  self.sets[name] = value
687 
688  # classification of libraries in the package
689  unquote = lambda x: x.strip('"').strip("'")
690  self.component_libraries = set([unquote(l['library']) for l in self.component_library])
691  self.linker_libraries = set([unquote(l['library']) for l in self.linker_library])
692  self.reflex_dictionaries = dict([(unquote(l['dictionary']), l.get('options', ''))
693  for l in self.reflex_dictionary])
def cmt2cmake.Package.data_packages (   self)
Return the list of data packages used by this package in the form of a
dictionary {name: version_pattern}.

Definition at line 554 of file cmt2cmake.py.

555  def data_packages(self):
556  '''
557  Return the list of data packages used by this package in the form of a
558  dictionary {name: version_pattern}.
559  '''
560  return dict([ (n, self.uses[n][0]) for n in self.uses if n in data_packages ])
def cmt2cmake.Package.generate (   self)

Definition at line 239 of file cmt2cmake.py.

240  def generate(self):
241  # header
242  data = ["#" * 80,
243  "# Package: %s" % self.name,
244  "#" * 80,
245  "gaudi_subdir(%s %s)" % (self.name, self.version),
246  ""]
247  # dependencies
248  # subdirectories (excluding specials)
249  subdirs = [n for n in sorted(self.uses)
250  if not n.startswith("LCG_Interfaces/")
251  and n not in ignored_packages
252  and n not in data_packages]
253 
254  inc_dirs = []
255  if subdirs:
256  # check if we are missing info for a subdir
257  missing_subdirs = set([s.rsplit('/')[-1] for s in subdirs]) - set(cache)
258  if missing_subdirs:
259  self.log.warning('Missing info cache for subdirs %s', ' '.join(sorted(missing_subdirs)))
260  # declare inclusion order
261  data.append(callStringWithIndent('gaudi_depends_on_subdirs', subdirs))
262  data.append('')
263  # consider header-only subdirs
264  # for each required subdir that comes with only headers, add its
265  # location to the call to 'include_directories'
266  inc_only = lambda s: cache.get(s.rsplit('/')[-1], {}).get('includes')
267  inc_dirs = filter(inc_only, subdirs)
268 
269 
270  # externals (excluding specials)
271  # - Python needs to be treated in a special way
272  find_packages = {}
273  for n in sorted(self.uses):
274  if n.startswith("LCG_Interfaces/"):
275  n = extName(n[15:])
276  # FIXME: find a general way to treat these special cases
277  if n == "PythonLibs":
278  if self.name not in needing_python: # only these packages actually link against Python
279  continue
280  # get custom link options
281  linkopts = self.macros.get(n + '_linkopts', '')
282  components = [m.group(1) or m.group(2)
283  for m in re.finditer(r'(?:\$\(%s_linkopts_([^)]*)\))|(?:-l(\w*))' % n,
284  linkopts)]
285  # FIXME: find a general way to treat the special cases
286  if n == 'COOL':
287  components = ['CoolKernel', 'CoolApplication']
288  elif n == 'CORAL':
289  components = ['CoralBase', 'CoralKernel', 'RelationalAccess']
290  elif n == 'RELAX' and self.copy_relax_rootmap:
291  components = [d['dict'] for d in self.copy_relax_rootmap if 'dict' in d]
292 
293  find_packages[n] = find_packages.get(n, []) + components
294 
295  # this second loops avoid double entries do to converging results of extName()
296  for n in sorted(find_packages):
297  args = [n]
298  components = find_packages[n]
299  if components:
300  if n == 'RELAX': # FIXME: probably we should set 'REQUIRED' for all the externals
301  args.append('REQUIRED')
302  args.append('COMPONENTS')
303  args.extend(components)
304  data.append('find_package(%s)' % ' '.join(args))
305  if find_packages:
306  data.append("")
307 
308  if self.name in no_pedantic:
309  data.append('string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")\n')
310 
311  # the headers can be installed via "PUBLIC_HEADERS" or by hand
312  if self.install_more_includes:
313  headers = [d for d in self.install_more_includes.values()
314  if os.path.isdir(os.path.join(self.path, d))]
315  else:
316  headers = []
317 
318  if self.god_headers or self.god_dictionary:
319  data.append("include(GaudiObjDesc)")
320  data.append("")
321 
322  god_headers_dest = None
323  if self.god_headers:
324  godargs = [self.god_headers["files"].replace("../", "")]
325 
326  godflags = self.macros.get('%sObj2Doth_GODflags' % self.name, "")
327  godflags = re.search(r'-s\s*(\S+)', godflags)
328  if godflags:
329  god_headers_dest = os.path.normpath('Event/' + godflags.group(1))
330  if god_headers_dest == 'src':
331  # special case
332  godargs.append('PRIVATE')
333  else:
334  godargs.append('DESTINATION ' + god_headers_dest)
335 
336  data.append(callStringWithIndent('god_build_headers', godargs))
337  data.append("")
338 
339  god_dict = []
340  if self.god_dictionary:
341  god_dict = [('--GOD--',
342  [self.god_dictionary["files"].replace("../", "")],
343  None, [])]
344 
345  rflx_dict = []
346  for d in self.reflex_dictionary:
347  for k in d:
348  v = d[k]
349  v = v.replace("$(%sROOT)/" % self.name.upper(), "")
350  v = v.replace("../", "")
351  d[k] = v
352  imports = [i.strip('"').replace('-import=', '') for i in d.get('imports', '').strip().split()]
353  rflx_dict.append((d['dictionary'] + 'Dict',
354  [d['headerfiles'], d['selectionfile']],
355  None,
356  imports))
357 
358  # libraries
359  global_imports = [extName(name[15:])
360  for name in self.uses
361  if name.startswith('LCG_Interfaces/') and self.uses[name][1]] # list of imported ext
362  if 'PythonLibs' in global_imports and self.name not in needing_python:
363  global_imports.remove('PythonLibs')
364 
365  subdir_imports = [s.rsplit('/')[-1] for s in subdirs if self.uses[s][1]]
366  local_links = [] # keep track of linker libraries found so far
367  applications_names = set([a[0] for a in self.applications])
368  # Note: a god_dictionary, a reflex_dictionary or an application is like a module
369  for name, sources, group, imports in self.libraries + god_dict + rflx_dict + self.applications:
370  isGODDict = isRflxDict = isComp = isApp = isLinker = False
371  if name == '--GOD--':
372  isGODDict = True
373  name = '' # no library name for GOD dictionaries
374  elif name.endswith('Dict') and name[:-4] in self.reflex_dictionaries:
375  isRflxDict = True
376  name = name[:-4]
377  elif name in self.component_libraries:
378  isComp = True
379  elif name in applications_names:
380  isApp = True
381  else:
382  if name not in self.linker_libraries:
383  self.log.warning('library %s not declared as component or linker, assume linker', name)
384  isLinker = True
385 
386  # prepare the bits of the command: cmd, name, sources, args
387  if isComp:
388  cmd = 'gaudi_add_module'
389  elif isGODDict:
390  cmd = 'god_build_dictionary'
391  elif isRflxDict:
392  cmd = 'gaudi_add_dictionary'
393  elif isApp:
394  cmd = 'gaudi_add_executable'
395  else: # i.e. isLinker (a fallback)
396  cmd = 'gaudi_add_library'
397 
398  if not sources:
399  self.log.warning("Missing sources for target %s", name)
400 
401  args = []
402  if isLinker:
403  if headers:
404  args.append('PUBLIC_HEADERS ' + ' '.join(headers))
405  else:
406  args.append('NO_PUBLIC_HEADERS')
407  elif isGODDict:
408  if god_headers_dest:
409  args.append('HEADERS_DESTINATION ' + god_headers_dest)
410  # check if we have a customdict in the documents
411  for docname, _, docsources in self.documents:
412  if docname == 'customdict':
413  args.append('EXTEND ' + docsources[0].replace('../', ''))
414  break
415 
416 
417  # # collection of link libraries. #
418  # Externals and subdirs are treated differently:
419  # - externals: just use the package name
420  # - subdirs: find the exported libraries in the global var cache
421  # We also have to add the local linker libraries.
422 
423  # separate external and subdir explicit imports
424  subdirsnames = [s.rsplit('/')[-1] for s in subdirs]
425  subdir_local_imports = [i for i in imports if i in subdirsnames]
426  ext_local_imports = [extName(i) for i in imports if i not in subdir_local_imports]
427 
428  # prepare the link list with the externals
429  links = global_imports + ext_local_imports
430  if links or inc_dirs:
431  # external links need the include dirs
432  args.append('INCLUDE_DIRS ' + ' '.join(links + inc_dirs))
433 
434  if links:
435  not_included = set(links).difference(find_packages, set([s.rsplit('/')[-1] for s in subdirs]))
436  if not_included:
437  self.log.warning('imports without use: %s', ', '.join(sorted(not_included)))
438 
439  # add subdirs...
440  for s in subdir_imports + subdir_local_imports:
441  if s in cache:
442  links.extend(cache[s]['libraries'])
443  # ... and local libraries
444  links.extend(local_links)
445  if 'AIDA' in links:
446  links.remove('AIDA') # FIXME: AIDA does not have a library
447 
448  if links:
449  # note: in some cases we get quoted library names
450  args.append('LINK_LIBRARIES ' + ' '.join([l.strip('"') for l in links]))
451 
452  if isRflxDict and self.reflex_dictionaries[name]:
453  args.append('OPTIONS ' + self.reflex_dictionaries[name])
454 
455  if isLinker:
456  local_links.append(name)
457 
458  # FIXME: very very special case :(
459  if name == 'garbage' and self.name == 'FileStager':
460  data.append('# only for the applications\nfind_package(Boost COMPONENTS program_options)\n')
461 
462  # write command
463  if not (isGODDict or isRflxDict):
464  # dictionaries to not need to have the paths fixed
465  sources = [os.path.normpath('src/' + s) for s in sources]
466  # FIXME: special case
467  sources = [s.replace('src/$(GAUDICONFROOT)', '${CMAKE_SOURCE_DIR}/GaudiConf') for s in sources]
468  libdata = callStringWithIndent(cmd, [name] + sources + args)
469 
470  # FIXME: wrap the test libraries in one if block (instead of several)
471  if group in ('tests', 'test'):
472  # increase indentation
473  libdata = [' ' + l for l in libdata.splitlines()]
474  # and wrap
475  libdata.insert(0, 'if(GAUDI_BUILD_TESTS)')
476  libdata.append('endif()')
477  libdata = '\n'.join(libdata)
478  data.append(libdata)
479  data.append('') # empty line
480 
481  # PyQt resources and UIs
482  if self.PyQtResource or self.PyQtUIC:
483  data.append("# gen_pyqt_* functions are provided by 'pygraphics'")
484  if self.PyQtResource:
485  qrc_files = self.PyQtResource["qrc_files"].replace("../", "")
486  qrc_dest = self.PyQtResource["outputdir"].replace("../python/", "")
487  qrc_target = qrc_dest.replace('/', '.') + '.Resources'
488  data.append('gen_pyqt_resource(%s %s %s)' % (qrc_target, qrc_dest, qrc_files))
489  if self.PyQtUIC:
490  ui_files = self.PyQtUIC["ui_files"].replace("../", "")
491  ui_dest = self.PyQtUIC["outputdir"].replace("../python/", "")
492  ui_target = qrc_dest.replace('/', '.') + '.UI'
493  data.append('gen_pyqt_uic(%s %s %s)' % (ui_target, ui_dest, ui_files))
494  if self.PyQtResource or self.PyQtUIC:
495  data.append('') # empty line
496 
497  if self.copy_relax_rootmap:
498  data.extend(['# Merge the RELAX rootmaps',
499  'set(rootmapfile ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/relax.rootmap)',
500  callStringWithIndent('add_custom_command',
501  ['OUTPUT ${rootmapfile}',
502  'COMMAND ${merge_cmd} ${RELAX_ROOTMAPS} ${rootmapfile}',
503  'DEPENDS ${RELAX_ROOTMAPS}']),
504  'add_custom_target(RelaxRootmap ALL DEPENDS ${rootmapfile})',
505  '\n# Install the merged file',
506  'install(FILES ${rootmapfile} DESTINATION lib)\n'])
507 
508  # installation
509  installs = []
510  if headers and not self.linker_libraries: # not installed yet
511  installs.append("gaudi_install_headers(%s)" % (" ".join(headers)))
512  if self.install_python_modules:
513  # if we install Python modules, we need to check if we have special
514  # names for the ConfUser modules
515  if (self.name + 'ConfUserModules') in self.macros:
516  installs.append('set_property(DIRECTORY PROPERTY CONFIGURABLE_USER_MODULES %s)'
517  % self.macros[self.name + 'ConfUserModules'])
518  installs.append("gaudi_install_python_modules()")
519  if self.install_scripts:
520  installs.append("gaudi_install_scripts()")
521  if installs:
522  data.extend(installs)
523  data.append('') # empty line
524 
525  # environment
526  def fixSetValue(s):
527  '''
528  Convert environment variable values from CMT to CMake.
529  '''
530  # escape '$' if not done already
531  s = re.sub(r'(?<!\\)\$', '\\$', s)
532  # replace parenthesis with curly braces
533  s = re.sub(r'\$\(([^()]*)\)', r'${\1}', s)
534  # replace variables like Package_root with PACKAGEROOT
535  v = re.compile(r'\$\{(\w*)_root\}')
536  m = v.search(s)
537  while m:
538  s = s[:m.start()] + ('${%sROOT}' % m.group(1).upper()) + s[m.end():]
539  m = v.search(s)
540  return s
541 
542  if self.sets:
543  data.append(callStringWithIndent('gaudi_env',
544  ['SET %s %s' % (v, fixSetValue(self.sets[v]))
545  for v in sorted(self.sets)]))
546  data.append('') # empty line
547 
548  # tests
549  if self.QMTest:
550  data.append("\ngaudi_add_test(QMTest QMTEST)")
551 
552  return "\n".join(data) + "\n"
def cmt2cmake.Package.process (   self,
  overwrite = None 
)

Definition at line 561 of file cmt2cmake.py.

562  def process(self, overwrite=None):
563  cml = os.path.join(self.path, "CMakeLists.txt")
564  if ((overwrite == 'force')
565  or (not os.path.exists(cml))
566  or ((overwrite == 'update')
567  and (os.path.getmtime(cml) < os.path.getmtime(self.requirements)))):
568  # write the file
569  data = self.generate()
570  writeToFile(cml, data, self.log)
571  else:
572  self.log.warning("file %s already exists", cml)

Member Data Documentation

cmt2cmake.Package.applications

Definition at line 192 of file cmt2cmake.py.

cmt2cmake.Package.CMTParser

Definition at line 226 of file cmt2cmake.py.

cmt2cmake.Package.component_libraries

Definition at line 222 of file cmt2cmake.py.

cmt2cmake.Package.component_library

Definition at line 217 of file cmt2cmake.py.

cmt2cmake.Package.copy_relax_rootmap

Definition at line 219 of file cmt2cmake.py.

cmt2cmake.Package.documents

Definition at line 193 of file cmt2cmake.py.

cmt2cmake.Package.god_dictionary

Definition at line 207 of file cmt2cmake.py.

cmt2cmake.Package.god_headers

Definition at line 206 of file cmt2cmake.py.

cmt2cmake.Package.install_more_includes

Definition at line 204 of file cmt2cmake.py.

cmt2cmake.Package.install_python_modules

Definition at line 205 of file cmt2cmake.py.

cmt2cmake.Package.install_scripts

Definition at line 205 of file cmt2cmake.py.

cmt2cmake.Package.libraries

Definition at line 191 of file cmt2cmake.py.

cmt2cmake.Package.linker_libraries

Definition at line 223 of file cmt2cmake.py.

cmt2cmake.Package.linker_library

Definition at line 218 of file cmt2cmake.py.

cmt2cmake.Package.log

Definition at line 225 of file cmt2cmake.py.

cmt2cmake.Package.macros

Definition at line 194 of file cmt2cmake.py.

cmt2cmake.Package.multi_patterns

Definition at line 214 of file cmt2cmake.py.

cmt2cmake.Package.name

Definition at line 184 of file cmt2cmake.py.

cmt2cmake.Package.path

Definition at line 180 of file cmt2cmake.py.

cmt2cmake.Package.paths

Definition at line 196 of file cmt2cmake.py.

cmt2cmake.Package.project

Definition at line 186 of file cmt2cmake.py.

cmt2cmake.Package.PyQtResource

Definition at line 208 of file cmt2cmake.py.

cmt2cmake.Package.PyQtUIC

Definition at line 209 of file cmt2cmake.py.

cmt2cmake.Package.QMTest

Definition at line 205 of file cmt2cmake.py.

cmt2cmake.Package.reflex_dictionaries

Definition at line 221 of file cmt2cmake.py.

cmt2cmake.Package.reflex_dictionary

Definition at line 216 of file cmt2cmake.py.

cmt2cmake.Package.requirements

Definition at line 185 of file cmt2cmake.py.

cmt2cmake.Package.sets

Definition at line 195 of file cmt2cmake.py.

cmt2cmake.Package.singleton_patterns

Definition at line 201 of file cmt2cmake.py.

cmt2cmake.Package.uses

Definition at line 189 of file cmt2cmake.py.

cmt2cmake.Package.version

Definition at line 190 of file cmt2cmake.py.


The documentation for this class was generated from the following file:
Generated at Wed Nov 28 2012 12:17:35 for Gaudi Framework, version v23r5 by Doxygen version 1.8.2 written by Dimitri van Heesch, © 1997-2004