2 '''CMS Conditions DB Serialization generator. 4 Generates the non-intrusive serialization code required for the classes 5 marked with the COND_SERIALIZABLE macro. 7 The code was taken from the prototype that did many other things as well 8 (finding transients, marking serializable classes, etc.). After removing 9 everything but what is required to build the serialization, the code was 10 made more robust and cleaned up a bit to be integrated on the BoostIO IB. 11 However, the code still needs to be restructured a bit more to improve 12 readability (e.g. name some constants, use a template engine, ask for 13 clang's bindings to be installed along clang itself, etc.). 16 __author__ =
'Miguel Ojeda' 17 __copyright__ =
'Copyright 2014, CERN' 18 __credits__ = [
'Giacomo Govi',
'Miguel Ojeda',
'Andreas Pfeiffer']
19 __license__ =
'Unknown' 20 __maintainer__ =
'Miguel Ojeda' 21 __email__ =
'mojedasa@cern.ch' 34 headers_template =
''' 37 #include <boost/serialization/base_object.hpp> 38 #include <boost/serialization/nvp.hpp> 39 #include <boost/serialization/export.hpp> 41 #include "CondFormats/Serialization/interface/Equal.h" 42 #include "CondFormats/Serialization/interface/Instantiate.h" 46 serialize_method_begin_template =
'''template <class Archive> 47 void {klass}::serialize(Archive & ar, const unsigned int) 50 serialize_method_base_object_template =
' ar & boost::serialization::make_nvp("{base_object_name_sanitised}", boost::serialization::base_object<{base_object_name}>(*this));' 52 serialize_method_member_template =
''' ar & boost::serialization::make_nvp("{member_name_sanitised}", {member_name});''' 54 serialize_method_end =
'''} 57 instantiation_template =
'''COND_SERIALIZATION_INSTANTIATE({klass}); 61 skip_namespaces = frozenset([
66 'std',
'boost',
'mpl_',
'boost_swap_impl',
69 'ROOT',
'edm',
'ora',
'coral',
'CLHEP',
'Geom',
'HepGeom',
73 if node.get_definition()
is None:
75 if node.location
is None or node.get_definition().location
is None:
77 return node.location == node.get_definition().location
80 for child
in node.get_children():
81 if child.spelling !=
'serialize' or child.kind != clang.cindex.CursorKind.FUNCTION_TEMPLATE
or is_definition_by_loc(child):
84 if [(x.spelling, x.kind,
is_definition_by_loc(x), x.type.kind)
for x
in child.get_children()] != [
85 (
'Archive', clang.cindex.CursorKind.TEMPLATE_TYPE_PARAMETER,
True, clang.cindex.TypeKind.UNEXPOSED),
86 (
'ar', clang.cindex.CursorKind.PARM_DECL,
True, clang.cindex.TypeKind.LVALUEREFERENCE),
87 (
'version', clang.cindex.CursorKind.PARM_DECL,
True, clang.cindex.TypeKind.UINT),
97 for child
in node.get_children():
98 if child.spelling ==
'cond_serialization_manual' and child.kind == clang.cindex.CursorKind.CXX_METHOD
and not is_definition_by_loc(child):
108 if node.extent.start.file
is None:
111 filename = node.extent.start.file.name
112 start = node.extent.start.offset
113 end = node.extent.end.offset
115 with open(filename,
'rb')
as fd:
118 return source[start:source.find(
';', end)]
123 clang.cindex.TypeKind.BOOL:
'bool',
124 clang.cindex.TypeKind.INT:
'int',
125 clang.cindex.TypeKind.LONG:
'long',
126 clang.cindex.TypeKind.UINT:
'unsigned int',
127 clang.cindex.TypeKind.ULONG:
'unsigned long',
128 clang.cindex.TypeKind.FLOAT:
'float',
129 clang.cindex.TypeKind.DOUBLE:
'double',
132 if node.type.kind
not in typekinds:
133 raise Exception(
'Not a known basic type.')
135 return typekinds[node.type.kind]
139 spelling = node.type.get_declaration().spelling
140 if spelling
is not None:
147 if all_template_types
is None:
148 all_template_types = []
150 logging.debug(
'%s', (node.spelling, all_template_types, namespace))
152 for child
in node.get_children():
153 if child.kind == clang.cindex.CursorKind.NAMESPACE:
157 if child.spelling
in skip_namespaces:
161 if child.spelling.startswith(
'_'):
164 logging.debug(
'Going into namespace %s', child.spelling)
169 if child.kind
in [clang.cindex.CursorKind.CLASS_DECL, clang.cindex.CursorKind.STRUCT_DECL, clang.cindex.CursorKind.CLASS_TEMPLATE]
and is_definition_by_loc(child):
170 logging.debug(
'Found struct/class/template definition: %s', child.spelling
if child.spelling
else '<anonymous>')
172 if only_from_path
is not None \
173 and child.location.file
is not None \
174 and not child.location.file.name.startswith(only_from_path):
175 logging.debug(
'Skipping since it is an external of this package: %s', child.spelling)
180 if child.spelling ==
'':
181 raise Exception(
'It is not possible to serialize anonymous/unnamed structs/classes.')
184 logging.info(
'Found manual serializable struct/class/template: %s', child.spelling)
187 logging.info(
'Found serializable struct/class/template: %s', child.spelling)
193 after_serialize =
False 194 after_serialize_count = 0
195 for member
in child.get_children():
197 if after_serialize_count == 2:
198 after_serialize =
False 200 after_serialize_count = after_serialize_count + 1
203 raise Exception(
'Expected unexposed declaration (friend) after serialize() but found something else: looks like the COND_SERIALIZABLE macro has been changed without updating the script.')
206 raise Exception(
'Could not find COND_SERIALIZABLE in the statement of the expected unexposed declarations (friends) after serialize(). Please fix the script/macro.')
208 logging.debug(
'Skipping expected unexposed declaration (friend) after serialize().')
212 if member.kind == clang.cindex.CursorKind.TEMPLATE_TYPE_PARAMETER:
213 logging.info(
' Found template type parameter: %s', member.spelling)
214 template_types.append((
'typename', member.spelling))
217 elif member.kind == clang.cindex.CursorKind.TEMPLATE_NON_TYPE_PARAMETER:
221 logging.info(
' Found template non-type parameter: %s %s', type_string, member.spelling)
222 template_types.append((type_string, member.spelling))
225 elif member.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER:
227 base_object = member.displayname
229 if base_object.startswith(prefix):
230 base_object = base_object[len(prefix):]
231 logging.info(
' Found base object: %s', base_object)
232 base_objects.append(base_object)
244 logging.info(
' Found member variable: %s', member.spelling)
245 members.append(member.spelling)
248 logging.info(
' Found transient member variable: %s', member.spelling)
249 transients.append(member.spelling)
251 raise Exception(
'Transient %s found for non-serializable class %s', member.spelling, child.spelling)
253 elif member.kind == clang.cindex.CursorKind.FUNCTION_TEMPLATE
and member.spelling ==
'serialize':
254 after_serialize =
True 255 logging.debug(
'Found serialize() method, skipping next two children which must be unexposed declarations.')
257 elif member.kind
in frozenset([
261 clang.cindex.CursorKind.CONSTRUCTOR,
262 clang.cindex.CursorKind.DESTRUCTOR,
263 clang.cindex.CursorKind.CXX_METHOD,
264 clang.cindex.CursorKind.CXX_ACCESS_SPEC_DECL,
265 clang.cindex.CursorKind.FUNCTION_TEMPLATE,
266 clang.cindex.CursorKind.TYPEDEF_DECL,
267 clang.cindex.CursorKind.CLASS_DECL,
268 clang.cindex.CursorKind.ENUM_DECL,
269 clang.cindex.CursorKind.VAR_DECL,
270 clang.cindex.CursorKind.STRUCT_DECL,
271 clang.cindex.CursorKind.UNION_DECL,
272 clang.cindex.CursorKind.CONVERSION_FUNCTION,
273 clang.cindex.CursorKind.TYPE_REF,
274 clang.cindex.CursorKind.DECL_REF_EXPR,
275 clang.cindex.CursorKind.CLASS_TEMPLATE,
276 clang.cindex.CursorKind.TYPE_ALIAS_DECL,
278 logging.debug(
'Skipping member: %s %s %s %s', member.displayname, member.spelling, member.kind, member.type.kind)
284 if 'friend' in statement:
287 'friend class ' in statement
or \
288 'friend struct ' in statement
or \
289 'friend std::ostream& operator<<(' in statement
or \
290 'friend std::istream& operator>>(' in statement:
291 logging.debug(
'Skipping known friend: %s', statement.splitlines()[0])
295 logging.warning(
'Unexposed declaration that looks like a friend declaration -- please check: %s %s %s %s %s', member.displayname, member.spelling, member.kind, member.type.kind, statement)
298 raise Exception(
'Unexposed declaration. This probably means (at the time of writing) that an unknown class was found (may happen, for instance, when the compiler does not find the headers for std::vector, i.e. missing -I option): %s %s %s %s %s' % (member.displayname, member.spelling, member.kind, member.type.kind, statement))
302 raise Exception(
'Unknown kind. Please fix the script: %s %s %s %s %s' % (member.displayname, member.spelling, member.kind, member.type.kind, statement))
305 template_use =
'%s<%s>' % (child.spelling,
', '.
join([template_type_name
for (_, template_type_name)
in template_types]))
307 template_use = child.spelling
309 new_namespace = namespace + template_use
311 new_all_template_types = all_template_types + [template_types]
313 results[new_namespace] = (child, serializable, new_all_template_types, base_objects, members, transients)
317 for (klass, (node, serializable, all_template_types, base_objects, members, transients))
in results.items():
318 if serializable
and len(members) == 0:
319 logging.info(
'No non-transient members found for serializable class %s', klass)
328 path, folder = os.path.split(path)
331 folders.append(folder)
343 command =
"scram b echo_%s_%s | tail -1 | cut -d '=' -f '2-' | xargs -n1" % (product_name, flags)
344 logging.debug(
'Running: %s', command)
345 return subprocess.check_output(command, shell=
True).splitlines()
348 """Extract clang version and set global clang_version and also return the same value.""" 350 if clang_version
is not None:
352 command =
"clang --version | grep 'clang version' | sed 's/clang version//'" 353 logging.debug(
"Running: {0}".
format(command))
354 (clang_version_major, clang_version_minor, clang_version_patchlevel) = subprocess.check_output(command, shell=
True).splitlines()[0].
strip().
split(
'.', 3)
355 clang_version = (
int(clang_version_major),
int(clang_version_minor),
int(clang_version_patchlevel))
356 logging.debug(
"Detected Clang version: {0}".
format(clang_version))
360 """Check if declaration is a friend""" 362 if clangv >= (4, 0, 0):
363 return memkind == clang.cindex.CursorKind.FRIEND_DECL
365 return memkind == clang.cindex.CursorKind.UNEXPOSED_DECL
369 logging.debug(
'%s = [', name)
371 logging.debug(
' %s', flag)
376 return map(
lambda diag: {
377 'severity' : diag.severity,
378 'location' : diag.location,
379 'spelling' : diag.spelling,
380 'ranges' : diag.ranges,
381 'fixits' : diag.fixits,
382 }, translation_unit.diagnostics)
386 command =
'echo "" | %s -x%s -v -E - 2>&1' % (gcc, language)
387 logging.debug(
'Running: %s', command)
391 for line
in subprocess.check_output(command, shell=
True).splitlines():
393 if line ==
'End of search list.':
396 path = os.path.normpath(line.strip())
401 if '/lib/gcc/' in path:
404 paths.append(
'-I%s' % path)
407 if line ==
'#include <...> search starts here:':
411 raise Exception(
'Default GCC search paths not found.')
416 return re.sub(
'[^a-zA-Z0-9.,-:]',
'-', var)
425 raise Exception(
'CMSSW_BASE is not set.')
426 logging.debug(
'cmssw_base = %s', self.
cmssw_base)
429 logging.debug(
'cwd = %s', cwd)
432 raise Exception(
'The filepath does not start with CMSSW_BASE.')
435 logging.debug(
'relative_path = %s', relative_path)
438 logging.debug(
'splitpath = %s', self.
split_path)
441 raise Exception(
'This script requires to be run inside a CMSSW package (usually within CondFormats), e.g. CondFormats/Alignment. The current path is: %s' % self.
split_path)
444 raise Exception(
'The first folder should be src.')
447 raise Exception(
'The second folder should be CondFormats.')
450 logging.debug(
'product_name = %s', product_name)
453 cpp_flags =
get_flags(product_name,
'CPPFLAGS')
454 cxx_flags =
get_flags(product_name,
'CXXFLAGS')
465 flags = [
'-xc++'] + cpp_flags + cxx_flags + std_flags
468 logging.debug(
'headers_h = %s', headers_h)
469 if not os.path.exists(headers_h):
470 raise Exception(
'File %s does not exist. Impossible to serialize package.' % headers_h)
472 logging.info(
'Searching serializable classes in %s/%s ...', self.
split_path[1], self.
split_path[2])
474 logging.debug(
'Parsing C++ classes in file %s ...', headers_h)
476 if "SCRAM_ARCH" in os.environ
and re.match(
'osx10*',os.environ[
'SCRAM_ARCH']):
478 libpath=os.path.dirname(os.path.realpath(clang.cindex.__file__))+
"/../../lib" 479 cindex.Config.set_library_path(libpath)
480 index = cindex.Index.create()
482 index = clang.cindex.Index.create()
483 translation_unit = index.parse(headers_h, flags)
484 if not translation_unit:
487 severity_names = (
'Ignored',
'Note',
'Warning',
'Error',
'Fatal')
488 get_severity_name =
lambda severity_num: severity_names[severity_num]
if severity_num < len(severity_names)
else 'Unknown' 489 max_severity_level = 0
491 for diagnostic
in diagnostics:
495 if diagnostic[
'spelling'].startswith(
'argument unused during compilation') \
496 or diagnostic[
'spelling'].startswith(
'unknown warning option'):
499 logf(
'Diagnostic: [%s] %s', get_severity_name(diagnostic[
'severity']), diagnostic[
'spelling'])
500 logf(
' at line %s in %s', diagnostic[
'location'].line, diagnostic[
'location'].file)
502 max_severity_level =
max(max_severity_level, diagnostic[
'severity'])
504 if max_severity_level >= 3:
505 raise Exception(
'Please, resolve all errors before proceeding.')
513 flags = [ flag
for flag
in flagsIn
if not flag.startswith((
'-march',
'-mtune',
'-fdebug-prefix-map',
'-ax',
'-wd')) ]
514 blackList = [
'--',
'-fipa-pta',
'-xSSE3',
'-fno-crossjumping',
'-fno-aggressive-loop-optimizations']
515 return [x
for x
in flags
if x
not in blackList]
519 filename = outFileName
523 n_serializable_classes = 0
525 source = headers_template.format(headers=os.path.join(self.
split_path[1], self.
split_path[2],
'src',
'headers.h'))
527 for klass
in sorted(self.
classes):
528 (node, serializable, all_template_types, base_objects, members, transients) = self.
classes[klass]
533 n_serializable_classes += 1
535 skip_instantiation =
False 536 for template_types
in all_template_types:
538 skip_instantiation =
True 539 source += (
'template <%s>' %
', '.
join([
'%s %s' % template_type
for template_type
in template_types])) +
'\n' 541 source += serialize_method_begin_template.format(klass=klass) +
'\n' 543 for base_object_name
in base_objects:
544 base_object_name_sanitised =
sanitise(base_object_name)
545 source += serialize_method_base_object_template.format(base_object_name=base_object_name, base_object_name_sanitised=base_object_name_sanitised) +
'\n' 547 for member_name
in members:
548 member_name_sanitised =
sanitise(member_name)
549 source += serialize_method_member_template.format(member_name=member_name, member_name_sanitised=member_name_sanitised) +
'\n' 551 source += serialize_method_end
553 if skip_instantiation:
556 source += instantiation_template.format(klass=klass) +
'\n' 558 if n_serializable_classes == 0:
559 raise Exception(
'No serializable classes found, while this package has a headers.h file.')
562 if os.path.exists(
'./src/SerializationManual.h' ) :
563 source +=
'#include "%s/%s/src/SerializationManual.h"\n' % (self.
split_path[1], self.
split_path[2])
565 logging.info(
'Writing serialization code for %s classes in %s ...', n_serializable_classes, filename)
566 with open(filename,
'wb')
as fd:
571 parser = argparse.ArgumentParser(description=
'CMS Condition DB Serialization generator.')
572 parser.add_argument(
'--verbose',
'-v', action=
'count', help=
'Verbosity level. -v reports debugging information.')
573 parser.add_argument(
'--output' ,
'-o', action=
'store', help=
'Specifies the path to the output file written. Default: src/Serialization.cc')
574 parser.add_argument(
'--package',
'-p', action=
'store', help=
'Specifies the path to the package to be processed. Default: the actual package')
576 opts, args = parser.parse_known_args()
578 logLevel = logging.INFO
579 if opts.verbose < 1
and opts.output
and opts.package:
580 logLevel = logging.WARNING
582 if opts.verbose >= 1:
583 logLevel = logging.DEBUG
586 format =
'[%(asctime)s] %(levelname)s: %(message)s',
591 pkgDir = opts.package
592 if pkgDir.endswith(
'/src') :
593 pkgDir, srcDir = os.path.split( opts.package )
595 logging.info(
"Processing package in %s " % pkgDir)
598 logging.info(
"Writing serialization code to %s " % opts.output)
602 if __name__ ==
'__main__':
def generate(map_blobs=False, class_name=None)
static std::string join(char **cmd)