ATLAS Offline Software
MergeConfigs.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
4 
5 """
6 MergeConfigs: merges han text configuration files, warning on name conflicts
7 @author Peter Onyisi <ponyisi@hep.uchicago.edu>
8 """
9 
10 
11 def list_directories(parent_dir, recurse=True):
12  """
13  returns a list of directories in parent_dir
14  Directory 'common' will appear first if present
15  if recurse = True, also includes all subdirectories in the tree
16  If user has defined DQCONFIG_DIR_WHITELIST env var, will filter based on that list
17  Example values would be: DQCONFIG_DIR_WHITELIST=L1Calo:HLT
18  """
19  import os
20  whitelist = os.getenv('DQCONFIG_DIR_WHITELIST','').split(":")
21  rv = []
22  if recurse:
23  for root, dum1, dum2 in os.walk(parent_dir):
24  if root == parent_dir:
25  continue
26  rv.append(root)
27  else:
28  import stat
29  subobjs = os.listdir(parent_dir)
30  rv = [os.join(parent_dir, x) for x in subobjs]
31  rv = [x for x in rv if stat.S_ISDIR(os.stat(x))]
32  rv.sort()
33  truerv = []
34  for x in rv:
35  if 'common' in x:
36  truerv = [x] + truerv
37  elif x.split("/")[-1] in whitelist or whitelist==['']:
38  truerv.append(x)
39  return truerv
40 
41 def _is_ok(dir, options):
42  return not dir.startswith(tuple(options.exclude), 2)
43 
44 def merge_han_configs(template, parent_dir, out, options):
45  import sys, os
46  """
47  Merge the files in the input list to the output file.
48  @param inlist: list of filenames of input files
49  @param out: output filename
50  """
51  import re
52  from PyUtils.Helpers import release_metadata
53 
54  rematch = re.compile(r'(\S*)\s+(\S*)\s+{')
55  # keywords we should worry about for name conflicts
56  kwlist = set(['algorithm', 'compositeAlgorithm', 'reference', 'thresholds'])
57  kwhash = {}
58  for kw in kwlist:
59  kwhash[kw] = {}
60 
61  files = []
62 
63  # First pass; find if there are name conflicts
64  for dir in list_directories(parent_dir):
65  f = os.path.join(dir, template)
66  if not os.access(f, os.R_OK):
67  continue
68  if not _is_ok(dir, options):
69  print (dir, 'excluded from merge')
70  continue
71  print ('Processing', f)
72  files.append(f)
73  fobj = open(f, 'r')
74  # reclevel = how nested we are; reclist = directories above us; kws = previous keywords
75  reclevel = 0; reclist = []; kws = []
76 
77  for line in fobj:
78  stripline = line.strip()
79  if len(stripline) == 0:
80  continue
81  if stripline[0] == '#':
82  continue
83  if stripline[0] == '}' and reclevel > 0:
84  # in principle a properly parsing file should only close objects it has opened. Dangerous.
85  reclevel -= 1; reclist.pop(); kws.pop()
86  continue
87  match = rematch.search(line)
88  if match is None:
89  continue
90  kws.append(match.group(1)); reclevel += 1; reclist.append(match.group(2))
91  if kws[-1] in kwlist:
92  # print (line)
93  # print (match.group(1), match.group(2),)
94  fullname = '/'.join(reclist)
95  # print (fullname)
96  if fullname in kwhash[match.group(1)]:
97  # print (reclist)
98  print ('ERROR: repeated definition of %s %s' % (match.group(1), fullname))
99  print (' Current file is %s' % f)
100  print (' Earlier definition was in %s' % kwhash[match.group(1)][fullname])
101  print (' Please fix this. Merging will now stop. Output file has not been created.')
102  sys.exit(1)
103  else:
104  kwhash[match.group(1)][fullname] = f
105  fobj.close()
106 
107  # if we got here, we should be ok on the unique names: pass 2
108  outobj = open(out, 'w')
109  outobj.write('# ****************************\n')
110  outobj.write('metadata GitInfo {\n')
111  outobj.write(' Hash = %s\n' % (release_metadata()['nightly release'] or "unknown"))
112  outobj.write('}\n')
113  outobj.write('# ****************************\n\n')
114  for f in files:
115  fobj = open(f, 'r')
116  for line in fobj:
117  if '$Id:' in line:
118  line += '# ' + f + '\n'
119  line = line.replace('$', '')
120  outobj.write(line)
121  outobj.write('\n')
122 
123 _usage = ('%prog [options] templatename parent_dir outfile\n\n'
124  'example: %prog cosmics_run.config . cosmics_run_merged.config')
125 
126 if __name__ == '__main__':
127  import sys
128  import optparse
129  parser = optparse.OptionParser(usage=_usage)
130  parser.add_option('--exclude', action='append', metavar='DIRECTORY',
131  default=[],
132  help='Exclude following directory from merge; can be repeated for multiple directories')
133  options, args = parser.parse_args()
134 
135  if len(args) != 3:
136  parser.print_usage()
137  merge_han_configs(sys.argv[1], sys.argv[2], sys.argv[3], options)
python.Helpers.release_metadata
def release_metadata()
Definition: Tools/PyUtils/python/Helpers.py:143
MergeConfigs.list_directories
def list_directories(parent_dir, recurse=True)
Definition: MergeConfigs.py:11
MergeConfigs._is_ok
def _is_ok(dir, options)
Definition: MergeConfigs.py:41
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:232
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
MergeConfigs.merge_han_configs
def merge_han_configs(template, parent_dir, out, options)
Definition: MergeConfigs.py:44
Trk::open
@ open
Definition: BinningType.h:40
Trk::split
@ split
Definition: LayerMaterialProperties.h:38