summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Moody <daniel.moody@mongodb.com>2021-04-20 21:49:15 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-06-03 21:22:43 +0000
commita07f2690b73e9524e06e66b49c524a05bc9f4c35 (patch)
treee5297e3d7c2650e2f4c0f99dd12434c168ce7116
parentbcd1f32d78c28f789a88a5c0936cded36abc7386 (diff)
downloadmongo-a07f2690b73e9524e06e66b49c524a05bc9f4c35.tar.gz
SERVER-56636 add libdeps graphpaths visualization
-rwxr-xr-xbuildscripts/libdeps/gacli.py35
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py103
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/package.json15
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js105
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js17
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js179
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js6
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js294
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/InfoExpander.js12
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/connect.js7
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/redux/graphPaths.js23
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js13
-rw-r--r--buildscripts/libdeps/libdeps/analyzer.py101
-rw-r--r--buildscripts/libdeps/libdeps/graph.py5
14 files changed, 776 insertions, 139 deletions
diff --git a/buildscripts/libdeps/gacli.py b/buildscripts/libdeps/gacli.py
index 9b71e1544fe..836dc633660 100755
--- a/buildscripts/libdeps/gacli.py
+++ b/buildscripts/libdeps/gacli.py
@@ -178,6 +178,18 @@ def setup_args_parser():
return parser.parse_args()
+def strip_build_dir(build_dir, node):
+ """Small util function for making args match the graph paths."""
+
+ return str(Path(node).relative_to(build_dir))
+
+
+def strip_build_dirs(build_dir, nodes):
+ """Small util function for making a list of nodes match graph paths."""
+
+ return [strip_build_dir(build_dir, node) for node in nodes]
+
+
def load_graph_data(graph_file, output_format):
"""Load a graphml file."""
@@ -196,25 +208,38 @@ def main():
args = setup_args_parser()
graph = load_graph_data(args.graph_file, args.format)
libdeps_graph = LibdepsGraph(graph=graph)
+ build_dir = libdeps_graph.graph['build_dir']
+
+ if libdeps_graph.graph['graph_schema_version'] == 1:
+ libdeps_graph = networkx.reverse_view(libdeps_graph)
analysis = libdeps_analyzer.counter_factory(libdeps_graph, args.counts)
for analyzer_args in args.direct_depends:
- analysis.append(libdeps_analyzer.DirectDependents(libdeps_graph, analyzer_args))
+ analysis.append(
+ libdeps_analyzer.DirectDependents(libdeps_graph,
+ strip_build_dir(build_dir, analyzer_args)))
for analyzer_args in args.common_depends:
- analysis.append(libdeps_analyzer.CommonDependents(libdeps_graph, analyzer_args))
+ analysis.append(
+ libdeps_analyzer.CommonDependents(libdeps_graph,
+ strip_build_dirs(build_dir, analyzer_args)))
for analyzer_args in args.exclude_depends:
- analysis.append(libdeps_analyzer.ExcludeDependents(libdeps_graph, analyzer_args))
+ analysis.append(
+ libdeps_analyzer.ExcludeDependents(libdeps_graph,
+ strip_build_dirs(build_dir, analyzer_args)))
for analyzer_args in args.graph_paths:
analysis.append(
- libdeps_analyzer.GraphPaths(libdeps_graph, analyzer_args[0], analyzer_args[1]))
+ libdeps_analyzer.GraphPaths(libdeps_graph, strip_build_dir(build_dir, analyzer_args[0]),
+ strip_build_dir(build_dir, analyzer_args[1])))
for analyzer_args in args.critical_edges:
analysis.append(
- libdeps_analyzer.CriticalEdges(libdeps_graph, analyzer_args[0], analyzer_args[1]))
+ libdeps_analyzer.CriticalEdges(libdeps_graph,
+ strip_build_dir(build_dir, analyzer_args[0]),
+ strip_build_dir(build_dir, analyzer_args[1])))
if args.indegree_one:
analysis.append(libdeps_analyzer.InDegreeOne(libdeps_graph))
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py b/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
index af34ffa65a9..ce321a1bd00 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
@@ -56,6 +56,7 @@ class BackendServer:
self.app.add_url_rule("/graph_files", "return_graph_files", self.return_graph_files)
self.socketio.on_event('git_hash_selected', self.git_hash_selected)
self.socketio.on_event('row_selected', self.row_selected)
+ self.socketio.on_event('receive_graph_paths', self.receive_graph_paths)
self.loaded_graphs = {}
self.current_selected_rows = {}
@@ -65,10 +66,12 @@ class BackendServer:
self.graph_file_tuple = namedtuple('GraphFile', ['version', 'git_hash', 'graph_file'])
self.graph_files = self.get_graphml_files()
+ self._dependents_graph = None
+ self._dependency_graph = None
+
try:
default_selected_graph = list(self.graph_files.items())[0][1].graph_file
self.load_graph_from_file(default_selected_graph)
- self._dependents_graph = networkx.reverse_view(self._dependency_graph)
except (IndexError, AttributeError) as ex:
print(ex)
print(
@@ -164,40 +167,43 @@ class BackendServer:
self.socketio.emit("node_infos", nodeinfo_data)
- def send_graph_data(self):
+ def send_graph_data(self, extra_nodes=None):
"""Convert the current selected rows into a format for D3."""
with self.app.test_request_context():
- nodes = set()
- links = set()
+ nodes = {}
+ links = {}
+
+ def add_node_to_graph_data(node):
+ nodes[str(node)] = {
+ 'id': str(node), 'name': Path(node).name,
+ 'type': self._dependents_graph.nodes()[str(node)]['bin_type']
+ }
+
+ def add_link_to_graph_data(source, target):
+ links[str(source) + str(target)] = {'source': str(source), 'target': str(target)}
for node, _ in self.current_selected_rows.items():
- nodes.add(
- tuple({
- 'id': str(node), 'name': node.name, 'type': self._dependents_graph.nodes()
- [str(node)]['bin_type']
- }.items()))
-
- for depender in self._dependency_graph[str(node)]:
-
- depender_path = Path(depender)
- if self._dependents_graph[depender][str(node)].get('direct'):
- nodes.add(
- tuple({
- 'id':
- str(depender_path), 'name':
- depender_path.name, 'type':
- self._dependents_graph.nodes()[str(depender_path)]
- ['bin_type']
- }.items()))
- links.add(
- tuple({'source': str(node), 'target': str(depender_path)}.items()))
+ add_node_to_graph_data(node)
+
+ for libdep in self._dependency_graph[str(node)]:
+ if self._dependents_graph[libdep][str(node)].get('direct'):
+ add_node_to_graph_data(libdep)
+ add_link_to_graph_data(node, libdep)
+
+ if extra_nodes is not None:
+ for node in extra_nodes:
+ add_node_to_graph_data(node)
+
+ for libdep in self._dependency_graph.get_direct_nonprivate_graph()[str(node)]:
+ add_node_to_graph_data(libdep)
+ add_link_to_graph_data(node, libdep)
node_data = {
'graphData': {
- 'nodes': [dict(node) for node in nodes],
- 'links': [dict(link) for link in links],
+ 'nodes': [data for node, data in nodes.items()],
+ 'links': [data for link, data in links.items()],
}, 'selectedNodes': [str(node) for node in list(self.current_selected_rows.keys())]
}
self.socketio.emit("graph_data", node_data)
@@ -205,18 +211,16 @@ class BackendServer:
def row_selected(self, message):
"""Construct the new graphData nodeInfo when a cell is selected."""
- print(f"Got row {message}!")
-
if message['isSelected'] == 'flip':
if message['data']['node'] in self.current_selected_rows:
- self.current_selected_rows.pop(message['data']['node'])
+ self.current_selected_rows.pop(Path(message['data']['node']))
else:
self.current_selected_rows[Path(message['data']['node'])] = message['data']
else:
if message['isSelected'] and message:
self.current_selected_rows[Path(message['data']['node'])] = message['data']
else:
- self.current_selected_rows.pop(message['data']['node'])
+ self.current_selected_rows.pop(Path(message['data']['node']))
self.socketio.start_background_task(self.send_graph_data)
self.socketio.start_background_task(self.send_node_infos)
@@ -227,7 +231,7 @@ class BackendServer:
with self.app.test_request_context():
analysis = libdeps.analyzer.counter_factory(
- self._dependents_graph,
+ self._dependency_graph,
[name[0] for name in libdeps.analyzer.CountTypes.__members__.items()])
ga = libdeps.analyzer.LibdepsGraphAnalysis(analysis)
results = ga.get_results()
@@ -237,6 +241,37 @@ class BackendServer:
graph_data.append({'id': i, 'type': data, 'value': results[data]})
self.socketio.emit("graph_results", graph_data)
+ def receive_graph_paths(self, message):
+ """Receive the reqest message and kick it off to another thread."""
+
+ self.socketio.start_background_task(self.send_paths, message)
+
+ def send_paths(self, message):
+ """Gather all the nodes in the graph for the node list."""
+
+ with self.app.test_request_context():
+
+ analysis = [
+ libdeps.analyzer.GraphPaths(self._dependency_graph, message['fromNode'],
+ message['toNode'])
+ ]
+ ga = libdeps.analyzer.LibdepsGraphAnalysis(analysis=analysis)
+ results = ga.get_results()
+
+ paths = results[libdeps.analyzer.DependsReportTypes.GRAPH_PATHS.name][(
+ message['fromNode'], message['toNode'])]
+ paths.sort(key=len)
+ nodes = set()
+ for path in paths:
+ for node in path:
+ nodes.add(node)
+
+ self.send_graph_data(extra_nodes=list(nodes))
+
+ self.socketio.emit(
+ "graph_path_results",
+ {'fromNode': message['fromNode'], 'toNode': message['toNode'], 'paths': paths})
+
def send_node_list(self):
"""Gather all the nodes in the graph for the node list."""
@@ -246,7 +281,7 @@ class BackendServer:
"selectedNodes": [str(node) for node in list(self.current_selected_rows.keys())]
}
- for node in self._dependents_graph.nodes():
+ for node in sorted(self._dependents_graph.nodes()):
node_path = Path(node)
node_data['graphData']['nodes'].append(
{'id': str(node_path), 'name': node_path.name})
@@ -262,7 +297,7 @@ class BackendServer:
self.current_selected_rows = {}
if message['hash'] in self.loaded_graphs:
self._dependents_graph = self.loaded_graphs[message['hash']]
- self._dependents_graph = networkx.reverse_view(self._dependency_graph)
+ self._dependency_graph = networkx.reverse_view(self._dependents_graph)
else:
print(
f'loading new graph {current_hash} because different than {message["hash"]}'
@@ -278,8 +313,6 @@ class BackendServer:
def git_hash_selected(self, message):
"""Load the new graph and perform queries on it."""
- print(f"Got requests2 {message}!")
-
emit("other_hash_selected", message, broadcast=True)
self.socketio.start_background_task(self.load_graph, message)
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/package.json b/buildscripts/libdeps/graph_visualizer_web_stack/package.json
index 3a69d02752e..4d8196478bb 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/package.json
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/package.json
@@ -13,12 +13,13 @@
"test": "react-scripts test",
"eject": "react-scripts eject"
},
+ "//": "TODO: adding bezier and force-graph and locking versions until https://github.com/vasturiano/force-graph/issues/182 is resolved",
"dependencies": {
"@emotion/react": "^11.1.4",
"@emotion/styled": "^11.0.0",
- "@material-ui/core": "^5.0.0-alpha.1",
- "@material-ui/icons": "^5.0.0-alpha.1",
- "@material-ui/lab": "^5.0.0-alpha.1",
+ "@material-ui/core": "5.0.0-alpha.22",
+ "@material-ui/icons": "5.0.0-alpha.22",
+ "@material-ui/lab": "5.0.0-alpha.22",
"canvas": "^2.5.0",
"date-fns": "^2.16.1",
"dayjs": "^1.9.7",
@@ -28,10 +29,14 @@
"p-limit": "^3.0.2",
"react": "^16.8",
"react-dom": "^16.0.0",
- "react-force-graph-2d": "^1.18.1",
- "react-force-graph-3d": "^1.18.8",
+ "bezier-js": "4.0.3",
+ "force-graph": "1.40.0",
+ "react-force-graph-2d": "1.18.1",
+ "react-force-graph-3d": "1.18.8",
"react-indiana-drag-scroll": "^1.8.0",
"react-redux": "^7.2.2",
+ "react-resize-aware": "^3.1.0",
+ "react-resize-detector": "^6.6.5",
"react-scripts": "latest",
"react-split-pane": "^0.1.92",
"react-virtualized": "^9.22.2",
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js
new file mode 100644
index 00000000000..c75a6513d7d
--- /dev/null
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js
@@ -0,0 +1,105 @@
+import React from "react";
+import { connect } from "react-redux";
+import { makeStyles, withStyles } from "@material-ui/core/styles";
+import Typography from "@material-ui/core/Typography";
+import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
+import Paper from "@material-ui/core/Paper";
+import MuiAccordion from "@material-ui/core/Accordion";
+import MuiAccordionSummary from "@material-ui/core/AccordionSummary";
+import MuiAccordionDetails from "@material-ui/core/AccordionDetails";
+
+import { getSelected } from "./redux/store";
+
+import GraphInfo from "./GraphInfo";
+import GraphPaths from "./GraphPaths";
+import LoadingBar from "./LoadingBar";
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ width: "100%",
+ },
+ heading: {
+ fontSize: theme.typography.pxToRem(15),
+ fontWeight: theme.typography.fontWeightRegular,
+ },
+}));
+
+const Accordion = withStyles({
+ root: {
+ border: "1px solid rgba(0, 0, 0, .125)",
+ boxShadow: "none",
+ "&:not(:last-child)": {
+ borderBottom: 0,
+ },
+ "&:before": {
+ display: "none",
+ },
+ "&$expanded": {
+ margin: "auto",
+ },
+ },
+ expanded: {},
+})(MuiAccordion);
+
+const AccordionSummary = withStyles({
+ root: {
+ backgroundColor: "rgba(0, 0, 0, .03)",
+ borderBottom: "1px solid rgba(0, 0, 0, .125)",
+ marginBottom: -1,
+ minHeight: 56,
+ "&$expanded": {
+ minHeight: 56,
+ },
+ },
+ content: {
+ "&$expanded": {
+ margin: "12px 0",
+ },
+ },
+ expanded: {},
+})(MuiAccordionSummary);
+
+const AccordionDetails = withStyles((theme) => ({
+ root: {
+ padding: theme.spacing(2),
+ },
+}))(MuiAccordionDetails);
+
+const AlgorithmExpander = ({ loading, width }) => {
+ const classes = useStyles();
+
+ return (
+ <div className={classes.root}>
+ <LoadingBar loading={loading} height={"100%"}>
+ <Paper style={{ maxHeight: "82vh", overflow: "auto" }}>
+ <Accordion>
+ <AccordionSummary
+ expandIcon={<ExpandMoreIcon />}
+ aria-controls="panel1a-content"
+ id="panel1a-header"
+ >
+ <Typography className={classes.heading}>Counts</Typography>
+ </AccordionSummary>
+ <AccordionDetails>
+ <GraphInfo datawidth={width} />
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary
+ expandIcon={<ExpandMoreIcon />}
+ aria-controls="panel1a-content"
+ id="panel1a-header"
+ >
+ <Typography className={classes.heading}>Graph Paths</Typography>
+ </AccordionSummary>
+ <AccordionDetails>
+ <GraphPaths datawidth={width} />
+ </AccordionDetails>
+ </Accordion>
+ </Paper>
+ </LoadingBar>
+ </div>
+ );
+};
+
+export default connect(getSelected)(AlgorithmExpander);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js
index 3975ffe9f3a..8364b55ff67 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js
@@ -24,25 +24,26 @@ function rgbToHex(r, g, b) {
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
- hex = hex.replace(shorthandRegex, function(m, r, g, b) {
+ hex = hex.replace(shorthandRegex, function (m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? {
- r: parseInt(result[1], 16),
- g: parseInt(result[2], 16),
- b: parseInt(result[3], 16)
- } : null;
+ return result
+ ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16),
+ }
+ : null;
}
-function incrementPallete(palleteColor, increment){
+function incrementPallete(palleteColor, increment) {
var rgb = hexToRgb(palleteColor);
rgb.r += increment;
rgb.g += increment;
rgb.b += increment;
return rgbToHex(rgb.r, rgb.g, rgb.b);
-
}
const styles = (theme) => ({
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js
index 672bdaa41c3..375942f4852 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js
@@ -55,11 +55,15 @@ const DrawGraph = ({
graphData,
nodes,
loading,
+ graphPaths,
+ updateCheckbox,
findNode,
setFindNode,
}) => {
const [activeComponent, setActiveComponent] = React.useState("2D");
const [selectedNodes, setSelectedNodes] = React.useState([]);
+ const [pathNodes, setPathNodes] = React.useState({});
+ const [pathEdges, setPathEdges] = React.useState([]);
const forceRef = useRef(null);
React.useEffect(() => {
@@ -78,18 +82,52 @@ const DrawGraph = ({
}, [nodes]);
React.useEffect(() => {
+ setPathNodes({ fromNode: graphPaths.fromNode, toNode: graphPaths.toNode });
+ var paths = Array();
+ for (var path = 0; path < graphPaths.paths.length; path++) {
+ var pathArr = Array();
+ for (var i = 0; i < graphPaths.paths[path].length; i++) {
+ if (i == 0) {
+ continue;
+ }
+ pathArr.push({
+ source: graphPaths.paths[path][i - 1],
+ target: graphPaths.paths[path][i],
+ });
+ }
+ paths.push(pathArr);
+ }
+ setPathEdges(paths);
+ }, [graphPaths]);
+
+ React.useEffect(() => {
if (forceRef.current != null) {
- forceRef.current.d3Force("charge").strength(-1400);
+ if (activeComponent == '3D'){
+ forceRef.current.d3Force("charge").strength(-2000);
+ }
+ else {
+ forceRef.current.d3Force("charge").strength(-10000);
+ }
+
}
- }, [forceRef.current]);
+ }, [forceRef.current, activeComponent]);
- const paintRing = React.useCallback((node, ctx) => {
- // add ring just for highlighted nodes
- ctx.beginPath();
- ctx.arc(node.x, node.y, 4 * 1.4, 0, 2 * Math.PI, false);
- ctx.fillStyle = "green";
- ctx.fill();
- });
+ const paintRing = React.useCallback(
+ (node, ctx) => {
+ // add ring just for highlighted nodes
+ ctx.beginPath();
+ ctx.arc(node.x, node.y, 7 * 1.4, 0, 2 * Math.PI, false);
+ if (node.id == pathNodes.fromNode) {
+ ctx.fillStyle = "blue";
+ } else if (node.id == pathNodes.toNode) {
+ ctx.fillStyle = "red";
+ } else {
+ ctx.fillStyle = "green";
+ }
+ ctx.fill();
+ },
+ [pathNodes]
+ );
function colorNodes(node) {
switch (node.type) {
@@ -134,6 +172,7 @@ const DrawGraph = ({
name="3D"
width={size}
dagMode="radialout"
+ dagLevelDistance={50}
graphData={graphData}
ref={forceRef}
nodeColor={colorNodes}
@@ -141,14 +180,70 @@ const DrawGraph = ({
backgroundColor={theme.palette.secondary.dark}
linkDirectionalArrowLength={6}
linkDirectionalArrowRelPos={1}
+ linkDirectionalParticles={(d) => {
+ if (graphPaths.selectedPath >= 0) {
+ for (
+ var i = 0;
+ i < pathEdges[graphPaths.selectedPath].length;
+ i++
+ ) {
+ if (
+ pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
+ pathEdges[graphPaths.selectedPath][i].target == d.target.id
+ ) {
+ return 5;
+ }
+ }
+ }
+ return 0;
+ }}
+ linkDirectionalParticleSpeed={(d) => {
+ return 0.01;
+ }}
nodeCanvasObjectMode={(node) => {
if (selectedNodes.includes(node.id)) {
return "before";
}
}}
+ linkColor={(d) => {
+ if (graphPaths.selectedPath >= 0) {
+ for (
+ var i = 0;
+ i < pathEdges[graphPaths.selectedPath].length;
+ i++
+ ) {
+ if (
+ pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
+ pathEdges[graphPaths.selectedPath][i].target == d.target.id
+ ) {
+ return "#12FF19";
+ }
+ }
+ }
+ return "#FAFAFA";
+ }}
+ linkDirectionalParticleWidth={6}
+ linkWidth={(d) => {
+ if (graphPaths.selectedPath >= 0) {
+ for (
+ var i = 0;
+ i < pathEdges[graphPaths.selectedPath].length;
+ i++
+ ) {
+ if (
+ pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
+ pathEdges[graphPaths.selectedPath][i].target == d.target.id
+ ) {
+ return 2;
+ }
+ }
+ }
+ return 1;
+ }}
+ nodeRelSize={7}
nodeCanvasObject={paintRing}
onNodeClick={(node, event) => {
- updateCheckbox(node.id);
+ updateCheckbox({ node: node.id, value: "flip" });
socket.emit("row_selected", {
data: { node: node.id, name: node.name },
isSelected: !selectedNodes.includes(node.id),
@@ -175,12 +270,70 @@ const DrawGraph = ({
}
}}
onNodeClick={(node, event) => {
- updateCheckbox(node.id);
+ updateCheckbox({ node: node.id, value: "flip" });
socket.emit("row_selected", {
data: { node: node.id, name: node.name },
isSelected: !selectedNodes.includes(node.id),
});
}}
+ linkColor={(d) => {
+ if (graphPaths.selectedPath >= 0) {
+ for (
+ var i = 0;
+ i < pathEdges[graphPaths.selectedPath].length;
+ i++
+ ) {
+ if (
+ pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
+ pathEdges[graphPaths.selectedPath][i].target == d.target.id
+ ) {
+ return "#12FF19";
+ }
+ }
+ }
+ return "#FAFAFA";
+ }}
+ linkDirectionalParticleWidth={7}
+ linkWidth={(d) => {
+ if (graphPaths.selectedPath >= 0) {
+ for (
+ var i = 0;
+ i < pathEdges[graphPaths.selectedPath].length;
+ i++
+ ) {
+ if (
+ pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
+ pathEdges[graphPaths.selectedPath][i].target == d.target.id
+ ) {
+ return 5;
+ }
+ }
+ }
+ return 1;
+ }}
+ linkDirectionalParticles={(d) => {
+ if (graphPaths.selectedPath >= 0) {
+ for (
+ var i = 0;
+ i < pathEdges[graphPaths.selectedPath].length;
+ i++
+ ) {
+ if (
+ pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
+ pathEdges[graphPaths.selectedPath][i].target == d.target.id
+ ) {
+ return 5;
+ }
+ }
+ }
+ return 0;
+ }}
+ linkDirectionalParticleSpeed={(d) => {
+ return 0.01;
+ }}
+ linkDirectionalParticleResolution={10}
+ linkOpacity={0.6}
+ nodeRelSize={7}
backgroundColor={theme.palette.secondary.dark}
linkDirectionalArrowLength={3.5}
linkDirectionalArrowRelPos={1}
@@ -191,4 +344,6 @@ const DrawGraph = ({
);
};
-export default connect(getGraphData, { setFindNode })(DrawGraph);
+export default connect(getGraphData, { setFindNode, updateCheckbox })(
+ DrawGraph
+);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js
index 0cdec3d12b4..3636581f375 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js
@@ -6,6 +6,7 @@ import Tab from "@material-ui/core/Tab";
import NodeList from "./NodeList";
import InfoExpander from "./InfoExpander";
+import AlgorithmExpander from "./AlgorithmExpander";
function a11yProps(index) {
return {
@@ -46,6 +47,7 @@ export default function GraphInfoTabs({ nodes, width }) {
<Tab label="Selected Info" {...a11yProps(0)} />
<Tab label="Node List" {...a11yProps(1)} />
<Tab label="Edge List" {...a11yProps(2)} />
+ <Tab label="Algorithms" {...a11yProps(3)} />
</Tabs>
</AppBar>
<div style={{ height: "100%" }} hidden={value != 0}>
@@ -54,6 +56,10 @@ export default function GraphInfoTabs({ nodes, width }) {
<div style={{ height: "100%" }} hidden={value != 1}>
<NodeList nodes={nodes}></NodeList>
</div>
+ <div style={{ height: "100%" }} hidden={value != 2}></div>
+ <div style={{ height: "100%" }} hidden={value != 3}>
+ <AlgorithmExpander width={width}></AlgorithmExpander>
+ </div>
</div>
);
}
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js
new file mode 100644
index 00000000000..0b86129bd98
--- /dev/null
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js
@@ -0,0 +1,294 @@
+import React from "react";
+import { connect } from "react-redux";
+import { FixedSizeList } from "react-window";
+import SplitPane from "react-split-pane";
+import { makeStyles, withStyles } from "@material-ui/core/styles";
+import ListItem from "@material-ui/core/ListItem";
+import ListItemText from "@material-ui/core/ListItemText";
+import Paper from "@material-ui/core/Paper";
+import Typography from "@material-ui/core/Typography";
+import Box from "@material-ui/core/Box";
+import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
+import MuiAccordion from "@material-ui/core/Accordion";
+import MuiAccordionSummary from "@material-ui/core/AccordionSummary";
+import MuiAccordionDetails from "@material-ui/core/AccordionDetails";
+import { socket } from "./connect";
+import useResizeAware from "react-resize-aware";
+
+import { getSelected } from "./redux/store";
+import { selectedGraphPaths, setSelectedPath } from "./redux/graphPaths";
+
+import OverflowTooltip from "./OverflowTooltip";
+
+const rowHeight = 25;
+
+const Accordion = withStyles({
+ root: {
+ border: "1px solid rgba(0, 0, 0, .125)",
+ boxShadow: "none",
+ "&:not(:last-child)": {
+ borderBottom: 0,
+ },
+ "&:before": {
+ display: "none",
+ },
+ "&$expanded": {
+ margin: "auto",
+ },
+ },
+ expanded: {},
+})(MuiAccordion);
+
+const AccordionSummary = withStyles({
+ root: {
+ backgroundColor: "rgba(0, 0, 0, .03)",
+ borderBottom: "1px solid rgba(0, 0, 0, .125)",
+ marginBottom: -1,
+ minHeight: 56,
+ "&$expanded": {
+ minHeight: 56,
+ },
+ },
+ content: {
+ "&$expanded": {
+ margin: "12px 0",
+ },
+ },
+ expanded: {},
+})(MuiAccordionSummary);
+
+const AccordionDetails = withStyles((theme) => ({
+ root: {
+ padding: theme.spacing(2),
+ },
+}))(MuiAccordionDetails);
+
+const GraphPaths = ({ selectedNodes, graphPaths, setSelectedPath, width }) => {
+ const [fromNode, setFromNode] = React.useState("");
+ const [toNode, setToNode] = React.useState("");
+ const [fromNodeId, setFromNodeId] = React.useState(0);
+ const [toNodeId, setToNodeId] = React.useState(0);
+ const [fromNodeExpanded, setFromNodeExpanded] = React.useState(false);
+ const [toNodeExpanded, setToNodeExpanded] = React.useState(false);
+ const [paneSize, setPaneSize] = React.useState("50%");
+
+ const [fromResizeListener, fromSizes] = useResizeAware();
+ const [toResizeListener, toSizes] = useResizeAware();
+
+ const useStyles = makeStyles((theme) => ({
+ root: {
+ width: "100%",
+ maxWidth: width,
+ backgroundColor: theme.palette.background.paper,
+ },
+ nested: {
+ paddingLeft: theme.spacing(4),
+ },
+ listItem: {
+ width: width,
+ },
+ }));
+ const classes = useStyles();
+
+ function toNodeRow({ index, style, data }) {
+ return (
+ <ListItem
+ button
+ style={style}
+ key={index}
+ onClick={() => {
+ setToNode(data[index].name);
+ setToNodeId(index);
+ setToNodeExpanded(false);
+ setPaneSize("50%");
+ if (fromNode != "" && data[fromNodeId]) {
+ const nodes = {
+ fromNode: data[fromNodeId].node,
+ toNode: data[index].node,
+ };
+ socket.emit("receive_graph_paths", nodes);
+ }
+ }}
+ >
+ <ListItemText primary={data[index].name} />
+ </ListItem>
+ );
+ }
+
+ function fromNodeRow({ index, style, data }) {
+ return (
+ <ListItem
+ button
+ style={style}
+ key={index}
+ onClick={() => {
+ setFromNode(data[index].name);
+ setFromNodeId(index);
+ setFromNodeExpanded(false);
+ setPaneSize("50%");
+
+ if (toNode != "" && data[toNodeId]) {
+ const nodes = {
+ fromNode: data[index].node,
+ toNode: data[toNodeId].node,
+ };
+ socket.emit("receive_graph_paths", nodes);
+ }
+ }}
+ >
+ <ListItemText primary={data[index].name} />
+ </ListItem>
+ );
+ }
+
+ function pathRow({ index, style, data }) {
+ return (
+ <ListItem
+ button
+ style={style}
+ key={index}
+ onClick={() => {
+ setSelectedPath(index);
+ }}
+ >
+ <ListItemText
+ primary={
+ "Path #" +
+ (index + 1).toString() +
+ " - Hops: " +
+ (data[index].length - 1).toString()
+ }
+ />
+ </ListItem>
+ );
+ }
+
+ function listHeight(numItems, minHeight, maxHeight) {
+ const size = numItems * rowHeight;
+ if (size > maxHeight) {
+ return maxHeight;
+ }
+ if (size < minHeight) {
+ return minHeight;
+ }
+ return size;
+ }
+
+ const handleToChange = (panel) => (event, newExpanded) => {
+ setPaneSize(newExpanded ? "0%" : "50%");
+ setToNodeExpanded(newExpanded ? panel : false);
+ };
+
+ const handleFromChange = (panel) => (event, newExpanded) => {
+ setPaneSize(newExpanded ? "100%" : "50%");
+ setFromNodeExpanded(newExpanded ? panel : false);
+ };
+
+ return (
+ <Paper elevation={3} style={{ backgroundColor: "rgba(0, 0, 0, .03)" }}>
+ <SplitPane
+ split="vertical"
+ minSize={"50%"}
+ size={paneSize}
+ style={{ position: "relative" }}
+ defaultSize={"50%"}
+ pane1Style={{ height: "100%" }}
+ pane2Style={{ height: "100%", width: "100%" }}
+ >
+ <Accordion
+ expanded={fromNodeExpanded}
+ onChange={handleFromChange(!fromNodeExpanded)}
+ >
+ <AccordionSummary
+ expandIcon={<ExpandMoreIcon />}
+ aria-controls="panel1a-content"
+ id="panel1a-header"
+ >
+ <Box
+ style={{
+ display: "flex",
+ flexDirection: "column",
+ }}
+ >
+ <Typography className={classes.heading}>From Node:</Typography>
+ <Typography
+ className={classes.heading}
+ style={{ width: fromSizes.width - 50 }}
+ noWrap={true}
+ display={"block"}
+ >
+ {fromResizeListener}
+ {fromNode}
+ </Typography>
+ </Box>
+ </AccordionSummary>
+ <AccordionDetails>
+ <FixedSizeList
+ height={listHeight(selectedNodes.length, 100, 200)}
+ width={width}
+ itemSize={rowHeight}
+ itemCount={selectedNodes.length}
+ itemData={selectedNodes}
+ >
+ {fromNodeRow}
+ </FixedSizeList>
+ </AccordionDetails>
+ </Accordion>
+
+ <Accordion
+ expanded={toNodeExpanded}
+ onChange={handleToChange(!toNodeExpanded)}
+ >
+ <AccordionSummary
+ expandIcon={<ExpandMoreIcon />}
+ aria-controls="panel1a-content"
+ id="panel1a-header"
+ >
+ <Box style={{ display: "flex", flexDirection: "column" }}>
+ <Typography className={classes.heading}>To Node:</Typography>
+ <Typography
+ className={classes.heading}
+ style={{ width: toSizes.width - 50 }}
+ noWrap={true}
+ display={"block"}
+ >
+ {toResizeListener}
+ {toNode}
+ </Typography>
+ </Box>
+ </AccordionSummary>
+ <AccordionDetails>
+ <FixedSizeList
+ height={listHeight(selectedNodes.length, 100, 200)}
+ width={width}
+ itemSize={rowHeight}
+ itemCount={selectedNodes.length}
+ itemData={selectedNodes}
+ >
+ {toNodeRow}
+ </FixedSizeList>
+ </AccordionDetails>
+ </Accordion>
+ </SplitPane>
+ <Paper elevation={2} style={{ backgroundColor: "rgba(0, 0, 0, .03)" }}>
+ <Typography className={classes.heading} style={{ margin: "10px" }}>
+ Num Paths: {graphPaths.paths.length}{" "}
+ </Typography>
+ </Paper>
+ <FixedSizeList
+ height={listHeight(graphPaths.paths.length, 100, 200)}
+ width={width}
+ itemSize={rowHeight}
+ itemCount={graphPaths.paths.length}
+ itemData={graphPaths.paths}
+ style={{ margin: "10px" }}
+ >
+ {pathRow}
+ </FixedSizeList>
+ </Paper>
+ );
+};
+
+export default connect(getSelected, { selectedGraphPaths, setSelectedPath })(
+ GraphPaths
+);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/InfoExpander.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/InfoExpander.js
index 8912b1c9d5b..dbe6f579eea 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/InfoExpander.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/InfoExpander.js
@@ -72,18 +72,6 @@ const InfoExpander = ({ selectedNodes, selectedEdges, loading, width }) => {
<div className={classes.root}>
<LoadingBar loading={loading} height={"100%"}>
<Paper style={{ maxHeight: "82vh", overflow: "auto" }}>
- <Accordion>
- <AccordionSummary
- expandIcon={<ExpandMoreIcon />}
- aria-controls="panel1a-content"
- id="panel1a-header"
- >
- <Typography className={classes.heading}>Counts</Typography>
- </AccordionSummary>
- <AccordionDetails>
- <GraphInfo datawidth={width} />
- </AccordionDetails>
- </Accordion>
{selectedNodes.map((node) => (
<Accordion key={node.node}>
<AccordionSummary
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/connect.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/connect.js
index b055f1910a5..efa3b8b9ec3 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/connect.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/connect.js
@@ -9,6 +9,7 @@ import { setGraphFiles, selectGraphFile } from "./redux/graphFiles";
import { setLoading } from "./redux/loading";
import { addLinks } from "./redux/links";
import { setGraphData } from "./redux/graphData";
+import { selectedGraphPaths } from "./redux/graphPaths";
const { REACT_APP_API_URL } = process.env;
@@ -27,6 +28,7 @@ const SocketConnection = ({
addLinks,
setGraphData,
setNodeInfos,
+ selectedGraphPaths,
}) => {
React.useEffect(() => {
fetch(REACT_APP_API_URL + "/graph_files")
@@ -82,6 +84,10 @@ const SocketConnection = ({
socket.on("node_infos", (incomingData) => {
setNodeInfos(incomingData.nodeInfos);
});
+
+ socket.on("graph_path_results", (incomingData) => {
+ selectedGraphPaths(incomingData);
+ });
}, []);
return null;
@@ -97,4 +103,5 @@ export default reduxConnect(null, {
selectGraphFile,
addLinks,
setGraphData,
+ selectedGraphPaths,
})(SocketConnection);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/graphPaths.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/graphPaths.js
new file mode 100644
index 00000000000..135c9c30e32
--- /dev/null
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/graphPaths.js
@@ -0,0 +1,23 @@
+import { initialState } from "./store";
+
+export const graphPaths = (state = initialState, action) => {
+ switch (action.type) {
+ case "selectedGraphPaths":
+ return action.payload;
+ case "setSelectedPath":
+ const newState = { ...state, selectedPath: action.payload };
+ return newState;
+ default:
+ return state;
+ }
+};
+
+export const selectedGraphPaths = (pathData) => ({
+ type: "selectedGraphPaths",
+ payload: pathData,
+});
+
+export const setSelectedPath = (path) => ({
+ type: "setSelectedPath",
+ payload: path,
+});
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js
index c4058f510eb..8024c7cb327 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js
@@ -7,6 +7,7 @@ import { loading } from "./loading";
import { links } from "./links";
import { graphData } from "./graphData";
import { findNode } from "./findNode";
+import { graphPaths } from "./graphPaths";
export const initialState = {
loading: false,
@@ -39,6 +40,15 @@ export const initialState = {
// {source: 'test/test1.so', target: 'test/test2.so'}
],
},
+ graphPaths: {
+ fromNode: "test",
+ toNode: "test",
+ paths: [
+ ["test1", "test2"],
+ ["test1", "test3", "test2"],
+ ],
+ selectedPath: -1,
+ },
counts: [{ id: 0, type: "node2", value: 0 }],
findNode: "",
nodeInfo: [
@@ -91,6 +101,7 @@ export const getSelected = (state) => {
selectedNodes: state.nodes.filter((node) => node.selected),
selectedEdges: [],
loading: state.loading,
+ graphPaths: state.graphPaths,
};
};
@@ -107,6 +118,7 @@ export const getGraphData = (state) => {
graphData: state.graphData,
loading: state.loading,
findNode: state.findNode,
+ graphPaths: state.graphPaths,
};
};
@@ -124,6 +136,7 @@ const store = createStore(
links,
graphData,
findNode,
+ graphPaths,
}),
initialState
);
diff --git a/buildscripts/libdeps/libdeps/analyzer.py b/buildscripts/libdeps/libdeps/analyzer.py
index 756d410d4e8..c440fcf7d8f 100644
--- a/buildscripts/libdeps/libdeps/analyzer.py
+++ b/buildscripts/libdeps/libdeps/analyzer.py
@@ -115,17 +115,14 @@ class Analyzer:
"""Base class for different types of analyzers."""
# pylint: disable=too-many-instance-attributes
- def __init__(self, graph, progress=True):
+ def __init__(self, dependency_graph, progress=True):
"""Store the graph and extract the build_dir from the graph."""
- self.graph_schema = graph.graph.get('graph_schema_version')
- if self.graph_schema == 1:
- self._dependents_graph = graph
- else:
- self._dependency_graph = graph
+ self.graph_schema = dependency_graph.graph.get('graph_schema_version')
+ self._dependency_graph = dependency_graph
- self._build_dir = Path(graph.graph['build_dir'])
- self.deptypes = json.loads(graph.graph.get('deptypes', "{}"))
+ self._build_dir = Path(dependency_graph.graph['build_dir'])
+ self.deptypes = json.loads(dependency_graph.graph.get('deptypes', "{}"))
self.set_progress(progress)
@property
@@ -153,16 +150,6 @@ class Analyzer:
return int(self._dependency_graph.get_deptype(deptype))
- def _strip_build_dir(self, node):
- """Small util function for making args match the graph paths."""
-
- return str(Path(node).relative_to(self._build_dir))
-
- def _strip_build_dirs(self, nodes):
- """Small util function for making a list of nodes match graph paths."""
-
- return [self._strip_build_dir(node) for node in nodes]
-
def set_progress(self, value=None):
"""Get a progress bar from the loaded graph."""
@@ -198,10 +185,10 @@ class Counter(Analyzer):
class NodeCounter(Counter):
"""Counts and reports number of nodes in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.NODE.name
@schema_check(schema_version=1)
@@ -214,10 +201,10 @@ class NodeCounter(Counter):
class EdgeCounter(Counter):
"""Counts and reports number of edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.EDGE.name
@schema_check(schema_version=1)
@@ -230,10 +217,10 @@ class EdgeCounter(Counter):
class DirectEdgeCounter(Counter):
"""Counts and reports number of direct edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.DIR_EDGE.name
@schema_check(schema_version=1)
@@ -246,10 +233,10 @@ class DirectEdgeCounter(Counter):
class TransEdgeCounter(Counter):
"""Counts and reports number of transitive edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.TRANS_EDGE.name
@schema_check(schema_version=1)
@@ -262,10 +249,10 @@ class TransEdgeCounter(Counter):
class DirectPubEdgeCounter(Counter):
"""Counts and reports number of direct public edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.DIR_PUB_EDGE.name
@schema_check(schema_version=1)
@@ -281,10 +268,10 @@ class DirectPubEdgeCounter(Counter):
class PublicEdgeCounter(Counter):
"""Counts and reports number of public edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.PUB_EDGE.name
@schema_check(schema_version=1)
@@ -297,10 +284,10 @@ class PublicEdgeCounter(Counter):
class PrivateEdgeCounter(Counter):
"""Counts and reports number of private edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.PRIV_EDGE.name
@schema_check(schema_version=1)
@@ -314,10 +301,10 @@ class PrivateEdgeCounter(Counter):
class InterfaceEdgeCounter(Counter):
"""Counts and reports number of interface edges in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.IF_EDGE.name
@schema_check(schema_version=1)
@@ -331,10 +318,10 @@ class InterfaceEdgeCounter(Counter):
class LibCounter(Counter):
"""Counts and reports number of library nodes in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.LIB.name
@schema_check(schema_version=1)
@@ -347,10 +334,10 @@ class LibCounter(Counter):
class ProgCounter(Counter):
"""Counts and reports number of program nodes in the graph."""
- def __init__(self, graph):
+ def __init__(self, dependency_graph):
"""Store graph and set type."""
- super().__init__(graph)
+ super().__init__(dependency_graph)
self._count_type = CountTypes.PROG.name
@schema_check(schema_version=1)
@@ -360,7 +347,7 @@ class ProgCounter(Counter):
return self.node_type_count(NodeProps.bin_type.name, 'Program')
-def counter_factory(graph, counters, progressbar=True):
+def counter_factory(dependency_graph, counters, progressbar=True):
"""Construct counters from a list of strings."""
counter_map = {
@@ -382,7 +369,7 @@ def counter_factory(graph, counters, progressbar=True):
counter_objs = []
for counter in counters:
if counter in counter_map:
- counter_obj = counter_map[counter](graph)
+ counter_obj = counter_map[counter](dependency_graph)
counter_obj.set_progress(progressbar)
counter_objs.append(counter_obj)
@@ -395,11 +382,11 @@ def counter_factory(graph, counters, progressbar=True):
class CommonDependents(Analyzer):
"""Finds common dependent nodes for a set of given dependency nodes."""
- def __init__(self, graph, nodes):
+ def __init__(self, dependency_graph, nodes):
"""Store graph and strip the nodes."""
- super().__init__(graph)
- self._nodes = self._strip_build_dirs(nodes)
+ super().__init__(dependency_graph)
+ self._nodes = nodes
@schema_check(schema_version=1)
def run(self):
@@ -419,11 +406,11 @@ class CommonDependents(Analyzer):
class DirectDependents(Analyzer):
"""Finds direct dependent nodes for a given dependency node."""
- def __init__(self, graph, node):
+ def __init__(self, dependency_graph, node):
"""Store graph and strip the node."""
- super().__init__(graph)
- self._node = self._strip_build_dir(node)
+ super().__init__(dependency_graph)
+ self._node = node
@schema_check(schema_version=1)
def run(self):
@@ -445,11 +432,11 @@ class DirectDependents(Analyzer):
class ExcludeDependents(Analyzer):
"""Finds dependents which depend on the first input node, but exclude the other input nodes."""
- def __init__(self, graph, nodes):
+ def __init__(self, dependency_graph, nodes):
"""Store graph and strip the nodes."""
- super().__init__(graph)
- self._nodes = self._strip_build_dirs(nodes)
+ super().__init__(dependency_graph)
+ self._nodes = nodes
@schema_check(schema_version=1)
def run(self):
@@ -509,11 +496,11 @@ class InDegreeOne(Analyzer):
class GraphPaths(Analyzer):
"""Finds all paths between two nodes in the graph."""
- def __init__(self, graph, from_node, to_node):
+ def __init__(self, dependency_graph, from_node, to_node):
"""Store graph and strip the nodes."""
- super().__init__(graph)
- self._from_node, self._to_node = self._strip_build_dirs([from_node, to_node])
+ super().__init__(dependency_graph)
+ self._from_node, self._to_node = from_node, to_node
@schema_check(schema_version=1)
def run(self):
@@ -545,11 +532,11 @@ class GraphPaths(Analyzer):
class CriticalEdges(Analyzer):
"""Finds all edges between two nodes, where removing those edges disconnects the two nodes."""
- def __init__(self, graph, from_node, to_node):
+ def __init__(self, dependency_graph, from_node, to_node):
"""Store graph and strip the nodes."""
- super().__init__(graph)
- self._from_node, self._to_node = self._strip_build_dirs([from_node, to_node])
+ super().__init__(dependency_graph)
+ self._from_node, self._to_node = from_node, to_node
@schema_check(schema_version=1)
def run(self):
@@ -672,7 +659,7 @@ class UnusedPublicLinter(Analyzer):
report[LinterTypes.PUBLIC_UNUSED.name] = self.run()
-def linter_factory(graph, linters, progressbar=True):
+def linter_factory(dependency_graph, linters, progressbar=True):
"""Construct linters from a list of strings."""
linter_map = {
@@ -685,7 +672,7 @@ def linter_factory(graph, linters, progressbar=True):
linters_objs = []
for linter in linters:
if linter in linter_map:
- linters_objs.append(linter_map[linter](graph, progressbar))
+ linters_objs.append(linter_map[linter](dependency_graph, progressbar))
else:
print(f"Skipping unknown counter: {linter}")
diff --git a/buildscripts/libdeps/libdeps/graph.py b/buildscripts/libdeps/libdeps/graph.py
index 2e7dbbcd234..74446ceff6f 100644
--- a/buildscripts/libdeps/libdeps/graph.py
+++ b/buildscripts/libdeps/libdeps/graph.py
@@ -120,11 +120,6 @@ class LibdepsGraph(networkx.DiGraph):
return self._deptypes[deptype]
- def _strip_build_dir(self, node):
- """Small util function for making args match the graph paths."""
-
- return str(Path(node).resolve().relative_to(Path(self.graph['build_dir']).resolve()))
-
def get_direct_nonprivate_graph(self):
"""Get a graph view of direct nonprivate edges."""