| The Gaudi Framework v27r1

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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 from subprocess import Popen, PIPE
8 import re
9 import logging
10
11 def indent(elem, level=0):
12 '''
13 Add spaces and newlines to elements to allow pretty-printing of XML.
14
15 http://effbot.org/zone/element-lib.htm#prettyprint
16 '''
17 i = "\n" + level*" "
18 if len(elem):
19 if not elem.text or not elem.text.strip():
20 elem.text = i + " "
21 if not elem.tail or not elem.tail.strip():
22 elem.tail = i
23 for elem in elem:
24 indent(elem, level+1)
25 if not elem.tail or not elem.tail.strip():
26 elem.tail = i
27 else:
28 if level and (not elem.tail or not elem.tail.strip()):
29 elem.tail = i
30
31 def CMakeParseArguments(args, options=None, single_value=None, multi_value=None):
32 '''
33 Parse a list or strings using the logic of the CMake function
34 CMAKE_PARSE_ARGUMENTS.
35
36 >>> res = CMakeParseArguments(['USE', 'Gaudi', 'v25r0', 'DATA', 'Det/SQLDDDB'],
37 ... multi_value=['USE', 'DATA'])
38 >>> res['USE']
39 ['Gaudi', 'v25r0']
40 >>> res['DATA']
41 ['Det/SQLDDDB']
42 >>> res['UNPARSED']
43 []
44 >>> res = CMakeParseArguments('a b OPTION1 c FLAG1 d OPTION2 e f'.split(),
45 ... options=['FLAG1', 'FLAG2'],
46 ... single_value=['OPTION1', 'OPTION2'])
47 >>> res['FLAG1']
48 True
49 >>> res['FLAG2']
50 False
51 >>> res['OPTION1']
52 'c'
53 >>> res['OPTION2']
54 'e'
55 >>> res['UNPARSED']
56 ['a', 'b', 'd', 'f']
57 '''
58 args = list(args)
59 options = set(options or [])
60 single_value = set(single_value or [])
61 multi_value = set(multi_value or [])
62 all_keywords = options.union(single_value).union(multi_value)
63 result = {'UNPARSED': []}
64 result.update((k, False) for k in options)
65 result.update((k, None) for k in single_value)
66 result.update((k, []) for k in multi_value)
67
68 while args:
69 arg = args.pop(0)
70 if arg in options:
71 result[arg] = True
72 elif arg in single_value:
73 result[arg] = args.pop(0)
74 elif arg in multi_value:
75 while args and args[0] not in all_keywords:
76 result[arg].append(args.pop(0))
77 else:
78 result['UNPARSED'].append(arg)
79 return result
80
81 if __name__ == '__main__':
82 from optparse import OptionParser
83 parser = OptionParser(usage='%prog [options] <cmake_lists> <lcg_version> <platform>')
84 parser.add_option('-o', '--output', action='store',
85 help='output filename')
86
87 opts, args = parser.parse_args()
88
89 if len(args) != 3:
90 parser.error('wrong number of arguments')
91
92 cmake_lists, lcg_version, platform = args
93
94 logging.basicConfig(level=logging.INFO)
95
96 # look for the CMake configuration file
97 if not os.path.exists(cmake_lists):
98 print 'The project does not have a CMake configuration, I cannot produce a manifest.xml'
99 sys.exit(0)
100
101 project_args = []
102 m = re.search(r'gaudi_project\s*\(([^)]*)\)',
103 open(cmake_lists).read(), re.MULTILINE)
104 if m:
105 project_args = m.group(1).split()
106 if len(project_args) < 2:
107 print 'error: invalid content of CMakeLists.txt'
108 sys.exit(1)
109
110 # parse gaudi_project arguments
111 name, version = project_args[:2]
112 project_args = project_args[2:]
113
114 parsed_args = CMakeParseArguments(project_args,
115 options=['FORTRAN'],
116 multi_value=['USE', 'DATA'])
117
118 # set the output
119 if opts.output in ('-', None):
120 output = sys.stdout
121 else:
122 output = opts.output
123
124 # prepare the XML content
125 manifest = ET.Element('manifest')
126 manifest.append(ET.Element('project', name=name, version=version))
127
128 heptools = ET.Element('heptools')
129 ht_version = ET.Element('version')
130 ht_version.text = lcg_version
131 ht_bin_tag = ET.Element('binary_tag')
132 ht_bin_tag.text = platform
133 ht_system = ET.Element('lcg_system')
134 ht_system.text = '-'.join(platform.split('-')[:-1])
135 heptools.extend([ht_version, ht_bin_tag, ht_system])
136 manifest.append(heptools)
137
138 if parsed_args['USE']:
139 used_projects = ET.Element('used_projects')
140 used_projects.extend(ET.Element('project', name=name, version=version)
141 for name, version in zip(parsed_args['USE'][::2],
142 parsed_args['USE'][1::2]))
143 manifest.append(used_projects)
144
145 if parsed_args['DATA']:
146 used_data_pkgs = ET.Element('used_data_pkgs')
147 def data_pkgs(args):
148 '''helper to translate the list of data packages'''
149 args = list(args) # clone
150 while args:
151 pkg = args.pop(0)
152 if len(args) >= 2 and args[0] == 'VERSION':
153 args.pop(0)
154 vers = args.pop(0)
155 else:
156 vers = '*'
157 yield (pkg, vers)
158 used_data_pkgs.extend(ET.Element('package', name=pkg, version=vers)
159 for pkg, vers in data_pkgs(parsed_args['DATA']))
160 manifest.append(used_data_pkgs)
161
162
163 logging.debug('collecting external dependencies info (with CMT)')
164 p = Popen(['cmt', 'show', 'uses'], stdout=PIPE)
165 externals = [l.split()[1]
166 for l in p.stdout
167 if l.startswith('use') and
168 'LCG_Interfaces' in l]
169 externals.sort()
170
171 # get the versions of the externals
172 def get_ext_vers(ext):
173 '''
174 Ask CMT the version of an external.
175 '''
176 logging.debug('getting version of %s', ext)
177 vers = Popen(['cmt', 'show', 'macro_value',
178 '%s_native_version' % ext],
179 stdout=PIPE).communicate()[0].strip()
180 logging.debug('using %s %s', ext, vers)
181 if vers == 'dummy': # special case in LCG
182 vers = ''
183 return vers
184
185 # mapping between LCG_Interface name and RPM name for special cases
186 rpm_names = {'Expat': 'expat'}
187 fix_rpm_name = lambda n: rpm_names.get(n, n)
188
189 packages = ET.Element('packages')
190 packages.extend([ET.Element('package', name=fix_rpm_name(ext), version=vers)
191 for ext, vers in [(ext, get_ext_vers(ext))
192 for ext in externals]
193 if vers])
194 heptools.append(packages)
195
196 destdir = os.path.dirname(output)
197 if not os.path.exists(destdir):
198 logging.debug('creating directory %s', destdir)
199 os.makedirs(destdir)
200
201 # finally write the produced XML
202 logging.debug('writing manifest file %s', output)
203 indent(manifest)
204 ET.ElementTree(manifest).write(output,
205 encoding="UTF-8", xml_declaration=True)
206
207 logging.debug('%s written', output)
project_manifest.CMakeParseArguments
def CMakeParseArguments
Definition: project_manifest.py:31
project_manifest.indent
def indent
Definition: project_manifest.py:11