gen_flatrecord.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # This script is intended to be compatible with both python2 and python3
4 from __future__ import print_function
5 
6 from pygccxml import *
7 
8 import os
9 import sys
10 
11 # Don't spam pygccxml deprecation warnings
12 import warnings
13 warnings.simplefilter(action = 'once', category = DeprecationWarning)
14 
15 # Types that ROOT will understand (with a little nudging in some cases)
16 fundamental_types = ['int', 'float', 'double', 'bool', 'unsigned int',
17  'short', 'short int', 'short unsigned int',
18  'long', 'long unsigned int',
19  'long long int', 'unsigned char',
20  'size_t',
21  'std::string']
22 
23 enums = []
24 
25 def is_fundamental(type):
26  while type[:5] == 'caf::': type = type[5:]
27 
28  return type in fundamental_types or type in enums
29 
30 
31 def translate_type(type):
32  while type[:5] == 'caf::': type = type[5:]
33 
34  # enums are small, right?
35  if type in enums: return 'short unsigned int'
36  # TTree can't handle long long at all? This will lose information though...
37  if type == 'long long int': return 'long'
38 
39  return type
40 
41 
42 def is_vector(type):
43  return type[:12] == 'std::vector<'
44 
45 
46 def vector_contents(type):
47  assert is_vector(type)
48  return type[12:type.find(',')]
49 
50 
51 def is_array(type):
52  return '[' in type
53 
54 
55 def array_type(type):
56  assert is_array(type)
57  # There always seems to be a space before the dimensions
58  return type[:type.find('[')-1]
59 
60 
61 def array_size(type):
62  assert is_array(type)
63  return int(type[type.find('[')+1:-1])
64 
65 
67  while type[:5] == 'caf::': type = type[5:]
68 
69  assert not is_fundamental(type)
70  assert not is_vector(type)
71 
72  if type == 'StandardRecord': return 'FlatRecord'
73 
74  return 'Flat'+type[2:]
75 
76 
77 def base_class(klass):
78  assert len(klass.bases) < 2, 'Support for multiple base classes not implemented'
79  if len(klass.bases) == 1:
80  return klass.bases[0].related_class
81  return None
82 
83 
84 # Recurse to find member variables including all base classes
86  vars = list(klass.variables())
87  base = base_class(klass)
88  if base: vars += variables_inc_bases(base)
89  return vars
90 
91 
92 def branch_cmd(name, treeName = 'tr'):
93  return 'branch('+treeName+', prefix+"'+name+'", &'+name+', policy);'
94 
95 def branch_cmd_no_policy(name, treeName = 'tr'):
96  return 'branch('+treeName+', prefix+"'+name+'", &'+name+', 0);'
97 
98 
99 
100 if len(sys.argv) < 1 or len(sys.argv) > 3:
101  print('Usage: gen_flatrecord.py [/path/to/header/outputs/] [/path/to/cxx/outputs/]')
102  sys.exit(1)
103 
104 headerDir = os.getcwd()
105 if len(sys.argv) >= 2: headerDir = sys.argv[1]
106 cxxDir = os.getcwd()
107 if len(sys.argv) >= 3: cxxDir = sys.argv[2]
108 
109 # Locate the castxml executable
110 generator_path, generator_name = utils.find_xml_generator()
111 
112 # Figure out where our source files are
113 priv = os.environ['SRT_PRIVATE_CONTEXT'] if 'SRT_PRIVATE_CONTEXT' in os.environ else None
114 pub = os.environ['SRT_PUBLIC_CONTEXT'] if 'SRT_PUBLIC_CONTEXT' in os.environ else None
115 
116 # For cmake build
117 if not pub: pub = os.environ['NOVASOFT_DIR']
118 
119 context = priv if (priv and os.path.exists(priv+'/StandardRecord/')) else pub
120 
121 path = []
122 if priv: path += [priv]
123 path += [pub]
124 path += [os.environ['ROOT_INC']]
125 
126 config = parser.xml_generator_configuration_t(
127  xml_generator_path=generator_path,
128  xml_generator=generator_name,
129  include_paths=path,
130  cflags='-std=c++1z -Wno-unknown-warning-option'#,
131 # start_with_declarations='caf::StandardRecord'
132  )
133 
134 decls = parser.parse([context+'/StandardRecord/StandardRecord.h'],
135  config)
136 
137 global_namespace = declarations.get_global_namespace(decls)
138 ns = global_namespace.namespace('caf')
139 
140 enums += [e.name for e in ns.enumerations()]
141 
142 # Keep track of which classes we've written out so far, for purposes of
143 # dependency tracking.
144 emitted = []
145 
146 
147 disclaimer = '''// This file was auto-generated by gen_flatrecord.py.
148 // DO NOT EDIT IT DIRECTLY.
149 // For documentation of the fields see the regular StandardRecord.h'''
150 
151 # We write individual headers so that the preprocessor will handle our
152 # dependency order, but put all the code in one .cxx becase it's much faster to
153 # compile that way.
154 sys.stdout = open(cxxDir+'/FlatRecord.cxx', 'wa')
155 print(disclaimer)
156 print()
157 print('#include "StandardRecord/StandardRecord.h"')
158 print()
159 print('#include "FlatMaker/TreeUtils.h"')
160 print()
161 print('#include "TTree.h"')
162 
163 for klass in ns.classes():
164  pt = type_to_flat_type(klass.name)
165 
166  sys.stdout = open(headerDir+'/'+pt+'.h', 'w')
167 
168  class TypeName:
169  def __init__(self, t, n, length = -1):
170  self.type = t
171  self.name = n
172  self.length = length
173 
174  def is_array(self): return self.length > 0
175 
176  basic_mems = []
177  class_mems = []
178  vec_mems = []
179  for v in variables_inc_bases(klass):
180  type = str(v.decl_type)
181 
182  if is_fundamental(type):
183  basic_mems += [TypeName(translate_type(type), v.name)]
184  elif is_array(type):
185  innert = array_type(type)
186  size = array_size(type)
187  if is_fundamental(innert):
188  vec_mems += [TypeName(innert, v.name, size)]
189  else:
190  vec_mems += [TypeName(type_to_flat_type(innert), v.name, size)]
191  elif is_vector(type):
192  innert = vector_contents(type)
193  if is_fundamental(innert):
194  vec_mems += [TypeName(innert, v.name)]
195  else:
196  vec_mems += [TypeName(type_to_flat_type(innert), v.name)]
197  else:
198  class_mems += [TypeName(type_to_flat_type(type), v.name)]
199 
200  print(disclaimer)
201  print()
202  print('#pragma once')
203  print()
204  print('#include "StandardRecord/SREnums.h"')
205  print()
206  incs = set()
207  for tn in class_mems: incs.add(tn.type)
208  for tn in vec_mems:
209  if not is_fundamental(tn.type):
210  incs.add(tn.type)
211  for i in incs: print('#include "FlatMaker/'+i+'.h"')
212  print()
213  print('#include <string>')
214  print()
215  print('class TTree;')
216  print('namespace caf{class '+klass.name+';}')
217  print()
218  print('namespace flat')
219  print('{')
220  print('class IBranchPolicy;')
221  print()
222  print('/// Flat encoding of \\ref', klass.name)
223  print('class', pt)
224  print('{')
225  print('public:')
226  print(' '+pt+'(const std::string& prefix, TTree* tr, const IBranchPolicy* policy);')
227  print(' ~'+pt+'();')
228  print()
229  print(' void Fill(const caf::'+klass.name+'& sr);')
230  print()
231  print('protected:')
232 
233  # Members
234  for tn in basic_mems:
235  print(' '+tn.type+' '+tn.name+';')
236 
237  if len(basic_mems) > 0 and len(class_mems) > 0: print()
238 
239  for tn in class_mems:
240  print(' '+tn.type+' '+tn.name+';')
241 
242  for tn in vec_mems:
243  print()
244  print(' TTree* '+tn.name+'_tree;')
245  print(' '+tn.type+' '+tn.name+';')
246  print(' long '+tn.name+'_idx;')
247  print(' int '+tn.name+'_length;')
248 
249  print('};')
250  print('} // end namespace')
251 
252  # Switch to appending into the main cxx file
253  sys.stdout = open(cxxDir+'/FlatRecord.cxx', 'a')
254 
255  print()
256  print('#include "FlatMaker/'+pt+'.h"')
257  print()
258  print('flat::'+pt+'::'+pt+'(const std::string& prefix, TTree* tr, const IBranchPolicy* policy)')
259 
260  # Initializer list
261  inits = [tn.name+'(prefix+"'+tn.name+'.", tr, policy)' for tn in class_mems]
262 
263  for tn in vec_mems:
264  inits += [tn.name+'_tree(make_tree(prefix+"'+tn.name+'", "'+tn.name+'", tr))']
265 
266  if is_fundamental(tn.type):
267  inits += [tn.name+'(0)']
268  else:
269  inits += [tn.name+'((prefix+"'+tn.name+'."), '+tn.name+'_tree, policy)']
270 
271  if tn.is_array():
272  inits += [tn.name+'_idx(0), '+tn.name+'_length(sizeof(caf::'+klass.name+'::'+tn.name+')/sizeof(*caf::'+klass.name+'::'+tn.name+'))']
273  else:
274  inits += [tn.name+'_idx(0), '+tn.name+'_length(0)']
275 
276  if len(inits) > 0: print(' : '+',\n '.join(inits))
277 
278  print('{')
279 
280  # Constructor body
281  for tn in basic_mems:
282  print(' '+branch_cmd(tn.name))
283 
284  for tn in vec_mems:
285  if is_fundamental(tn.type):
286  print(' '+branch_cmd(tn.name, tn.name+'_tree'))
287  print(' if('+tn.name+'_tree->GetNbranches() > 0){')
288  print(' '+branch_cmd_no_policy(tn.name+'_idx'))
289  print(' '+branch_cmd_no_policy(tn.name+'_length'))
290  print(' }')
291 
292  print('}')
293 
294  print()
295 
296  print('flat::'+pt+'::~'+pt+'()')
297  print('{')
298  for tn in vec_mems:
299  trName = tn.name+'_tree'
300  print(' if('+trName+'->GetNbranches() > 0) '+trName+'->Write();')
301  print(' delete '+trName+';')
302  print('}')
303 
304  print()
305 
306  print('void flat::'+pt+'::Fill(const caf::'+klass.name+'& sr)')
307  print('{')
308  for tn in basic_mems:
309  print(' '+tn.name+' = sr.'+tn.name+';')
310 
311  if len(basic_mems) > 0 and len(class_mems) > 0: print()
312 
313  for tn in class_mems:
314  print(' '+tn.name+'.Fill(sr.'+tn.name+');')
315 
316  for tn in vec_mems:
317  print()
318  print(' '+tn.name+'_idx += '+tn.name+'_length; // increment taken by previous record')
319  if not tn.is_array(): # have to re-fill each time for vectors
320  print(' '+tn.name+'_length = sr.'+tn.name+'.size();')
321  print(' for(const auto& x: sr.'+tn.name+'){')
322  if is_fundamental(tn.type):
323  print(' '+tn.name+' = x;')
324  else:
325  print(' '+tn.name+'.Fill(x);')
326  print(' '+tn.name+'_tree->Fill();');
327  print(' }')
328  print('}')
329 
330 sys.stderr.write('Wrote FlatRecord.cxx and associated headers\n')
def base_class(klass)
def variables_inc_bases(klass)
def array_type(type)
def branch_cmd_no_policy(name, treeName='tr')
def array_size(type)
def branch_cmd(name, treeName='tr')
bool print
def is_fundamental(type)
def is_array(type)
procfile open("FD_BRL_v0.txt")
def translate_type(type)
def __init__(self, t, n, length=-1)
def is_vector(type)
def vector_contents(type)
def type_to_flat_type(type)