From 5160ac659f98322148cd7f1026d33f3baec41f53 Mon Sep 17 00:00:00 2001 From: Wolfgang Bangerth Date: Fri, 18 Apr 2025 21:03:07 -0600 Subject: [PATCH] Add a script that traces which other files a .cc or .h file depends on. --- contrib/utilities/find_header_dependencies.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 contrib/utilities/find_header_dependencies.py diff --git a/contrib/utilities/find_header_dependencies.py b/contrib/utilities/find_header_dependencies.py new file mode 100644 index 0000000000..bfe5cb2813 --- /dev/null +++ b/contrib/utilities/find_header_dependencies.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 + +## ------------------------------------------------------------------------ +## +## SPDX-License-Identifier: LGPL-2.1-or-later +## Copyright (C) 2025 by the deal.II authors +## +## This file is part of the deal.II library. +## +## Part of the source code is dual licensed under Apache-2.0 WITH +## LLVM-exception OR LGPL-2.1-or-later. Detailed license information +## governing the source code and code contributions can be found in +## LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II. +## +## ------------------------------------------------------------------------ + +# Given the name of a source or header file, find all deal.II header +# files it depends on directly or indirectly. +# +# Call this script via +# python3 contrib/utilities/find_header_dependencies.py +# where '' is the name of the source file to be analyzed. + + +from glob import glob +import networkx as nx +import re +import sys + +source_file = sys.argv[1] + +match_dealii_includes = re.compile(r"# *include *<(deal.II/.*)>") + + +# For a given header or source file, read through all the lines and +# extract the ones that correspond to further #include statements. For +# those, add a link to the graph. +def add_includes_for_file(file, G): + f = open(file) + lines = f.readlines() + f.close() + + for line in lines: + m = match_dealii_includes.match(line) + if m: + included_file = m.group(1) + G.add_edge(file, "include/" + included_file) + + +# Create a list of all source files in the build folder +filelist = glob("include/deal.II/**/*.h", recursive=True) +assert filelist, "Please call the script from the top-level deal.II directory." +if source_file not in filelist: + filelist.append(source_file) + + +# For each header file plus the one provided on the command line, add +# the includes as the edges of a directed graph. +G = nx.DiGraph() +G.add_node(source_file) # Make sure the graph has at least one node +for file in filelist: + add_includes_for_file(file, G) + +# Now find everything that is upstream of the module partition +# specified on the command line: +print(f"The dependencies of module partition '{source_file}' are:") +for node in nx.dfs_postorder_nodes(G, source=source_file): + if node != source_file: + print(f" {node}") + +n_dependencies = sum(1 for _ in nx.dfs_postorder_nodes(G, source=source_file)) - 1 +print(f"{n_dependencies} files are upstream dependencies for '{source_file}'") -- 2.39.5