summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py58
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/package.json2
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js4
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js62
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js192
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/EdgeList.js261
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfo.js12
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js29
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js112
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/NodeList.js101
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/OverflowTooltip.js59
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/redux/links.js24
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/redux/linksTrans.js32
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/redux/showTransitive.js16
-rw-r--r--buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js51
-rw-r--r--site_scons/libdeps.py35
16 files changed, 824 insertions, 226 deletions
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 f9f1b2b25aa..85d7e993d1f 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/flask/flask_backend.py
@@ -32,6 +32,7 @@ from collections import namedtuple, OrderedDict
import flask
import networkx
+import cxxfilt
from flask_cors import CORS
from flask_session import Session
@@ -157,16 +158,12 @@ class BackendServer:
'name': key, 'value': value
} for key, value in dependents_graph.nodes(data=True)[str(node)].items()],
'dependers': [{
- 'node':
- depender, 'symbols':
- dependents_graph[str(node)][depender].get('symbols',
- '').split(' ')
+ 'node': depender, 'symbols': dependents_graph[str(node)]
+ [depender].get('symbols')
} for depender in dependents_graph[str(node)]],
'dependencies': [{
- 'node':
- dependency, 'symbols':
- dependents_graph[dependency][str(node)].get('symbols',
- '').split(' ')
+ 'node': dependency, 'symbols': dependents_graph[dependency]
+ [str(node)].get('symbols')
} for dependency in dependency_graph[str(node)]],
})
@@ -189,16 +186,17 @@ class BackendServer:
nodes = {}
links = {}
+ links_trans = {}
def add_node_to_graph_data(node):
nodes[str(node)] = {
- 'id': str(node), 'name': Path(node).name, 'type': dependents_graph.nodes()
- [str(node)]['bin_type']
+ 'id': str(node), 'name': Path(node).name,
+ 'type': dependents_graph.nodes()[str(node)].get('bin_type', '')
}
- def add_link_to_graph_data(source, target):
+ def add_link_to_graph_data(source, target, data):
links[str(source) + str(target)] = {
- 'source': str(source), 'target': str(target)
+ 'source': str(source), 'target': str(target), 'data': data
}
for node in selected_nodes:
@@ -207,7 +205,15 @@ class BackendServer:
for libdep in dependency_graph[str(node)]:
if dependents_graph[libdep][str(node)].get('direct'):
add_node_to_graph_data(libdep)
- add_link_to_graph_data(node, libdep)
+ add_link_to_graph_data(node, libdep,
+ dependents_graph[libdep][str(node)])
+
+ if "transitive_edges" in req_body.keys() and req_body["transitive_edges"] is True:
+ for node in selected_nodes:
+ for libdep in dependency_graph[str(node)]:
+ if str(libdep) in nodes.keys():
+ add_link_to_graph_data(node, libdep,
+ dependents_graph[libdep][str(node)])
if "extra_nodes" in req_body.keys():
extra_nodes = req_body["extra_nodes"]
@@ -216,12 +222,14 @@ class BackendServer:
for libdep in dependency_graph.get_direct_nonprivate_graph()[str(node)]:
add_node_to_graph_data(libdep)
- add_link_to_graph_data(node, libdep)
+ add_link_to_graph_data(node, libdep,
+ dependents_graph[libdep][str(node)])
node_data = {
'graphData': {
'nodes': [data for node, data in nodes.items()],
'links': [data for link, data in links.items()],
+ 'links_trans': [data for link, data in links_trans.items()],
}
}
return node_data, 200
@@ -306,7 +314,27 @@ class BackendServer:
else:
if git_hash in self.graph_files:
file_path = self.graph_files[git_hash].graph_file
- graph = libdeps.graph.LibdepsGraph(networkx.read_graphml(file_path))
+ nx_graph = networkx.read_graphml(file_path)
+ if int(self.get_graph_build_data(file_path).version) > 3:
+ for source, target in nx_graph.edges:
+ try:
+ nx_graph[source][target]['symbols'] = list(
+ nx_graph[source][target].get('symbols').split('\n'))
+ except AttributeError:
+ nx_graph[source][target]['symbols'] = []
+ else:
+ for source, target in nx_graph.edges:
+ try:
+ nx_graph[source][target]['symbols'] = list(
+ map(cxxfilt.demangle,
+ nx_graph[source][target].get('symbols').split()))
+ except AttributeError:
+ try:
+ nx_graph[source][target]['symbols'] = list(
+ nx_graph[source][target].get('symbols').split())
+ except AttributeError:
+ nx_graph[source][target]['symbols'] = []
+ graph = libdeps.graph.LibdepsGraph(nx_graph)
self.loaded_graphs[git_hash] = graph
return graph
return None
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/package.json b/buildscripts/libdeps/graph_visualizer_web_stack/package.json
index f1da6698942..ace34ed2d8d 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/package.json
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/package.json
@@ -38,7 +38,7 @@
"react-redux": "^7.2.2",
"react-resize-aware": "^3.1.0",
"react-resize-detector": "^6.6.5",
- "react-scripts": "latest",
+ "react-scripts": "^4.0.3",
"react-split-pane": "^0.1.92",
"react-virtualized": "^9.22.2",
"react-window": "^1.8.6",
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js
index c75a6513d7d..7473e6be830 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/AlgorithmExpander.js
@@ -65,7 +65,7 @@ const AccordionDetails = withStyles((theme) => ({
},
}))(MuiAccordionDetails);
-const AlgorithmExpander = ({ loading, width }) => {
+const AlgorithmExpander = ({ loading, width, transPathFrom, transPathTo }) => {
const classes = useStyles();
return (
@@ -93,7 +93,7 @@ const AlgorithmExpander = ({ loading, width }) => {
<Typography className={classes.heading}>Graph Paths</Typography>
</AccordionSummary>
<AccordionDetails>
- <GraphPaths datawidth={width} />
+ <GraphPaths datawidth={width} transPathFrom={transPathFrom} transPathTo={transPathTo}/>
</AccordionDetails>
</Accordion>
</Paper>
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js
index a1d43ca7de2..00220d4078a 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/DataGrid.js
@@ -12,6 +12,8 @@ import { getRows } from "./redux/store";
import { updateSelected } from "./redux/nodes";
import { setGraphData } from "./redux/graphData";
import { setNodeInfos } from "./redux/nodeInfo";
+import { setLinks } from "./redux/links";
+import { setLinksTrans } from "./redux/linksTrans";
function componentToHex(c) {
var hex = c.toString(16);
@@ -90,10 +92,13 @@ const DataGrid = ({
updateSelected,
classes,
setGraphData,
+ setLinks,
+ setLinksTrans,
selectedGraph,
setNodeInfos,
selectedNodes,
searchedNodes,
+ showTransitive
}) => {
const [checkBoxes, setCheckBoxes] = React.useState([]);
@@ -103,31 +108,36 @@ const DataGrid = ({
function newGraphData() {
let gitHash = selectedGraph;
- let postData = {
- "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node)
- };
- fetch('/api/graphs/' + gitHash + '/d3', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setGraphData(data.graphData);
- });
- fetch('/api/graphs/' + gitHash + '/nodes/details', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setNodeInfos(data.nodeInfos);
- });
+ if (gitHash) {
+ let postData = {
+ "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node),
+ "transitive_edges": showTransitive
+ };
+ fetch('/api/graphs/' + gitHash + '/d3', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setGraphData(data.graphData);
+ setLinks(data.graphData.links);
+ setLinksTrans(data.graphData.links_trans);
+ });
+ fetch('/api/graphs/' + gitHash + '/nodes/details', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setNodeInfos(data.nodeInfos);
+ });
+ }
}
const getRowClassName = ({ index }) => {
@@ -249,6 +259,6 @@ const DataGrid = ({
);
};
-export default connect(getRows, { updateSelected, setGraphData, setNodeInfos })(
+export default connect(getRows, { updateSelected, setGraphData, setNodeInfos, setLinks, setLinksTrans })(
withStyles(styles)(DataGrid)
);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js
index 2e1b429928a..b0724498b71 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/DrawGraph.js
@@ -6,6 +6,8 @@ import ForceGraph3D from "react-force-graph-3d";
import SwitchComponents from "./SwitchComponent";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
+import FormControlLabel from "@material-ui/core/FormControlLabel";
+import Checkbox from "@material-ui/core/Checkbox";
import theme from "./theme";
import { getGraphData } from "./redux/store";
@@ -13,6 +15,9 @@ import { updateCheckbox } from "./redux/nodes";
import { setFindNode } from "./redux/findNode";
import { setGraphData } from "./redux/graphData";
import { setNodeInfos } from "./redux/nodeInfo";
+import { setLinks } from "./redux/links";
+import { setLinksTrans } from "./redux/linksTrans";
+import { setShowTransitive } from "./redux/showTransitive";
import LoadingBar from "./LoadingBar";
const handleFindNode = (node_value, graphData, activeComponent, forceRef) => {
@@ -55,6 +60,7 @@ const DrawGraph = ({
size,
graphData,
nodes,
+ links,
loading,
graphPaths,
updateCheckbox,
@@ -62,18 +68,29 @@ const DrawGraph = ({
setFindNode,
setGraphData,
setNodeInfos,
- selectedGraph
+ selectedGraph,
+ setLinks,
+ setLinksTrans,
+ setShowTransitive,
+ showTransitive
}) => {
const [activeComponent, setActiveComponent] = React.useState("2D");
const [pathNodes, setPathNodes] = React.useState({});
const [pathEdges, setPathEdges] = React.useState([]);
const forceRef = useRef(null);
+ const PARTICLE_SIZE = 5;
+
React.useEffect(() => {
handleFindNode(findNode, graphData, activeComponent, forceRef);
setFindNode("");
}, [findNode, graphData, activeComponent, forceRef]);
+ React.useEffect(() => {
+ newGraphData();
+ }, [showTransitive]);
+
+ const selectedEdge = links.filter(link => link.selected == true)[0];
const selectedNodes = nodes.filter(node => node.selected == true).map(node => node.node);
React.useEffect(() => {
@@ -109,31 +126,36 @@ const DrawGraph = ({
function newGraphData() {
let gitHash = selectedGraph;
- let postData = {
- "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node)
- };
- fetch('/api/graphs/' + gitHash + '/d3', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setGraphData(data.graphData);
- });
- fetch('/api/graphs/' + gitHash + '/nodes/details', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setNodeInfos(data.nodeInfos);
- });
+ if (gitHash) {
+ let postData = {
+ "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node),
+ "transitive_edges": showTransitive
+ };
+ fetch('/api/graphs/' + gitHash + '/d3', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setGraphData(data.graphData);
+ setLinks(data.graphData.links);
+ setLinksTrans(data.graphData.links_trans);
+ });
+ fetch('/api/graphs/' + gitHash + '/nodes/details', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setNodeInfos(data.nodeInfos);
+ });
+ }
}
const paintRing = React.useCallback(
@@ -166,6 +188,20 @@ const DrawGraph = ({
}
}
+ function isSameEdge(edgeA, edgeB) {
+ if (edgeA.source.id && edgeA.target.id) {
+ if (edgeB.source.id && edgeB.target.id) {
+ return (edgeA.source.id == edgeB.source.id &&
+ edgeA.target.id == edgeB.target.id);
+ }
+ }
+ if (edgeA.source == edgeB.source &&
+ edgeA.target == edgeB.target) {
+ return true;
+ }
+ return false;
+ }
+
return (
<LoadingBar loading={loading} height={"100%"}>
<Button
@@ -191,6 +227,15 @@ const DrawGraph = ({
);
}}
/>
+ <FormControlLabel
+ style={{ marginInline: 5 }}
+ control={<Checkbox
+ style={{ marginInline: 10 }}
+ checked={ showTransitive }
+ onClick={ () => setShowTransitive(!showTransitive) }
+ />}
+ label="Show Viewable Transitive Edges"
+ />
<SwitchComponents active={activeComponent}>
<ForceGraph2D
name="3D"
@@ -215,10 +260,15 @@ const DrawGraph = ({
pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
pathEdges[graphPaths.selectedPath][i].target == d.target.id
) {
- return 5;
+ return PARTICLE_SIZE;
}
}
}
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, d)) {
+ return PARTICLE_SIZE;
+ }
+ }
return 0;
}}
linkDirectionalParticleSpeed={(d) => {
@@ -229,7 +279,18 @@ const DrawGraph = ({
return "before";
}
}}
+ linkLineDash={(d) => {
+ if (d.data.direct) {
+ return [];
+ }
+ return [5, 3];
+ }}
linkColor={(d) => {
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, d)) {
+ return "#ED7811";
+ }
+ }
if (graphPaths.selectedPath >= 0) {
for (
var i = 0;
@@ -248,6 +309,11 @@ const DrawGraph = ({
}}
linkDirectionalParticleWidth={6}
linkWidth={(d) => {
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, d)) {
+ return 2;
+ }
+ }
if (graphPaths.selectedPath >= 0) {
for (
var i = 0;
@@ -264,6 +330,29 @@ const DrawGraph = ({
}
return 1;
}}
+ onLinkClick={(link, event) => {
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, link)) {
+ setLinks(
+ links.map((temp_link) => {
+ temp_link.selected = false;
+ return temp_link;
+ })
+ );
+ return;
+ }
+ }
+ setLinks(
+ links.map((temp_link, index) => {
+ if (index == link.index) {
+ temp_link.selected = true;
+ } else {
+ temp_link.selected = false;
+ }
+ return temp_link;
+ })
+ );
+ }}
nodeRelSize={7}
nodeCanvasObject={paintRing}
onNodeClick={(node, event) => {
@@ -309,7 +398,15 @@ const DrawGraph = ({
}
}
}
- return "#FAFAFA";
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, d)) {
+ return "#ED7811";
+ }
+ }
+ if (d.data.direct == false) {
+ return "#303030";
+ }
+ return "#FFFFFF";
}}
linkDirectionalParticleWidth={7}
linkWidth={(d) => {
@@ -323,10 +420,15 @@ const DrawGraph = ({
pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
pathEdges[graphPaths.selectedPath][i].target == d.target.id
) {
- return 5;
+ return 3;
}
}
}
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, d)) {
+ return 3;
+ }
+ }
return 1;
}}
linkDirectionalParticles={(d) => {
@@ -340,10 +442,15 @@ const DrawGraph = ({
pathEdges[graphPaths.selectedPath][i].source == d.source.id &&
pathEdges[graphPaths.selectedPath][i].target == d.target.id
) {
- return 5;
+ return PARTICLE_SIZE;
}
}
}
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, d)) {
+ return PARTICLE_SIZE;
+ }
+ }
return 0;
}}
linkDirectionalParticleSpeed={(d) => {
@@ -351,6 +458,29 @@ const DrawGraph = ({
}}
linkDirectionalParticleResolution={10}
linkOpacity={0.6}
+ onLinkClick={(link, event) => {
+ if (selectedEdge) {
+ if (isSameEdge(selectedEdge, link)) {
+ setLinks(
+ links.map((temp_link) => {
+ temp_link.selected = false;
+ return temp_link;
+ })
+ );
+ return;
+ }
+ }
+ setLinks(
+ links.map((temp_link, index) => {
+ if (index == link.index) {
+ temp_link.selected = true;
+ } else {
+ temp_link.selected = false;
+ }
+ return temp_link;
+ })
+ );
+ }}
nodeRelSize={7}
backgroundColor={theme.palette.secondary.dark}
linkDirectionalArrowLength={3.5}
@@ -362,6 +492,6 @@ const DrawGraph = ({
);
};
-export default connect(getGraphData, { setFindNode, updateCheckbox, setGraphData, setNodeInfos })(
+export default connect(getGraphData, { setFindNode, updateCheckbox, setGraphData, setNodeInfos, setLinks, setLinksTrans, setShowTransitive })(
DrawGraph
);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/EdgeList.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/EdgeList.js
new file mode 100644
index 00000000000..28226bc6206
--- /dev/null
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/EdgeList.js
@@ -0,0 +1,261 @@
+import React, { useState } from "react";
+import clsx from "clsx";
+import { connect } from "react-redux";
+import { getEdges } from "./redux/store";
+import { setFindNode } from "./redux/findNode";
+import { setLinks } from "./redux/links";
+import { setGraphData } from "./redux/graphData";
+import { setSelectedPath } from "./redux/graphPaths";
+import { AutoSizer, Column, Table } from "react-virtualized";
+import TableCell from "@material-ui/core/TableCell";
+import Typography from "@material-ui/core/Typography";
+import Tooltip from '@material-ui/core/Tooltip';
+import GraphPaths from "./GraphPaths";
+
+import { makeStyles, withStyles } from "@material-ui/core/styles";
+
+import LoadingBar from "./LoadingBar";
+import TextField from "@material-ui/core/TextField";
+import { List, ListItemText, Paper, Button } from "@material-ui/core";
+
+const columns = [
+ { dataKey: "type", label: "Type", width: 30 },
+ { dataKey: "source", label: "From", width: 180 },
+ { dataKey: "to", label: "➔", width: 40 },
+ { dataKey: "target", label: "To", width: 180 },
+];
+
+const visibilityTypes = ['Global', 'Public', 'Private', 'Interface'];
+
+function componentToHex(c) {
+ var hex = c.toString(16);
+ return hex.length == 1 ? "0" + hex : hex;
+ }
+
+ function rgbToHex(r, g, b) {
+ return "#" + componentToHex(r) + componentToHex(g) + componentToHex(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) {
+ 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;
+ }
+
+ 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) => ({
+ flexContainer: {
+ display: "flex",
+ alignItems: "center",
+ },
+ table: {
+ // temporary right-to-left patch, waiting for
+ // https://github.com/bvaughn/react-virtualized/issues/454
+ "& .ReactVirtualized__Table__headerRow": {
+ flip: false,
+ paddingRight: theme.direction === "rtl" ? "0 !important" : undefined,
+ },
+ },
+ tableRowOdd: {
+ backgroundColor: incrementPallete(theme.palette.grey[800], 10),
+ },
+ tableRowEven: {
+ backgroundColor: theme.palette.grey[800],
+ },
+ tableRowHover: {
+ "&:hover": {
+ backgroundColor: theme.palette.grey[600],
+ },
+ },
+ tableCell: {
+ flex: 1,
+ },
+ noClick: {
+ cursor: "initial",
+ },
+ });
+
+const EdgeList = ({ selectedGraph, links, setLinks, linksTrans, loading, setFindNode, classes, setTransPath }) => {
+ const [searchTerm, setSearchTerm] = useState('');
+
+ const selectedLinks = links.filter(link => link.selected);
+
+ function searchedLinks() {
+ if (searchTerm == '') {
+ return links;
+ }
+ return links.filter(link => {
+ if (link.source.name && link.target.name) {
+ return link.source.name.indexOf(searchTerm) > -1 || link.target.name.indexOf(searchTerm) > -1;
+ }});
+ }
+
+ function handleRowClick(event) {
+ setLinks(
+ links.map((temp_link, index) => {
+ if (index == searchedLinks()[event.index].index) {
+ temp_link.selected = !temp_link.selected;
+ } else {
+ temp_link.selected = false;
+ }
+ return temp_link;
+ })
+ );
+ setTransPath(event, '', '');
+ }
+
+ function handleSearchTermChange(event) {
+ setSearchTerm(event.target.value);
+ }
+
+ function reduceNodeName(node) {
+ if (node.name) {
+ return node.name;
+ }
+ return node.substring(node.lastIndexOf('/') + 1);
+ }
+
+ const getRowClassName = ({ index }) => {
+ return clsx(
+ index % 2 == 0 ? styles.tableRowEven : classes.tableRowOdd,
+ classes.flexContainer,
+ {
+ [classes.tableRowHover]: index !== -1,
+ }
+ );
+ };
+
+ const cellRenderer = ({ cellData, columnIndex, rowIndex }) => {
+
+ return (
+ <TableCell
+ component="div"
+ >
+ { columnIndex == 0 ?
+ ( searchedLinks()[rowIndex].data?.direct ?
+ <Tooltip title="DIRECT" placement="right" arrow><p>D</p></Tooltip>
+ :
+ <Tooltip title="TRANSITIVE" placement="right" arrow><p>T</p></Tooltip>
+ )
+ :
+ ""
+ }
+ { columnIndex == 1 ? reduceNodeName(searchedLinks()[rowIndex].source) : "" }
+ { columnIndex == 2 ? (searchedLinks()[rowIndex].selected ? <span style={{ color: "#ED7811" }}>➔</span> : "➔") : "" }
+ { columnIndex == 3 ? reduceNodeName(searchedLinks()[rowIndex].target) : "" }
+ </TableCell>
+ );
+ };
+
+ const headerRenderer = ({ label, columnIndex }) => {
+ return (
+ <TableCell
+ component="div"
+ >
+ <Typography
+ style={{ width: "100%" }}
+ align="left"
+ variant="caption"
+ component="h2"
+ >
+ {label}
+ </Typography>
+ </TableCell>
+ );
+ };
+
+ return (
+ <LoadingBar loading={loading} height={"95%"}>
+ <TextField
+ fullWidth
+ onChange={handleSearchTermChange}
+ onClick={(event)=> event.target.select()}
+ label="Search for Edge"
+ />
+ <div style={{ height: "30%" }}>
+ <AutoSizer>
+ {({ height, width }) => (
+ <Table
+ height={height}
+ width={width}
+ rowCount={searchedLinks().length}
+ rowGetter={({ index }) => searchedLinks()[index].target}
+ rowHeight={25}
+ onRowClick={handleRowClick}
+ gridStyle={{
+ direction: "inherit",
+ }}
+ size={"small"}
+ rowClassName={getRowClassName}
+ headerHeight={35}
+ >
+ {columns.map(({ dataKey, ...other }, index) => {
+ return (
+ <Column
+ key={dataKey}
+ headerRenderer={(headerProps) =>
+ headerRenderer({
+ ...headerProps,
+ columnIndex: index,
+ })
+ }
+ cellRenderer={cellRenderer}
+ dataKey={dataKey}
+ {...other}
+ />
+ );
+ })}
+ </Table>
+ )}
+ </AutoSizer>
+ </div>
+ <Paper style={{ border: "2px solid", height: "55%", padding: 5, overflow: 'auto' }} hidden={(selectedLinks.length <= 0)}>
+ <List dense={true} style={{ padding: 5 }}>
+ <Paper elevation={3} style={{ backgroundColor: "rgba(33, 33, 33)", padding: 15 }}>
+ <h4 style={{ margin: 0 }}>{ selectedLinks[0]?.source.name } ➔ { selectedLinks[0]?.target.name }</h4>
+ <ListItemText primary={ <span><strong>Type:</strong> { selectedLinks[0]?.data.direct ? "Direct" : "Transitive" }</span> }/>
+ <ListItemText primary={ <span><strong>Visibility:</strong> { visibilityTypes[selectedLinks[0]?.data.visibility] }</span> }/>
+ <ListItemText primary={ <span><strong>Source:</strong> { selectedLinks[0]?.source.id }</span> } secondary= { selectedLinks[0]?.source.type }/>
+ <ListItemText primary={ <span><strong>Target:</strong> { selectedLinks[0]?.target.id }</span> } secondary= { selectedLinks[0]?.target.type }/>
+ <div>
+ <ListItemText primary={ <strong>Symbol Dependencies: { selectedLinks[0]?.data.symbols.length }</strong> }/>
+ { selectedLinks[0]?.data.symbols.map((symbol, index) => {
+ return (
+ <span key={index}>
+ <ListItemText secondary={ symbol } style={{textIndent: "-1em", marginLeft: "1em", overflowWrap: "break-word"}}></ListItemText>
+ <hr style={{ border: "0px", borderTop: "0.5px solid rgba(255, 255, 255, .2)", marginTop: "2px", marginBottom: "2px"}}></hr>
+ </span>
+ );
+ })
+ }
+ </div>
+ <div hidden={(selectedLinks[0]?.data.direct ? "Direct" : "Transitive") == "Direct" }>
+ <br></br>
+ <Button variant="contained" onClick={ (event) => setTransPath(event, selectedLinks[0]?.source.id, selectedLinks[0]?.target.id) }>View Paths</Button>
+ </div>
+ </Paper>
+ </List>
+ </Paper>
+ </LoadingBar>
+ );
+};
+
+export default connect(getEdges, { setGraphData, setFindNode, setLinks, setSelectedPath })(withStyles(styles)(EdgeList)); \ No newline at end of file
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfo.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfo.js
index b5f3cec93eb..5863d2bea9d 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfo.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfo.js
@@ -25,11 +25,13 @@ const useStyles = makeStyles({
const GraphInfo = ({ selectedGraph, counts, datawidth, setCounts }) => {
React.useEffect(() => {
let gitHash = selectedGraph;
- fetch('/api/graphs/' + gitHash + '/analysis')
- .then(response => response.json())
- .then(data => {
- setCounts(data.results);
- });
+ if (gitHash) {
+ fetch('/api/graphs/' + gitHash + '/analysis')
+ .then(response => response.json())
+ .then(data => {
+ setCounts(data.results);
+ });
+ }
}, [selectedGraph]);
const classes = useStyles();
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js
index 3636581f375..671a8d775af 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphInfoTabs.js
@@ -5,6 +5,7 @@ import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import NodeList from "./NodeList";
+import EdgeList from "./EdgeList";
import InfoExpander from "./InfoExpander";
import AlgorithmExpander from "./AlgorithmExpander";
@@ -26,17 +27,27 @@ const useStyles = makeStyles((theme) => ({
export default function GraphInfoTabs({ nodes, width }) {
const classes = useStyles();
- const [value, setValue] = React.useState(0);
+ const [tab, setTab] = React.useState(1);
+ const [transPathFrom, setTransPathFrom] = React.useState('');
+ const [transPathTo, setTransPathTo] = React.useState('');
const handleChange = (event, newValue) => {
- setValue(newValue);
+ setTab(newValue);
+ };
+
+ const handleTransPath = (event, fromNode, toNode) => {
+ setTransPathFrom(fromNode);
+ setTransPathTo(toNode);
+ if (fromNode != '' && toNode != '') {
+ setTab(3);
+ }
};
return (
<div className={classes.root}>
<AppBar position="static" color="default">
<Tabs
- value={value}
+ value={tab}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
@@ -50,15 +61,17 @@ export default function GraphInfoTabs({ nodes, width }) {
<Tab label="Algorithms" {...a11yProps(3)} />
</Tabs>
</AppBar>
- <div style={{ height: "100%" }} hidden={value != 0}>
+ <div style={{ height: "100%" }} hidden={tab != 0}>
<InfoExpander width={width}></InfoExpander>
</div>
- <div style={{ height: "100%" }} hidden={value != 1}>
+ <div style={{ height: "100%" }} hidden={tab != 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 style={{ height: "100%" }} hidden={tab != 2}>
+ <EdgeList nodes={nodes} setTransPath={handleTransPath}></EdgeList>
+ </div>
+ <div style={{ height: "100%" }} hidden={tab != 3}>
+ <AlgorithmExpander width={width} transPathFrom={transPathFrom} transPathTo={transPathTo}></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
index 39d35aeb50d..ead5fd4245d 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/GraphPaths.js
@@ -17,6 +17,8 @@ import useResizeAware from "react-resize-aware";
import { getSelected } from "./redux/store";
import { selectedGraphPaths, setSelectedPath } from "./redux/graphPaths";
import { setGraphData } from "./redux/graphData";
+import { setLinks } from "./redux/links";
+import { setLinksTrans } from "./redux/linksTrans";
import OverflowTooltip from "./OverflowTooltip";
@@ -63,7 +65,21 @@ const AccordionDetails = withStyles((theme) => ({
},
}))(MuiAccordionDetails);
-const GraphPaths = ({ nodes, selectedGraph, selectedNodes, graphPaths, setSelectedPath, width, selectedGraphPaths, setGraphData }) => {
+const GraphPaths = ({
+ nodes,
+ selectedGraph,
+ selectedNodes,
+ graphPaths,
+ setSelectedPath,
+ width,
+ selectedGraphPaths,
+ setGraphData,
+ setLinks,
+ setLinksTrans,
+ showTransitive,
+ transPathFrom,
+ transPathTo
+}) => {
const [fromNode, setFromNode] = React.useState("");
const [toNode, setToNode] = React.useState("");
const [fromNodeId, setFromNodeId] = React.useState(0);
@@ -89,39 +105,71 @@ const GraphPaths = ({ nodes, selectedGraph, selectedNodes, graphPaths, setSelect
},
}));
const classes = useStyles();
+
+ React.useEffect(() => {
+ setFromNode(transPathFrom);
+ setFromNodeExpanded(false);
+ setToNode(transPathFrom);
+ setToNodeExpanded(false);
+ setPaneSize("50%");
+ if (transPathFrom != '' && transPathTo != '') {
+ getGraphPaths(transPathFrom, transPathTo);
+ } else {
+ selectedGraphPaths({
+ fromNode: '',
+ toNode: '',
+ paths: [],
+ selectedPath: -1
+ });
+ }
+ }, [transPathFrom, transPathTo]);
function getGraphPaths(fromNode, toNode) {
let gitHash = selectedGraph;
- let postData = {
- "fromNode": fromNode,
- "toNode": toNode
- };
- fetch('/api/graphs/' + gitHash + '/paths', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- selectedGraphPaths(data);
- let postData = {
- "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node),
- "extra_nodes": data.extraNodes
- };
- fetch('/api/graphs/' + gitHash + '/d3', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setGraphData(data.graphData);
- });
- });
+ if (gitHash) {
+ let postData = {
+ "fromNode": fromNode,
+ "toNode": toNode
+ };
+ fetch('/api/graphs/' + gitHash + '/paths', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ selectedGraphPaths(data);
+ let postData = {
+ "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node),
+ "extra_nodes": data.extraNodes,
+ "transitive_edges": showTransitive
+ };
+ fetch('/api/graphs/' + gitHash + '/d3', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setGraphData(data.graphData);
+ setLinks(
+ data.graphData.links.map((link) => {
+ if (link.source == fromNode && link.target == toNode) {
+ link.selected = true;
+ } else {
+ link.selected = false;
+ }
+ return link;
+ })
+ );
+ setLinksTrans(data.graphData.links_trans);
+ });
+ });
+ }
}
function toNodeRow({ index, style, data }) {
@@ -315,6 +363,6 @@ const GraphPaths = ({ nodes, selectedGraph, selectedNodes, graphPaths, setSelect
);
};
-export default connect(getSelected, { selectedGraphPaths, setSelectedPath, setGraphData })(
+export default connect(getSelected, { selectedGraphPaths, setSelectedPath, setGraphData, setLinks, setLinksTrans })(
GraphPaths
);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/NodeList.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/NodeList.js
index 7a385853ed6..bdcc5755f43 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/NodeList.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/NodeList.js
@@ -9,8 +9,10 @@ import LoadingBar from "./LoadingBar";
import TextField from "@material-ui/core/TextField";
import { setNodes, updateCheckbox, updateSelected } from "./redux/nodes";
+import { setNodeInfos } from "./redux/nodeInfo";
import { setGraphData } from "./redux/graphData";
-import { addLinks } from "./redux/links";
+import { setLinks } from "./redux/links";
+import { setLinksTrans } from "./redux/linksTrans";
import { setLoading } from "./redux/loading";
import { setListSearchTerm } from "./redux/listSearchTerm";
import { Button, Autocomplete, Grid } from "@material-ui/core";
@@ -21,57 +23,63 @@ const columns = [
{ id: "ID", dataKey: "node", label: "Node", width: 200 },
];
-const NodeList = ({ selectedGraph, nodes, searchedNodes, loading, setFindNode, setNodes, addLinks, setLoading, setListSearchTerm, updateCheckbox, updateSelected, setGraphData}) => {
+const NodeList = ({ selectedGraph, nodes, searchedNodes, loading, setFindNode, setNodeInfos, setNodes, setLinks, setLinksTrans, setLoading, setListSearchTerm, updateCheckbox, updateSelected, setGraphData, showTransitive}) => {
const [searchPath, setSearchPath] = React.useState('');
React.useEffect(() => {
let gitHash = selectedGraph;
- fetch('/api/graphs/' + gitHash + '/nodes')
- .then(response => response.json())
- .then(data => {
- setNodes(data.nodes.map((node, index) => {
- return {
- id: index,
- node: node,
- name: node.substring(node.lastIndexOf('/') + 1),
- check: "checkbox",
- selected: false,
- };
- }));
- addLinks(data.links);
- setLoading(false);
- });
- setSearchPath(null);
- setListSearchTerm('');
+ if (gitHash) {
+ fetch('/api/graphs/' + gitHash + '/nodes')
+ .then(response => response.json())
+ .then(data => {
+ setNodes(data.nodes.map((node, index) => {
+ return {
+ id: index,
+ node: node,
+ name: node.substring(node.lastIndexOf('/') + 1),
+ check: "checkbox",
+ selected: false,
+ };
+ }));
+ setLoading(false);
+ });
+ setSearchPath(null);
+ setListSearchTerm('');
+ }
}, [selectedGraph]);
function newGraphData() {
let gitHash = selectedGraph;
- let postData = {
- "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node)
- };
- fetch('/api/graphs/' + gitHash + '/d3', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setGraphData(data.graphData);
- });
- fetch('/api/graphs/' + gitHash + '/nodes/details', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- setNodeInfos(data.nodeInfos);
- });
+ if (gitHash) {
+ let postData = {
+ "selected_nodes": nodes.filter(node => node.selected == true).map(node => node.node),
+ "transitive_edges": showTransitive
+ };
+ fetch('/api/graphs/' + gitHash + '/d3', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setGraphData(data.graphData);
+ setLinks(data.graphData.links);
+ setLinksTrans(data.graphData.links_trans);
+ });
+ fetch('/api/graphs/' + gitHash + '/nodes/details', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ setNodeInfos(data.nodeInfos);
+ });
+ }
}
function nodePaths() {
@@ -102,7 +110,7 @@ const NodeList = ({ selectedGraph, nodes, searchedNodes, loading, setFindNode, s
function handleSearchTermChange(event, newTerm) {
if (newTerm == null) {
setSearchPath('');
- setListSearchTerm(newTerm);
+ setListSearchTerm('');
} else {
setSearchPath(newTerm);
setListSearchTerm(newTerm);
@@ -125,7 +133,6 @@ const NodeList = ({ selectedGraph, nodes, searchedNodes, loading, setFindNode, s
renderInput={(params) => <TextField {...params}
label="Search by Path or Name"
variant="outlined"
- onClick={(event) => event.target.select()}
/>}
/>
</Grid>
@@ -167,4 +174,4 @@ const NodeList = ({ selectedGraph, nodes, searchedNodes, loading, setFindNode, s
);
};
-export default connect(getNodes, { setFindNode, setNodes, addLinks, setLoading, setListSearchTerm, updateCheckbox, updateSelected, setGraphData })(NodeList); \ No newline at end of file
+export default connect(getNodes, { setFindNode, setNodes, setNodeInfos, setLinks, setLinksTrans, setLoading, setListSearchTerm, updateCheckbox, updateSelected, setGraphData })(NodeList); \ No newline at end of file
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/OverflowTooltip.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/OverflowTooltip.js
index d41891364a1..c09ec44f319 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/OverflowTooltip.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/OverflowTooltip.js
@@ -11,6 +11,8 @@ import { updateCheckbox } from "./redux/nodes";
import { setGraphData } from "./redux/graphData";
import { setNodeInfos } from "./redux/nodeInfo";
import { getGraphData } from "./redux/store";
+import { setLinks } from "./redux/links";
+import { setLinksTrans } from "./redux/linksTrans";
const OverflowTip = (props) => {
const textElementRef = useRef(null);
@@ -26,31 +28,36 @@ const OverflowTip = (props) => {
function newGraphData() {
let gitHash = props.selectedGraph;
- let postData = {
- "selected_nodes": props.nodes.filter(node => node.selected == true).map(node => node.node)
- };
- fetch('/api/graphs/' + gitHash + '/d3', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- props.setGraphData(data.graphData);
- });
- fetch('/api/graphs/' + gitHash + '/nodes/details', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(postData)
- })
- .then(response => response.json())
- .then(data => {
- props.setNodeInfos(data.nodeInfos);
- });
+ if (gitHash) {
+ let postData = {
+ "selected_nodes": props.nodes.filter(node => node.selected == true).map(node => node.node),
+ "transitive_edges": props.showTransitive
+ };
+ fetch('/api/graphs/' + gitHash + '/d3', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ props.setGraphData(data.graphData);
+ props.setLinks(data.graphData.links);
+ props.setLinksTrans(data.graphData.links_trans);
+ });
+ fetch('/api/graphs/' + gitHash + '/nodes/details', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(postData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ props.setNodeInfos(data.nodeInfos);
+ });
+ }
}
useEffect(() => {
@@ -98,4 +105,4 @@ const OverflowTip = (props) => {
);
};
-export default connect(getGraphData, { updateCheckbox, setGraphData, setNodeInfos })(OverflowTip);
+export default connect(getGraphData, { updateCheckbox, setGraphData, setNodeInfos, setLinks, setLinksTrans })(OverflowTip);
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/links.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/links.js
index 04d0d3d2de8..9d47584b7f5 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/links.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/links.js
@@ -2,15 +2,31 @@ import { initialState } from "./store";
export const links = (state = initialState, action) => {
switch (action.type) {
- case "addLinks":
+ case "addLink":
+ var arr = Object.assign(state);
+ return [...arr, action.payload];
+ case "setLinks":
return action.payload;
-
+ case "updateSelectedLinks":
+ var newState = Object.assign(state);
+ newState[action.payload.index].selected = action.payload.value;
+ return newState;
default:
return state;
}
};
-export const addLinks = (links) => ({
- type: "addLinks",
+export const addLink = (link) => ({
+ type: "addLink",
+ payload: link,
+});
+
+export const setLinks = (links) => ({
+ type: "setLinks",
payload: links,
});
+
+export const updateSelectedLinks = (newValue) => ({
+ type: "updateSelectedLinks",
+ payload: newValue,
+});
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/linksTrans.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/linksTrans.js
new file mode 100644
index 00000000000..f0cff77a892
--- /dev/null
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/linksTrans.js
@@ -0,0 +1,32 @@
+import { initialState } from "./store";
+
+export const linksTrans = (state = initialState, action) => {
+ switch (action.type) {
+ case "addLinkTrans":
+ var arr = Object.assign(state);
+ return [...arr, action.payload];
+ case "setLinksTrans":
+ return action.payload;
+ case "updateSelectedLinksTrans":
+ var newState = Object.assign(state);
+ newState[action.payload.index].selected = action.payload.value;
+ return newState;
+ default:
+ return state;
+ }
+};
+
+export const addLinkTrans = (link) => ({
+ type: "addLinkTrans",
+ payload: link,
+});
+
+export const setLinksTrans = (links) => ({
+ type: "setLinksTrans",
+ payload: links,
+});
+
+export const updateSelectedLinksTrans = (newValue) => ({
+ type: "updateSelectedLinksTrans",
+ payload: newValue,
+});
diff --git a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/showTransitive.js b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/showTransitive.js
new file mode 100644
index 00000000000..05e1f727275
--- /dev/null
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/showTransitive.js
@@ -0,0 +1,16 @@
+import { initialState } from "./store";
+
+export const showTransitive = (state = initialState, action) => {
+ switch (action.type) {
+ case "setShowTransitive":
+ return action.payload;
+
+ default:
+ return state;
+ }
+};
+
+export const setShowTransitive = (showTransitive) => ({
+ type: "setShowTransitive",
+ payload: showTransitive,
+});
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 4ef6b2e74d5..f12517980b8 100644
--- a/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js
+++ b/buildscripts/libdeps/graph_visualizer_web_stack/src/redux/store.js
@@ -5,6 +5,8 @@ import { counts } from "./counts";
import { nodeInfo } from "./nodeInfo";
import { loading } from "./loading";
import { links } from "./links";
+import { linksTrans } from "./linksTrans";
+import { showTransitive } from "./showTransitive";
import { graphData } from "./graphData";
import { findNode } from "./findNode";
import { graphPaths } from "./graphPaths";
@@ -13,25 +15,18 @@ import { listSearchTerm } from "./listSearchTerm";
export const initialState = {
loading: false,
graphFiles: [
- // {id: 0, value: 'graphfile.graphml', version: 1, git: '1234567', selected: false}
+ // { id: 0, value: 'graphfile.graphml', version: 1, git: '1234567', selected: false }
],
nodes: [
- {
- id: 0,
- node: "test/test1.so",
- name: "test1",
- check: "checkbox",
- selected: false,
- },
- {
- id: 1,
- node: "test/test2.so",
- name: "test2",
- check: "checkbox",
- selected: false,
- },
+ // { id: 0, node: "test/test1.so", name: "test1", check: "checkbox", selected: false }
+ ],
+ links: [
+ // { source: "test/test1.so", target: "test/test2.so" }
+ ],
+ linksTrans: [
+ // { source: "test/test1.so", target: "test/test2.so" }
],
- links: [{ source: "test/test1.so", target: "test/test2.so" }],
+ showTransitive: false,
graphData: {
nodes: [
// {id: 'test/test1.so', name: 'test1.so'},
@@ -67,7 +62,7 @@ export const initialState = {
export const getCurrentGraphHash = (state) => {
let selectedGraphFiles = state.graphFiles.filter(x => x.selected == true);
- let selectedGraph = '0000000';
+ let selectedGraph = undefined;
if (selectedGraphFiles.length > 0) {
selectedGraph = selectedGraphFiles[0].git;
}
@@ -107,7 +102,8 @@ export const getRows = (state) => {
rowGetter: ({ index }) => searchedNodes[index],
checkBox: ({ index }) => searchedNodes[index].selected,
nodes: state.nodes,
- searchedNodes: searchedNodes
+ searchedNodes: searchedNodes,
+ showTransitive: state.showTransitive,
};
};
@@ -116,9 +112,11 @@ export const getSelected = (state) => {
selectedGraph: getCurrentGraphHash(state),
selectedNodes: state.nodes.filter((node) => node.selected),
nodes: state.nodes,
+ links: state.links,
selectedEdges: [],
loading: state.loading,
graphPaths: state.graphPaths,
+ showTransitive: state.showTransitive,
};
};
@@ -129,6 +127,19 @@ export const getNodes = (state) => {
loading: state.loading,
listSearchTerm: state.listSearchTerm,
searchedNodes: state.nodes.filter(node => node.node.indexOf(state.listSearchTerm) > -1),
+ showTransitive: state.showTransitive
+ };
+};
+
+export const getEdges = (state) => {
+ return {
+ selectedGraph: getCurrentGraphHash(state),
+ nodes: state.nodes,
+ links: state.links,
+ linksTrans: state.linksTrans,
+ selectedLinks: state.links.filter(link => link.selected == true),
+ searchedNodes: state.nodes.filter(node => node.node.indexOf(state.listSearchTerm) > -1),
+ showTransitive: state.showTransitive,
};
};
@@ -136,10 +147,12 @@ export const getGraphData = (state) => {
return {
selectedGraph: getCurrentGraphHash(state),
nodes: state.nodes,
+ links: state.links,
graphData: state.graphData,
loading: state.loading,
findNode: state.findNode,
graphPaths: state.graphPaths,
+ showTransitive: state.showTransitive,
};
};
@@ -155,10 +168,12 @@ const store = createStore(
graphFiles,
loading,
links,
+ linksTrans,
graphData,
findNode,
graphPaths,
listSearchTerm,
+ showTransitive
}),
initialState
);
diff --git a/site_scons/libdeps.py b/site_scons/libdeps.py
index 767f2a7baf4..9cb445bd257 100644
--- a/site_scons/libdeps.py
+++ b/site_scons/libdeps.py
@@ -63,6 +63,7 @@ import textwrap
import hashlib
import json
import fileinput
+import subprocess
try:
import networkx
@@ -1265,7 +1266,9 @@ def generate_graph(env, target, source):
libdeps_graph = env.GetLibdepsGraph()
+ demangled_symbols = {}
for symbol_deps_file in source:
+
with open(str(symbol_deps_file)) as f:
symbols = {}
try:
@@ -1279,16 +1282,26 @@ def generate_graph(env, target, source):
except json.JSONDecodeError:
env.FatalError(f"Failed processing json file: {str(symbol_deps_file)}")
- for libdep in symbols:
- from_node = os.path.abspath(str(symbol_deps_file)[:-len(env['SYMBOLDEPSSUFFIX'])])
- to_node = os.path.abspath(libdep).strip()
- libdeps_graph.add_edges_from([(
- from_node,
- to_node,
- {EdgeProps.symbols.name: " ".join(symbols[libdep])},
- )])
- node = env.File(str(symbol_deps_file)[:-len(env['SYMBOLDEPSSUFFIX'])])
- add_node_from(env, node)
+ demangled_symbols[str(symbol_deps_file)] = symbols
+
+ p1 = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ stdout, stderr = p1.communicate(json.dumps(demangled_symbols).encode('utf-8'))
+ demangled_symbols = json.loads(stdout.decode("utf-8"))
+
+ for deps_file in demangled_symbols:
+
+ for libdep in demangled_symbols[deps_file]:
+
+ from_node = os.path.abspath(str(deps_file)[:-len(env['SYMBOLDEPSSUFFIX'])])
+ to_node = os.path.abspath(libdep).strip()
+ libdeps_graph.add_edges_from([(
+ from_node,
+ to_node,
+ {EdgeProps.symbols.name: "\n".join(demangled_symbols[deps_file][libdep])},
+ )])
+ node = env.File(str(deps_file)[:-len(env['SYMBOLDEPSSUFFIX'])])
+ add_node_from(env, node)
libdeps_graph_file = f"{env.Dir('$BUILD_DIR').path}/libdeps/libdeps.graphml"
networkx.write_graphml(libdeps_graph, libdeps_graph_file, named_key_ids=True)
@@ -1364,7 +1377,7 @@ def setup_environment(env, emitting_shared=False, debug='off', linting='on'):
env['LIBDEPS_SYMBOL_DEP_FILES'] = symbol_deps
env['LIBDEPS_GRAPH_FILE'] = env.File("${BUILD_DIR}/libdeps/libdeps.graphml")
- env['LIBDEPS_GRAPH_SCHEMA_VERSION'] = 3
+ env['LIBDEPS_GRAPH_SCHEMA_VERSION'] = 4
env["SYMBOLDEPSSUFFIX"] = '.symbol_deps'
libdeps_graph = LibdepsGraph()