-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f488914
commit 4c2b683
Showing
18 changed files
with
915 additions
and
409 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
aiofiles==22.1.0 | ||
aiosqlite==0.19.0 | ||
anyio==3.6.2 | ||
appnope==0.1.3 | ||
argon2-cffi==21.3.0 | ||
argon2-cffi-bindings==21.2.0 | ||
arrow==1.2.3 | ||
asttokens==2.2.1 | ||
attrs==23.1.0 | ||
Babel==2.12.1 | ||
backcall==0.2.0 | ||
beautifulsoup4==4.12.2 | ||
bleach==6.0.0 | ||
blinker==1.4 | ||
certifi==2022.12.7 | ||
cffi==1.15.1 | ||
charset-normalizer==3.1.0 | ||
cmake==3.24.1.1 | ||
cmake-generators==1.0.9 | ||
colorama==0.4.4 | ||
comm==0.1.3 | ||
commonmark==0.9.1 | ||
cycler==0.11.0 | ||
debugpy==1.6.7 | ||
decorator==5.1.1 | ||
defusedxml==0.7.1 | ||
distro==1.7.0 | ||
docutils==0.18.1 | ||
executing==1.2.0 | ||
fastjsonschema==2.16.3 | ||
feedgenerator==2.0.0 | ||
fonttools==4.29.1 | ||
fqdn==1.5.1 | ||
future-fstrings==1.2.0 | ||
gcovr==5.2 | ||
gitdb==4.0.9 | ||
GitPython==3.1.27 | ||
h5py==3.8.0 | ||
idna==3.4 | ||
importlib-metadata==6.6.0 | ||
importlib-resources==5.12.0 | ||
ipykernel==6.22.0 | ||
ipython==8.12.1 | ||
ipython-genutils==0.2.0 | ||
isoduration==20.11.0 | ||
jedi==0.18.2 | ||
Jinja2==3.0.3 | ||
joblib==1.3.1 | ||
json5==0.9.11 | ||
jsonpointer==2.3 | ||
jsonschema==4.17.3 | ||
jupyter-events==0.6.3 | ||
jupyter-ydoc==0.2.4 | ||
jupyter_client==8.2.0 | ||
jupyter_core==5.3.0 | ||
jupyter_server==2.5.0 | ||
jupyter_server_fileid==0.9.0 | ||
jupyter_server_terminals==0.4.4 | ||
jupyter_server_ydoc==0.8.0 | ||
jupyterlab==3.6.3 | ||
jupyterlab-pygments==0.2.2 | ||
jupyterlab_server==2.22.1 | ||
kiwisolver==1.3.2 | ||
lxml==4.9.2 | ||
MarkupSafe==2.0.1 | ||
matplotlib==3.5.1 | ||
matplotlib-inline==0.1.6 | ||
mistune==2.0.5 | ||
mpi4py==3.1.4 | ||
nbclassic==0.5.6 | ||
nbclient==0.7.4 | ||
nbconvert==7.3.1 | ||
nbformat==5.8.0 | ||
nest-asyncio==1.5.6 | ||
networkx==3.1 | ||
ninja==1.10.2.4 | ||
nose==1.3.7 | ||
notebook==6.5.4 | ||
notebook_shim==0.2.3 | ||
numpy==1.22.2 | ||
packaging==21.3 | ||
pandas==1.4.1 | ||
pandocfilters==1.5.0 | ||
parso==0.8.3 | ||
pelican==4.7.2 | ||
pexpect==4.8.0 | ||
pickleshare==0.7.5 | ||
Pillow==9.0.1 | ||
pkgutil_resolve_name==1.3.10 | ||
platformdirs==3.5.0 | ||
prometheus-client==0.16.0 | ||
prompt-toolkit==3.0.38 | ||
psutil==5.9.5 | ||
ptyprocess==0.7.0 | ||
pure-eval==0.2.2 | ||
pybind11==2.10.0 | ||
pycparser==2.21 | ||
Pygments==2.11.2 | ||
pyparsing==3.0.7 | ||
pyphen==0.13.2 | ||
pyrsistent==0.19.3 | ||
python-dateutil==2.8.2 | ||
python-json-logger==2.0.7 | ||
pytz==2021.3 | ||
PyYAML==6.0 | ||
pyzmq==25.0.2 | ||
requests==2.29.0 | ||
rfc3339-validator==0.1.4 | ||
rfc3986-validator==0.1.1 | ||
rich==11.2.0 | ||
rodin @ file:///Users/carlos/Projects/rodin | ||
scikit-build==0.15.0 | ||
scikit-learn==1.3.0 | ||
scipy==1.9.0 | ||
seaborn==0.12.2 | ||
Send2Trash==1.8.2 | ||
six==1.16.0 | ||
sklearn==0.0.post7 | ||
smmap==5.0.0 | ||
sniffio==1.3.0 | ||
soupsieve==2.4.1 | ||
stack-data==0.6.2 | ||
svn==1.0.1 | ||
termcolor==2.3.0 | ||
terminado==0.17.1 | ||
threadpoolctl==3.2.0 | ||
tinycss2==1.2.1 | ||
tomli==2.0.1 | ||
tornado==6.3.1 | ||
traitlets==5.9.0 | ||
typing_extensions==4.5.0 | ||
Unidecode==1.3.2 | ||
uri-template==1.2.0 | ||
urllib3==1.26.15 | ||
wcwidth==0.2.6 | ||
webcolors==1.13 | ||
webencodings==0.5.1 | ||
websocket-client==1.5.1 | ||
y-py==0.5.9 | ||
ypy-websocket==0.8.2 | ||
zipp==3.15.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
import os | ||
import re | ||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
import argparse | ||
import networkx as nx | ||
from pathlib import Path | ||
from shutil import which | ||
from termcolor import colored | ||
from networkx.drawing.nx_agraph import write_dot | ||
|
||
PT_TO_INCH = (1.0 / 72) | ||
|
||
MIN_NODE_SIZE = 2.0 | ||
MAX_NODE_SIZE = 7.0 | ||
|
||
MIN_FONT_SIZE = 6 | ||
MAX_FONT_SIZE = 42 | ||
|
||
|
||
COMMON_CPP_EXTENSIONS = ['.h', '.hpp', '.hxx'] | ||
|
||
COLORSCHEME = 'spectral9' | ||
NUMBER_OF_COLORS = 9 | ||
|
||
RODIN_TOP_LEVEL_INCLUDES = { | ||
'Rodin.h', | ||
'Rodin/Alert.h', | ||
'Rodin/Variational.h', | ||
'Rodin/Geometry.h', | ||
'RodinExternal.h', | ||
'RodinExternal/MMG.h', | ||
} | ||
|
||
RODIN_TOP_LEVEL_INCLUDES_COLOR_MAP = { | ||
'Alert.h': 'red', | ||
'Variational.h': 'green', | ||
'Geometry.h': 'blue' | ||
} | ||
|
||
|
||
class GraphvizNotFoundError(Exception): | ||
pass | ||
|
||
|
||
def merge_matching_paths(path1, path2): | ||
parts1 = Path(path1).parts | ||
parts2 = Path(path2).parts | ||
if parts2[:len(parts1)] == parts1: | ||
merged_parts = parts1 + parts2[len(parts1):] | ||
merged_path = Path(*merged_parts) | ||
return str(merged_path) | ||
else: | ||
joined_path = Path(path1) / path2 | ||
return str(joined_path) | ||
|
||
|
||
def extract_includes(file_path, context_path): | ||
includes = [] | ||
with open(file_path, 'r') as file: | ||
for line in file: | ||
# system_include_match = re.match(r'#include\s*[<](.*)[>]', line) | ||
user_include_match = re.match(r'#include\s*["](.*)["]', line) | ||
if user_include_match: | ||
include = user_include_match.group(1) | ||
if include == 'ForwardDecls.h': | ||
continue | ||
include_path = os.path.dirname(include) | ||
if include_path: | ||
if not include_path.startswith('Rodin'): | ||
include = merge_matching_paths(context_path, include) | ||
else: | ||
include = os.path.join(context_path, include) | ||
includes.append(include) | ||
return includes | ||
|
||
|
||
def generate_dependency_graph(root_path, extensions): | ||
G = nx.DiGraph() | ||
for root, _, files in os.walk(root_path): | ||
for file in files: | ||
if any(file.endswith(ext) for ext in extensions): | ||
source_path = os.path.join(root, file) | ||
relative_path = os.path.relpath(source_path, root_path) | ||
context_path = os.path.dirname(relative_path) | ||
includes = extract_includes(source_path, context_path) | ||
G.add_node(relative_path) | ||
for include in includes: | ||
G.add_edge(relative_path, include) | ||
return G | ||
|
||
def get_predecessors_map(graph): | ||
sinks = set() | ||
node_to_set = {} | ||
for node in graph.nodes(): | ||
node_to_set[node] = set() | ||
if graph.out_degree(node) == 0: | ||
sinks.add(node) | ||
for sink in sinks: | ||
visited = set() | ||
q = { sink } | ||
while len(q) > 0: | ||
node = q.pop() | ||
visited.add(node) | ||
for n in graph.predecessors(node): | ||
node_to_set[n].add(node) | ||
node_to_set[n] |= node_to_set[node] | ||
if n not in visited: | ||
q.add(n) | ||
return node_to_set | ||
|
||
def get_accumulated_map(graph): | ||
predecessors_map = get_predecessors_map(graph) | ||
node_to_accumulated = {} | ||
for node, predecessors in predecessors_map.items(): | ||
node_to_accumulated[node] = len(predecessors) | ||
return node_to_accumulated | ||
|
||
def get_flow_map(graph): | ||
node_to_flow = {} | ||
for node in graph.nodes(): | ||
out_degree = graph.out_degree(node) | ||
in_degree = graph.in_degree(node) | ||
node_to_flow[node] = in_degree - out_degree | ||
return node_to_flow | ||
|
||
def get_laplacian_map(graph): | ||
laplacian_matrix = nx.directed_laplacian_matrix(graph) | ||
node_to_laplacian = {} | ||
for node_idx, node in enumerate(graph.nodes()): | ||
laplacian_value = laplacian_matrix[node_idx, node_idx] | ||
node_to_laplacian[node] = laplacian_value | ||
return node_to_laplacian | ||
|
||
def get_pagerank_map(graph): | ||
pagerank_scores = nx.pagerank(graph, alpha=0.85) | ||
return pagerank_scores | ||
|
||
def get_color_map(m): | ||
max_v = max(m.values()) | ||
min_v = min(m.values()) | ||
color_map = {} | ||
for node, v in m.items(): | ||
std = float(v - min_v) / float(max_v - min_v) | ||
color_map[node] = int(NUMBER_OF_COLORS - std * (NUMBER_OF_COLORS - 1)) | ||
return color_map | ||
|
||
def get_nodesize_map(m): | ||
max_v = max(m.values()) | ||
min_v = min(m.values()) | ||
size_map = {} | ||
for node, v in m.items(): | ||
std = float(v - min_v) / float(max_v - min_v) | ||
size_map[node] = int((MAX_NODE_SIZE - MIN_NODE_SIZE) * std + MIN_NODE_SIZE) | ||
return size_map | ||
|
||
def get_fontsize_map(m): | ||
max_v = max(m.values()) | ||
min_v = min(m.values()) | ||
fontsize_map = {} | ||
for node, v in m.items(): | ||
std = float(v - min_v) / float(max_v - min_v) | ||
fontsize_map[node] = int((MAX_FONT_SIZE - MIN_FONT_SIZE) * std + MIN_FONT_SIZE) | ||
return fontsize_map | ||
|
||
|
||
def check_graphviz_command(command): | ||
dot_command = which(command) | ||
if not dot_command: | ||
raise GraphvizNotFoundError( | ||
"Graphviz %s command not found. Please install Graphviz to generate graphical output." % command) | ||
|
||
if __name__ == '__main__': | ||
parser = argparse.ArgumentParser( | ||
description='Generates #include dependency graph.') | ||
parser.add_argument('root_path', help='Root path of the codebase.') | ||
parser.add_argument('--layout_engine', | ||
default='dot', | ||
help='Layout engine.') | ||
parser.add_argument('--dot', | ||
help='Output DOT file path.') | ||
parser.add_argument('--png', | ||
help='Output PNG file path.') | ||
parser.add_argument('--svg', | ||
help='Output SVG file path.') | ||
parser.add_argument('--extensions', nargs='+', | ||
default=COMMON_CPP_EXTENSIONS, | ||
help='List of file extensions to consider.') | ||
args = parser.parse_args() | ||
|
||
root_path = args.root_path | ||
folder_name = os.path.basename(os.path.normpath(root_path)) | ||
|
||
dot_filename = args.dot if args.dot else f'{folder_name}_include_dependency_graph.dot' | ||
|
||
try: | ||
check_graphviz_command(args.layout_engine) | ||
except GraphvizNotFoundError as e: | ||
print(colored(e, 'red')) | ||
exit(1) | ||
|
||
root_path = args.root_path | ||
dependency_graph = generate_dependency_graph(root_path, args.extensions) | ||
weight_map = get_accumulated_map(dependency_graph) | ||
color_map = get_color_map(weight_map) | ||
fontsize_map = get_fontsize_map(weight_map) | ||
nodesize_map = get_nodesize_map(weight_map) | ||
|
||
nx.set_node_attributes(dependency_graph, 'filled', name='style') | ||
nx.set_node_attributes(dependency_graph, 'circle', name='shape') | ||
nx.set_node_attributes(dependency_graph, 'true', name='fixedsize') | ||
nx.set_node_attributes(dependency_graph, COLORSCHEME, name='colorscheme') | ||
nx.set_node_attributes(dependency_graph, color_map, name='fillcolor') | ||
nx.set_node_attributes(dependency_graph, weight_map, name='weight') | ||
nx.set_node_attributes(dependency_graph, nodesize_map, name='width') | ||
nx.set_node_attributes(dependency_graph, nodesize_map, name='height') | ||
nx.set_node_attributes(dependency_graph, fontsize_map, name='fontsize') | ||
|
||
write_dot(dependency_graph, dot_filename) | ||
print(colored(f'DOT output generated: {dot_filename}', 'green')) | ||
|
||
layout_engine = args.layout_engine | ||
print(colored(f'Using layout engine {layout_engine}', 'blue')) | ||
command_line = f'{layout_engine} ' | ||
command_line += '-Goverlap=prism -Gbeautify=true -Gsmoothing=true' | ||
if args.png: | ||
png_filename = args.png | ||
command_line += " -Tpng -o %s " % png_filename | ||
|
||
if args.svg: | ||
svg_filename = args.svg | ||
command_line += " -Tsvg -o %s " % svg_filename | ||
|
||
command_line += dot_filename | ||
os.system(command_line) | ||
|
||
if args.png: | ||
print(colored(f'PNG output generated: {png_filename}', 'green')) | ||
if args.svg: | ||
print(colored(f'SVG output generated: {svg_filename}', 'green')) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.