Import Geant4 11.1.0 source tree

This commit is contained in:
Gabriele Cosmo
2022-12-09 14:43:28 +01:00
parent c07cea1fe0
commit 9f34590941
3810 changed files with 200490 additions and 182326 deletions
+204 -80
View File
@@ -24,38 +24,95 @@ import sys
import re
import json
import graphlib
from collections import defaultdict, OrderedDict
def initdb(filename):
db = {}
db = {'modules': {}, 'build_settings': set()}
with open(filename, 'r') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
db[row[0]] = {'location': row[1],
'headers': set(row[2].split(';')) - set(['']),
'public_deps': set(row[3].split(';')) - set(['']),
'private_deps': set(row[4].split(';')) - set(['']),
'interface_deps': set(row[5].split(';')) - set([''])}
db['modules'][row[0]] = {'location': row[1],
'headers': set(row[2].split(';')) - set(['']),
'public_deps': set(row[3].split(';')) - set(['']),
'private_deps': set(row[4].split(';')) - set(['']),
'interface_deps': set(row[5].split(';')) - set(['']),
'parent_target': row[6]}
db['build_settings'].add(row[7])
return db
def scan_includes(input_file):
def get_modules(db):
return db['modules'].keys()
def get_module(name, db):
return db['modules'][name]
def get_location(name, db):
return get_module(name, db)['location']
def get_headers(name, db):
return get_module(name, db)['headers']
def get_public_deps(name, db):
return get_module(name, db)['public_deps']
def get_private_deps(name, db):
return get_module(name, db)['private_deps']
def get_interface_deps(name, db):
return get_module(name, db)['interface_deps']
def get_parent_target(name, db):
return get_module(name, db)['parent_target']
def what_provides(h, db):
return set([m for m in get_modules(db) if h in get_headers(m, db)])
def has_setting(setting, build_settings):
if setting.startswith('!'):
return setting[1:] not in build_settings
else:
return setting in build_settings
def scan_includes(input_file, settings):
result = set()
lines = open(input_file).readlines()
for line in lines:
m = re.search("^ *# *include *\"(.*)\"", line)
m = re.search("^ *# *include *\"(.*)\"(.*)$", line)
if m:
c = re.search(
"\s*//\s*no_geant4_module_check\s*(\((.*)\))?.*$", m.group(2))
if c:
conds = set()
if c.group(2):
conds = set(
filter(None, [''.join(c.split(' ')) for c in c.group(2).strip().split(',')]))
if not conds or any(has_setting(s, settings) for s in conds):
continue
result.add(m.group(1))
return result
def scan_files(input_path):
def scan_files(input_path, settings):
used_headers = set()
with os.scandir(input_path) as hdr_it:
for entry in hdr_it:
used_headers |= scan_includes(entry.path)
used_headers |= scan_includes(entry.path, settings)
return used_headers
@@ -64,8 +121,8 @@ def find_modules(list_of_headers, module_db):
found_modules = set()
orphan_hdrs = set()
for h in list_of_headers:
ms = [k for k, v in module_db.items() if h in v['headers']]
found_modules |= set(ms)
ms = what_provides(h, module_db)
found_modules |= ms
if not ms:
orphan_hdrs.add(h)
@@ -75,21 +132,28 @@ def find_modules(list_of_headers, module_db):
def usage_requirements(module_name, module_db):
""" Find modules needed publically and privately by input module
"""
module_path = module_db[module_name]['location']
module_headers = module_db[module_name]['headers']
try:
module_path = get_location(module_name, module_db)
module_headers = get_headers(module_name, module_db)
except KeyError as err:
print(f'No module named \'{module_name}\'', file=sys.stderr)
sys.exit(1)
# Determine public usage reqs
includes_from_headers = scan_files(os.path.join(module_path, "include"))
includes_from_headers = scan_files(os.path.join(module_path, "include"), module_db['build_settings'])
includes_from_headers -= module_headers
public_deps = find_modules(includes_from_headers, module_db)
# The same for private
includes_from_srcs = scan_files(os.path.join(module_path, "src"))
includes_from_srcs -= module_headers
private_deps = find_modules(includes_from_srcs, module_db)
# Public deps are higher priority
private_deps['modules'] -= public_deps['modules']
private_deps['headers'] -= public_deps['headers']
# The same for private, if module has srcs
try:
includes_from_srcs = scan_files(os.path.join(module_path, "src"), module_db['build_settings'])
includes_from_srcs -= module_headers
private_deps = find_modules(includes_from_srcs, module_db)
# Public deps are higher priority
private_deps['modules'] -= public_deps['modules']
private_deps['headers'] -= public_deps['headers']
except FileNotFoundError:
private_deps = {'modules': set(), 'headers': set()}
# Transform results to output dict
d = {'module': module_name,
@@ -108,18 +172,22 @@ def usage_requirements(module_name, module_db):
def check_consistency(module_name, module_db):
""" Check module declared/apparent dependencies for consistency
"""
# NB: Can have false positives from externals G4expat, G4clhep, G4zlib, G4tools (plus imported :: targets)
def filter_dependencies(dep_list):
return set([x for x in dep_list if not re.match("^.+::.+", x)]) - set(['G4expat', 'G4clhep', 'G4zlib', 'G4tools', 'G4ptl'])
# NB: Can have false positives from externals G4expat, G4clhep, G4zlib, G4tools (plus imported :: targets and stdlib)
def filter_dependencies(dep_list, module_name):
return set([x for x in dep_list if not re.match("^.+::.+", x)]) - set(['G4expat', 'G4clhep', 'G4zlib', 'G4tools', 'G4ptl', 'stdc++fs'])
ur = usage_requirements(module_name, module_db)
apparent_public_deps = filter_dependencies(ur['dependencies']['public'])
apparent_private_deps = filter_dependencies(ur['dependencies']['private'])
apparent_public_deps = filter_dependencies(
ur['dependencies']['public'], module_name)
apparent_private_deps = filter_dependencies(
ur['dependencies']['private'], module_name)
declared_public_deps = filter_dependencies(
module_db[module_name]['public_deps'])
get_public_deps(module_name, module_db), module_name)
declared_interface_deps = filter_dependencies(
get_interface_deps(module_name, module_db), module_name)
declared_private_deps = filter_dependencies(
module_db[module_name]['private_deps'])
get_private_deps(module_name, module_db), module_name)
# Collate any consistency errors
report = []
@@ -128,41 +196,72 @@ def check_consistency(module_name, module_db):
duplicated_deps = declared_public_deps & declared_private_deps
if duplicated_deps:
report.append(
f'- has duplicated PUBLIC/PRIVATE dependencies: {duplicated_deps}')
f' - has duplicated PUBLIC/PRIVATE dependencies: {duplicated_deps}')
# - Apparent dep not declared
missing_public_deps = apparent_public_deps - declared_public_deps
missing_public_deps = apparent_public_deps - \
declared_public_deps - declared_interface_deps
if missing_public_deps:
report.append(
f'- may require PUBLIC dependencies: {missing_public_deps}')
f' + may require PUBLIC or INTERFACE dependencies: {missing_public_deps}')
missing_private_deps = apparent_private_deps - declared_private_deps
if missing_private_deps:
report.append(
f'- may require PRIVATE dependencies: {missing_private_deps}')
f' + may require PRIVATE dependencies: {missing_private_deps}')
# - Declared dep not in apparent (overdeclared)
overdeclared_public_deps = declared_public_deps - apparent_public_deps
if overdeclared_public_deps:
report.append(
f'- may not require PUBLIC dependencies: {overdeclared_public_deps}')
f' - may not require PUBLIC dependencies: {overdeclared_public_deps}')
overdeclared_private_deps = declared_private_deps - apparent_private_deps
if overdeclared_private_deps:
report.append(
f'- may not require PRIVATE dependencies: {overdeclared_private_deps}')
f' - may not require PRIVATE dependencies: {overdeclared_private_deps}')
return report
def find_cycles(module_db, verbose):
def do_provides(header_name, module_db, verbose):
# find the header, there should not be duplicates!
mods = what_provides(header_name, module_db)
if len(mods) == 0:
print(f"No module provides header '{header_name}'", file=sys.stderr)
sys.exit(1)
if len(mods) > 1:
print(
f"Header '{args.provides}' is provided by multiple modules: {mods}", file=sys.stderr)
sys.exit(1)
print(mods.pop())
def do_check_consistency(module_name, module_db):
try:
cc = check_consistency(module_name, module_db)
if cc:
print(f'{module_name} has inconsistent dependencies:', file=sys.stderr)
print("\n".join(cc), file=sys.stderr)
sys.exit(1)
else:
print(f'Module {module_name} appears consistent')
except KeyError as err:
print(f'No module named \'{module_name}\'', file=sys.stderr)
sys.exit(1)
def do_find_cycles(module_db, verbose):
""" Check for any cycles in the complete module dependency graph
"""
# Build adjacency list
adjlist = {}
for k, v in module_db.items():
adjlist[k] = list(v['public_deps'] | v['private_deps']
| v['interface_deps'])
for m in get_modules(module_db):
adjlist[m] = list(get_public_deps(m, module_db)
| get_private_deps(m, module_db)
| get_interface_deps(m, module_db))
try:
# Topo sort throws cycle error if one occurs during prepare
@@ -182,19 +281,45 @@ def find_cycles(module_db, verbose):
ts.done(*nodes)
generation += 1
print(f"pass: No cycles detected in module dependency graph")
print(f"No cycles detected in module dependency graph")
except graphlib.CycleError as err:
print(
f"error: cycles detected in module dependency graph",
f"Cycles detected in module dependency graph:",
file=sys.stderr,
)
cycle = " <- ".join(err.args[1])
print(f"cycle: {cycle}", file=sys.stderr)
cycle = " -> ".join(reversed(err.args[1]))
print(f"{cycle}", file=sys.stderr)
sys.exit(1)
def do_find_inconsistencies(db, verbose):
inconsistent = {}
for m in get_modules(db):
cc = check_consistency(m, db)
if cc:
inconsistent[m] = "\n".join(cc)
if len(inconsistent) > 0:
for k, v in inconsistent.items():
print(f'{k}:\n{v}', file=sys.stderr)
sys.exit(1)
else:
print(
"No inconsistencies detected in declared/apparent module dependencies")
def do_libraries(db, verbose):
libmap = defaultdict(set)
for m in get_modules(db):
libmap[get_parent_target(m, db)].add(m)
for k, v in sorted(libmap.items()):
print(f'{k}: {v}')
if __name__ == "__main__":
# Parse command line to get command to run
parser = argparse.ArgumentParser(
description=str(__doc__), formatter_class=argparse.RawDescriptionHelpFormatter
)
@@ -210,6 +335,11 @@ if __name__ == "__main__":
action='store_true',
help="list declared source code modules"
)
query_group.add_argument(
"-s", "--source",
metavar="<module>",
help="print directory holding CMake file where module is defined"
)
query_group.add_argument(
"-i", "--interface",
metavar="<module>",
@@ -240,6 +370,16 @@ if __name__ == "__main__":
action="store_true",
help="find inconsistencies in apparent/declared dependencies of all modules"
)
query_group.add_argument(
"--library",
metavar="<module>",
help="print final library (.so/.dll) that module is compiled into"
)
query_group.add_argument(
"--libraries",
action="store_true",
help="print final libraries and their module compositions"
)
args = parser.parse_args()
# Initialize the module/header "database"
@@ -251,52 +391,36 @@ if __name__ == "__main__":
# Implementations
if args.list:
print("\n".join(db.keys()))
print("\n".join(get_modules(db)))
elif args.source:
try:
print(get_location(args.source, db))
except KeyError as err:
print(f"No module named {err}", file=sys.stderr)
sys.exit(1)
elif args.interface:
try:
print("\n".join(db[args.interface]['headers']))
print(
"\n".join(sorted(get_headers(args.interface, db))))
except KeyError as err:
print(f"No module named {err}", file=sys.stderr)
sys.exit(1)
elif args.provides:
# find the header, there should not be duplicates!
mods = [k for k, v in db.items() if args.provides in v['headers']]
if len(mods) == 0:
print(
f"No module provides header '{args.provides}'",
file=sys.stderr)
sys.exit(1)
if len(mods) > 1:
print(
f"Header '{args.provides}' is provided by multiple modules: {mods}",
file=sys.stderr)
sys.exit(1)
print(mods[0])
do_provides(args.provides, db, args.verbose)
elif args.usage_requirements:
ur = usage_requirements(args.usage_requirements, db)
print(json.dumps(ur, indent=2))
elif args.check_consistency:
cc = check_consistency(args.check_consistency, db)
if cc:
print(f'{args.check_consistency}:', file=sys.stderr)
print("\n".join(cc), file=sys.stderr)
sys.exit(1)
else:
print(
f'{args.check_consistency} appears consistent')
do_check_consistency(args.check_consistency, db)
elif args.find_cycles:
find_cycles(db, args.verbose)
do_find_cycles(db, args.verbose)
elif args.find_inconsistencies:
inconsistent = {}
for m in db.keys():
cc = check_consistency(m, db)
if cc:
inconsistent[m] = "\n".join(cc)
if len(inconsistent) > 0:
print(f'Inconsistent dependencies in modules:', file=sys.stderr)
for k, v in inconsistent.items():
print(f'{k}:\n{v}', file=sys.stderr)
do_find_inconsistencies(db, args.verbose)
elif args.library:
try:
print(get_parent_target(args.library, db))
except KeyError as err:
print(f"No module named {err}", file=sys.stderr)
sys.exit(1)
elif args.libraries:
do_libraries(db, args.verbose)