All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
project_manifest.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 import sys
5 import os
6 from xml.etree import ElementTree as ET
7 import re
8 
9 def indent(elem, level=0):
10  '''
11  Add spaces and newlines to elements to allow pretty-printing of XML.
12 
13  http://effbot.org/zone/element-lib.htm#prettyprint
14  '''
15  i = "\n" + level*" "
16  if len(elem):
17  if not elem.text or not elem.text.strip():
18  elem.text = i + " "
19  if not elem.tail or not elem.tail.strip():
20  elem.tail = i
21  for elem in elem:
22  indent(elem, level+1)
23  if not elem.tail or not elem.tail.strip():
24  elem.tail = i
25  else:
26  if level and (not elem.tail or not elem.tail.strip()):
27  elem.tail = i
28 
29 def CMakeParseArguments(args, options=None, single_value=None, multi_value=None):
30  '''
31  Parse a list or strings using the logic of the CMake function
32  CMAKE_PARSE_ARGUMENTS.
33 
34  >>> res = CMakeParseArguments(['USE', 'Gaudi', 'v25r0', 'DATA', 'Det/SQLDDDB'],
35  ... multi_value=['USE', 'DATA'])
36  >>> res['USE']
37  ['Gaudi', 'v25r0']
38  >>> res['DATA']
39  ['Det/SQLDDDB']
40  >>> res['UNPARSED']
41  []
42  >>> res = CMakeParseArguments('a b OPTION1 c FLAG1 d OPTION2 e f'.split(),
43  ... options=['FLAG1', 'FLAG2'],
44  ... single_value=['OPTION1', 'OPTION2'])
45  >>> res['FLAG1']
46  True
47  >>> res['FLAG2']
48  False
49  >>> res['OPTION1']
50  'c'
51  >>> res['OPTION2']
52  'e'
53  >>> res['UNPARSED']
54  ['a', 'b', 'd', 'f']
55  '''
56  args = list(args)
57  options = set(options or [])
58  single_value = set(single_value or [])
59  multi_value = set(multi_value or [])
60  all_keywords = options.union(single_value).union(multi_value)
61  result = {'UNPARSED': []}
62  result.update((k, False) for k in options)
63  result.update((k, None) for k in single_value)
64  result.update((k, []) for k in multi_value)
65 
66  while args:
67  arg = args.pop(0)
68  if arg in options:
69  result[arg] = True
70  elif arg in single_value:
71  result[arg] = args.pop(0)
72  elif arg in multi_value:
73  while args and args[0] not in all_keywords:
74  result[arg].append(args.pop(0))
75  else:
76  result['UNPARSED'].append(arg)
77  return result
78 
79 if __name__ == '__main__':
80  from optparse import OptionParser
81  parser = OptionParser(usage='%prog [options] <cmake_lists> <lcg_version> <platform>')
82  parser.add_option('-o', '--output', action='store',
83  help='output filename')
84 
85  opts, args = parser.parse_args()
86 
87  if len(args) != 3:
88  parser.error('wrong number of arguments')
89 
90  cmake_lists, lcg_version, platform = args
91 
92  # look for the CMake configuration file
93  if not os.path.exists(cmake_lists):
94  print 'The project does not have a CMake configuration, I cannot produce a manifest.xml'
95  sys.exit(0)
96 
97  project_args = []
98  m = re.search(r'gaudi_project\s*\(([^)]*)\)',
99  open(cmake_lists).read(), re.MULTILINE)
100  if m:
101  project_args = m.group(1).split()
102  if len(project_args) < 2:
103  print 'error: invalid content of CMakeLists.txt'
104  sys.exit(1)
105 
106  # parse gaudi_project arguments
107  name, version = project_args[:2]
108  project_args = project_args[2:]
109 
110  parsed_args = CMakeParseArguments(project_args,
111  options=['FORTRAN'],
112  multi_value=['USE', 'DATA'])
113 
114  # set the output
115  if opts.output in ('-', None):
116  output = sys.stdout
117  else:
118  output = opts.output
119 
120  # prepare the XML content
121  manifest = ET.Element('manifest')
122  manifest.append(ET.Element('project', name=name, version=version))
123 
124  heptools = ET.Element('heptools')
125  ht_version = ET.Element('version')
126  ht_version.text = lcg_version
127  ht_bin_tag = ET.Element('binary_tag')
128  ht_bin_tag.text = platform
129  ht_system = ET.Element('lcg_system')
130  ht_system.text = '-'.join(platform.split('-')[:-1])
131  heptools.extend([ht_version, ht_bin_tag, ht_system])
132  manifest.append(heptools)
133 
134  if parsed_args['USE']:
135  used_projects = ET.Element('used_projects')
136  used_projects.extend(ET.Element('project', name=name, version=version)
137  for name, version in zip(parsed_args['USE'][::2],
138  parsed_args['USE'][1::2]))
139  manifest.append(used_projects)
140 
141  if parsed_args['DATA']:
142  used_data_pkgs = ET.Element('used_data_pkgs')
143  def data_pkgs(args):
144  '''helper to translate the list of data packages'''
145  args = list(args) # clone
146  while args:
147  pkg = args.pop(0)
148  if len(args) >= 2 and args[0] == 'VERSION':
149  args.pop(0)
150  vers = args.pop(0)
151  else:
152  vers = '*'
153  yield (pkg, vers)
154  used_data_pkgs.extend(ET.Element('package', name=pkg, version=vers)
155  for pkg, vers in data_pkgs(parsed_args['DATA']))
156  manifest.append(used_data_pkgs)
157 
158  # finally write the produced XML
159  indent(manifest)
160  ET.ElementTree(manifest).write(output,
161  encoding="UTF-8", xml_declaration=True)