3"""Check Geant4's module dependency graph for cycles
5Geant4 organises source code into "Modules", with each Module being a
6directory containing headers and sources in `include/` and `src/`
7subdirectories respectively. One or more Modules are grouped/compiled
8into the actual libraries.
10CMake will raise errors if there are circular dependencies between
11shared libraries (cycles are allowed between static libraries). However,
12CMake cannot detect cycles between Modules directly, are these are a
13Geant4 construct. Whilst cycles between modules that get added to the
14same library are technically o.k., they still indicate either:
16- A bad design/organisation of code
17- A mistake in the declared dependencies for a module(s)
19To help developers and CI pick module cycles, Geant4's CMake scripts
20write out the declared module dependencies in a text file as an Adjacency
21List graph. This comprises one line per module, with the first column
22being the module name, and any remaining columns being the modules the
23first one "depends on", i.e. uses code from.
25This program reads that file and uses Python's graphlib module to try
26topologically sorting this graph. Failure to sort indicates a cycle, and
27this will be printed to stdout and a non-zero return code emitted on exit.
34if __name__ ==
"__main__":
36 parser = argparse.ArgumentParser(
37 description=str(__doc__), formatter_class=argparse.RawDescriptionHelpFormatter
44 help=
"file to read adjacency list from",
50 help=
"print topologically sorted list of modules",
52 args = parser.parse_args()
57 with open(args.file)
as f:
59 if not line.strip().startswith(
"#"):
60 nodes = line.rstrip(
"\n").
split(
" ")
61 adjList[nodes[0]] = nodes[1:]
64 print(f
"warning: graph read from '{args.file}' is empty")
71 ts = graphlib.TopologicalSorter(adjList)
79 nodes = ts.get_ready()
84 print(f
"pass: No cycles detected in graph defined in '{args.file}'")
86 except OSError
as err:
87 print(f
"OS error: {err}")
89 except graphlib.CycleError
as err:
91 f
"error: cycles detected in input graph from '{args.file}':",
94 cycle =
" <- ".join(err.args[1])
95 print(f
"cycle: {cycle}", file=sys.stderr)
void print(G4double elem)