Belle II Software  release-05-02-19
process_dir.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 import os
5 import stat
6 from SCons.Script import *
7 
8 
9 def define_aliases(
10  env,
11  target,
12  dir_name,
13  extension=None,
14 ):
15 
16  parent_dir = dir_name
17  while len(parent_dir) > 0:
18  env.Alias(parent_dir, target)
19  if extension:
20  env.Alias(parent_dir + '.' + extension, target)
21 
22  next_parent_dir = os.path.split(parent_dir)[0]
23  # check split actually does something here
24  if next_parent_dir == parent_dir:
25  break
26  parent_dir = next_parent_dir
27 
28  if extension:
29  env.Alias(extension, target)
30 
31 
32 def get_files(path_name):
33  result = Glob(path_name)
34  files = [f for f in result if not os.path.isdir(str(f))]
35  return files
36 
37 
38 def get_python_files_recursive(topdir_path):
39  python_file_nodes = []
40 
41  for (dir_path, dir_names, file_names) in os.walk(topdir_path):
42  for file_name in file_names:
43  if file_name.endswith('.py'):
44  file_path = os.path.join(dir_path, file_name)
45  file_node = File(file_path)
46  python_file_nodes.append(file_node)
47 
48  return python_file_nodes
49 
50 
51 def process_dir(
52  parent_env,
53  dir_name,
54  is_module_dir
55 ):
56 
57  # remove leading ./
58  if dir_name.startswith('./'):
59  dir_name = dir_name[2:]
60 
61  # determine library name
62  if dir_name == '.':
63  lib_name = parent_env['PACKAGE']
64  else:
65  lib_name = dir_name.replace(os.sep, '_')
66 
67  # get list of header and linkdef files
68  header_files = get_files(os.path.join(dir_name, '*.h'))
69  if dir_name != '.':
70  header_files += get_files(os.path.join(dir_name, 'include', '*.h'))
71  linkdef_files = []
72  for header_file in header_files:
73  if str(header_file).lower().endswith('linkdef.h'):
74  linkdef_files.append(header_file)
75  header_files.remove(header_file)
76 
77  # get list of source files
78  src_nodes = get_files(os.path.join(dir_name, '*.cc')) \
79  + get_files(os.path.join(dir_name, 'src', '*.cc')) \
80  + get_files(os.path.join(dir_name, '*.c')) \
81  + get_files(os.path.join(dir_name, 'src', '*.c')) \
82  + get_files(os.path.join(dir_name, '*.f')) \
83  + get_files(os.path.join(dir_name, 'src', '*.f')) \
84  + get_files(os.path.join(dir_name, '*.F')) \
85  + get_files(os.path.join(dir_name, 'src', '*.F'))
86  src_files = [os.path.join(parent_env['BUILDDIR'], str(node)) for node in
87  src_nodes]
88 
89  # get list of test files
90  test_files = [os.path.join(parent_env['BUILDDIR'], str(node)) for node in
91  get_files(os.path.join(dir_name, 'tests', '*.cc'))]
92 
93  # get list of script files
94  script_files = get_python_files_recursive(os.path.join(dir_name, 'scripts'
95  ))
96 
97  # get list of executable script files
98  executable_files = []
99  executable_mode = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
100  for tools_file in get_files(os.path.join(dir_name, 'tools', '*')):
101  executable_file = str(tools_file)
102  if os.stat(executable_file).st_mode & executable_mode \
103  == executable_mode:
104  executable_files.append(tools_file)
105 
106  # get list of data files
107  data_files = get_files(os.path.join(dir_name, 'data', '*'))
108 
109  # create environment for directory
110  env = parent_env.Clone()
111  env['HEADER_FILES'] = header_files
112  env['LINKDEF_FILES'] = linkdef_files
113  env['SRC_FILES'] = src_files
114  env['TEST_FILES'] = test_files
115  env['TEST_LIBS'] = []
116  env['SCRIPT_FILES'] = script_files
117  env['EXECUTABLE_FILES'] = executable_files
118  env['DATA_FILES'] = data_files
119  env['DATAOBJECT_LIB'] = []
120  env['DATAOBJECT_LIBS'] = []
121 
122  # clean up some environment variables that should not be inherited from the parent environment
123  if 'SUBLIB' in env.Dictionary():
124  del env.Dictionary()['SUBLIB']
125  if 'PYTHON_MODULE' in env.Dictionary():
126  del env.Dictionary()['PYTHON_MODULE']
127  if 'LIBS' in env.Dictionary():
128  del env.Dictionary()['LIBS']
129 
130  if dir_name in env.get('DISABLE_COMPILER_WARNINGS', []):
131  env.AppendUnique(CXXFLAGS=['-w'], CCFLAGS=['-w'], FORTRANFLAGS=['-w'], LINKFLAGS=['-w'])
132 
133  # link dataobjects to analysis modules
134  if dir_name == '.':
135  env.Append(LIBS=['dataobjects'])
136 
137  # include SConscript file if it exists
138  sconscript_name = os.path.join(dir_name, 'SConscript')
139  if os.path.isfile(sconscript_name):
140  result = SConscript(sconscript_name, exports='env')
141 
142  # use the new environment if it was updated by the SConscript file
143  if isinstance(result, Environment):
144  env = result
145 
146  # don't continue with the default build process if the SConscript file requests this
147  if not env.Dictionary().get('CONTINUE', True):
148  return
149 
150  # Add additional sources.
151  if 'ADDITIONAL_SOURCES' in env.Dictionary():
152  additional_src_nodes = []
153  for source in env.Dictionary()['ADDITIONAL_SOURCES']:
154  additional_src_nodes += get_files(os.path.join(dir_name, source))
155  additional_src_files = [
156  os.path.join(parent_env['BUILDDIR'], str(node)) for node in
157  additional_src_nodes]
158  if (len(additional_src_files) > 0):
159  src_files.append(additional_src_files)
160  env['SRC_FILES'] = src_files
161 
162  # install header files in the include directory
163  includes = env.Install(os.path.join(env['INCDIR'], dir_name),
164  env['HEADER_FILES'])
165  define_aliases(env, includes, dir_name, 'include')
166 
167  # install script files in the library directory
168  script_targets = []
169  for script_file_node in env['SCRIPT_FILES']:
170  script_file_path = str(script_file_node)
171  script_dir = os.path.dirname(script_file_path)
172 
173  destination_reldir = os.path.relpath(script_dir,
174  os.path.join(dir_name, 'scripts'))
175 
176  if not destination_reldir:
177  continue
178  destination_dir = os.path.join(env['LIBDIR'], destination_reldir)
179  script_target = env.Install(destination_dir, script_file_node)
180  script_targets.append(script_target)
181 
182  define_aliases(env, script_targets, dir_name, 'scripts')
183 
184  # install executable script files in the bin directory
185  executables = env.Install(env['BINDIR'], env['EXECUTABLE_FILES'])
186  define_aliases(env, executables, dir_name, 'tools')
187 
188  # install data files in the data directory
189  data = env.Install(os.path.join(env['DATADIR'], dir_name), env['DATA_FILES'
190  ])
191  define_aliases(env, data, dir_name, 'data')
192 
193  # remember tests defined in this directory
194  local_test_files = env['TEST_FILES']
195 
196  # loop over subdirs
197  entries = os.listdir(dir_name)
198  exclude_dirs = set()
199  if (os.path.exists(os.path.join(dir_name, '.excluded_directories'))):
200  f = open('.excluded_directories', 'r')
201  for line in f.readlines():
202  exclude_dirs.add(line.rstrip('\n'))
203  f.close()
204  print(f'Excluded directories: {exclude_dirs}')
205  for entry in entries:
206  if entry in exclude_dirs:
207  continue
208  if entry.find('.') == -1 \
209  and not os.path.isfile(os.path.join(dir_name, entry)) and entry not in [
210  'include',
211  'src',
212  'tools',
213  'tests',
214  'scripts',
215  'data',
216  'doc',
217  'examples',
218  'modules',
219  ]:
220  if dir_name == '.' and entry in [
221  'build',
222  'include',
223  'lib',
224  'bin',
225  'modules',
226  'data',
227  'site_scons',
228  ]:
229  continue
230  process_dir(env, os.path.join(dir_name, entry), is_module_dir and dir_name != '.')
231 
232  # determine whether we are in a special directory
233  is_package_dir = dir_name == env['PACKAGE']
234  is_sublib_dir = env.Dictionary().get('SUBLIB', False)
235  is_python_module_dir = env.Dictionary().get('PYTHON_MODULE', False)
236  is_dataobjects_dir = os.path.basename(dir_name) == 'dataobjects' \
237  and env['PACKAGE'] != 'framework'
238  if dir_name == 'dataobjects':
239  is_dataobjects_dir = True
240  is_module_dir = False
241  lib_name = parent_env['PACKAGE']
242 
243  # check whether we have to create a new library
244  if is_package_dir or is_sublib_dir or is_python_module_dir \
245  or is_module_dir or is_dataobjects_dir:
246 
247  # generate dictionaries
248  dict_files = []
249  aux_dict_targets = []
250  for linkdef_file in env['LINKDEF_FILES']:
251  # set the name of library generated at this stage
252  # will be read by the RootDict builder
253  dict_filename = str(linkdef_file).replace(os.sep, '_')[:-9] + 'Dict.cc'
254  dict_file, rootmap_file, rootpcm_file = env.RootDict(os.path.join(env['BUILDDIR'], dict_filename), linkdef_file,
255  ROOTCLING_ROOTMAP_LIB=lib_name)
256  # add the extra cxxflags
257  dict_ccflags = env["CCFLAGS"] + env["ROOTCLING_EXTRA_CCFLAGS"]
258  # add current directory to include path for dictionary compilation
259  dict_files.append(env.SharedObject(dict_file, CPPPATH=['.'] + env['CPPPATH'], CCFLAGS=dict_ccflags))
260 
261  # install corresponding pcm file in lib (for cling)
262  aux_dict_targets.append(env.Copy(os.path.join(env['LIBDIR'], rootpcm_file.name), rootpcm_file))
263  # install corresponding rootmap files to support auto-loading of libraries
264  # once used via ROOT
265  aux_dict_targets.append(env.Copy(os.path.join(env['LIBDIR'], rootmap_file.name), rootmap_file))
266 
267  # build a shared library with all source and dictionary files
268  if len(env['SRC_FILES']) > 0 or len(dict_files) > 0:
269 
270  # determine path of library and adjust path and name for modules
271  lib_dir_name = env['LIBDIR']
272  if is_module_dir:
273  lib_dir_name = env['MODDIR']
274  if os.path.basename(dir_name) != 'modules' and dir_name != '.':
275  lib_name = os.path.basename(dir_name)
276 
277  # update list of dataobject libraries
278  if is_dataobjects_dir:
279  parent_env['DATAOBJECT_LIB'] = lib_name
280 
281  # create library and map for modules
282  lib = env.SharedLibrary(os.path.join(lib_dir_name, lib_name),
283  [env['SRC_FILES'], dict_files])
284  debug = env.StripDebug(lib)
285 
286  lib_files = [lib, debug] + aux_dict_targets
287  if is_module_dir:
288  map_file = os.path.join(lib_dir_name, env.subst('$SHLIBPREFIX') + lib_name + '.b2modmap')
289  # Adding lib_files is important to ensure we load local module
290  # libraries if they are newer than those in central directory
291  map_sources = env['SRC_FILES'] + lib_files
292 
293  reg_map = env.RegMap(map_file, map_sources)
294  lib_files.append(reg_map)
295 
296  if env['MODULE_IO'] and env.get('HAS_DOT', False):
297  module_io = env.ModuleIo(reg_map)
298  env.Depends(module_io, [lib, reg_map])
299  env.Requires(module_io, [os.path.join('$BINDIR', 'basf2')])
300  env.Alias('module-io', module_io)
301 
302  # define build target aliases
303  env.Alias(lib_name, lib_files)
304  if is_module_dir:
305  define_aliases(env, lib_files, dir_name, 'modules')
306  else:
307  define_aliases(env, lib_files, dir_name, 'lib')
308 
309  # install python module libraries with a file name that is recognized by python
310  if is_python_module_dir:
311  pymod = env.InstallAs(os.path.join(env['LIBDIR'], os.path.basename(dir_name) + env.subst('$SHLIBSUFFIX')), lib)
312  define_aliases(env, pymod, dir_name, 'lib')
313  else:
314 
315  # add linkdef, and source files to parent environment if we are in a normal sub-directory
316  parent_env['LINKDEF_FILES'] += env['LINKDEF_FILES']
317  parent_env['SRC_FILES'] += env['SRC_FILES']
318 
319  # add dataobject libs to parent environment
320  if 'DATAOBJECT_LIB' in env.Dictionary():
321  parent_env.Append(DATAOBJECT_LIBS=env['DATAOBJECT_LIB'])
322  if 'DATAOBJECT_LIBS' in env.Dictionary():
323  parent_env.AppendUnique(DATAOBJECT_LIBS=env['DATAOBJECT_LIBS'])
324 
325  # process modules directory last so that it is known whether the main library exists
326  if os.path.isdir(os.path.join(dir_name, 'modules')):
327  process_dir(env, os.path.join(dir_name, 'modules'), True)
328 
329  # setup environment for building executables, include SConscript if it exists
330  save_env = env.Clone()
331  env['TOOLS_FILES'] = get_files(os.path.join(dir_name, 'tools', '*.cc'))
332  sconscript_name = os.path.join(dir_name, 'tools', 'SConscript')
333  if os.path.isfile(sconscript_name):
334  result = SConscript(sconscript_name, exports='env')
335  if isinstance(result, Environment):
336  env = result
337 
338  # build a binary for each source file in the tools directory
339  for bin_file in env['TOOLS_FILES']:
340  bin_filename = os.path.splitext(os.path.basename(str(bin_file)))[0]
341  bin_env = env.Clone()
342  bin_env['LIBS'] = []
343  if bin_filename in bin_env['TOOLS_LIBS']:
344  bin_env['LIBS'] = Flatten([env.subst(str(lib)).split() for lib in
345  Flatten(bin_env['TOOLS_LIBS'
346  ][bin_filename])])
347  if bin_filename in bin_env['TOOLS_LIBPATH']:
348  bin_env['LIBPATH'] = bin_env['TOOLS_LIBPATH'][bin_filename]
349  tool = bin_env.Program(os.path.join(bin_env['BINDIR'], bin_filename),
350  os.path.join(bin_env['BUILDDIR'],
351  str(bin_file)))
352  debug = bin_env.StripDebug(tool)
353  env.Alias(os.path.join(dir_name, 'tools', bin_filename), [tool, debug])
354  env.Alias(os.path.join(dir_name, 'tools'), [tool, debug])
355  env.Alias(os.path.join(dir_name, bin_filename), [tool, debug])
356  define_aliases(env, [tool, debug], dir_name, 'bin')
357 
358  # restore original environment
359  env = save_env
360 
361  # build shared objects from the tests/*.cc files in this directory
362  if len(local_test_files) > 0:
363  local_test_env = env.Clone()
364  sconscript_name = os.path.join(dir_name, 'tests', 'SConscript')
365  if os.path.isfile(sconscript_name):
366  result = SConscript(sconscript_name, exports='env')
367  if isinstance(result, Environment):
368  local_test_env = result
369  local_test_env.PrependUnique(LIBS=['test_main'])
370  local_test_env.AppendUnique(LIBS=['mdst_dbobjects', 'framework', '$ROOT_LIBS', 'gtest', 'pthread'])
371  env['TEST_FILES'] = [test_file for test_file in env['TEST_FILES']
372  if test_file not in local_test_files]
373  env.Prepend(TEST_FILES=local_test_env.SharedObject(local_test_files))
374  env.AppendUnique(TEST_LIBS=local_test_env['LIBS'])
375 
376  # combine all tests from subdirectories to a new test executable
377  if len(env['TEST_FILES']) > 0:
378  test_filename = lib_name + '-unittests'
379  test_env = env.Clone()
380  test_env['LIBS'] = env['TEST_LIBS']
381  test = test_env.Program(os.path.join(test_env['BINDIR'],
382  test_filename), env['TEST_FILES'])
383  env.StripDebug(test)
384  env.Alias(os.path.join(dir_name, 'tests', test_filename), test)
385  env.Alias(os.path.join(dir_name, 'tests'), test)
386  env.Alias(os.path.join(dir_name, test_filename), test)
387  define_aliases(env, test, dir_name, 'tests')
388 
389  # add test files and libs to parent environment
390  parent_env.AppendUnique(TEST_LIBS=env['TEST_LIBS'])
391  parent_env.Append(TEST_FILES=env['TEST_FILES'])
392 
393 
394 def generate(env):
395  env.AddMethod(process_dir, 'ProcessDirectory')
396 
397 
398 def exists(env):
399  return True
process_dir
Definition: process_dir.py:1
Script