diff options
-rw-r--r-- | SConstruct | 1 | ||||
-rw-r--r-- | buildscripts/libdeps/SCHEMA_CHANGE_LOG.txt | 3 | ||||
-rw-r--r-- | buildscripts/libdeps/analyzer_unittests.py | 38 | ||||
-rwxr-xr-x | buildscripts/libdeps/gacli.py | 9 | ||||
-rw-r--r-- | buildscripts/libdeps/libdeps/analyzer.py | 63 | ||||
-rw-r--r-- | buildscripts/libdeps/libdeps/graph.py | 3 | ||||
-rw-r--r-- | site_scons/libdeps_next.py | 3 |
7 files changed, 72 insertions, 48 deletions
diff --git a/SConstruct b/SConstruct index be5c60e5956..fb573d5513c 100644 --- a/SConstruct +++ b/SConstruct @@ -1284,7 +1284,6 @@ def shim_library(env, name, needs_link=False, *args, **kwargs): for n in nodes: setattr(n.attributes, "needs_link", needs_link) - setattr(n.attributes, "is_shim", True) return nodes diff --git a/buildscripts/libdeps/SCHEMA_CHANGE_LOG.txt b/buildscripts/libdeps/SCHEMA_CHANGE_LOG.txt new file mode 100644 index 00000000000..4e170dfd6e1 --- /dev/null +++ b/buildscripts/libdeps/SCHEMA_CHANGE_LOG.txt @@ -0,0 +1,3 @@ +3 removed shim node property +2 flipped edge direction in graph file data +1 initial schema
\ No newline at end of file diff --git a/buildscripts/libdeps/analyzer_unittests.py b/buildscripts/libdeps/analyzer_unittests.py index c0f9b3bbd2d..b1f0fde43a3 100644 --- a/buildscripts/libdeps/analyzer_unittests.py +++ b/buildscripts/libdeps/analyzer_unittests.py @@ -33,10 +33,10 @@ import libdeps.analyzer from libdeps.graph import LibdepsGraph, EdgeProps, NodeProps, CountTypes -def add_node(graph, node, builder, shim): +def add_node(graph, node, builder): """Add a node to the graph.""" - graph.add_nodes_from([(node, {NodeProps.bin_type.name: builder, NodeProps.shim.name: shim})]) + graph.add_nodes_from([(node, {NodeProps.bin_type.name: builder})]) def add_edge(graph, from_node, to_node, **kwargs): @@ -74,15 +74,15 @@ def get_double_diamond_mock_graph(): # \lib4.so \lib8.so # - add_node(graph, 'lib1.so', 'SharedLibrary', False) - add_node(graph, 'lib2.so', 'SharedLibrary', False) - add_node(graph, 'lib3.so', 'SharedLibrary', False) - add_node(graph, 'lib4.so', 'SharedLibrary', False) - add_node(graph, 'lib5.so', 'SharedLibrary', False) - add_node(graph, 'lib6.so', 'SharedLibrary', False) - add_node(graph, 'lib7.so', 'SharedLibrary', False) - add_node(graph, 'lib8.so', 'SharedLibrary', False) - add_node(graph, 'lib9.so', 'SharedLibrary', False) + add_node(graph, 'lib1.so', 'SharedLibrary') + add_node(graph, 'lib2.so', 'SharedLibrary') + add_node(graph, 'lib3.so', 'SharedLibrary') + add_node(graph, 'lib4.so', 'SharedLibrary') + add_node(graph, 'lib5.so', 'SharedLibrary') + add_node(graph, 'lib6.so', 'SharedLibrary') + add_node(graph, 'lib7.so', 'SharedLibrary') + add_node(graph, 'lib8.so', 'SharedLibrary') + add_node(graph, 'lib9.so', 'SharedLibrary') add_edge(graph, 'lib1.so', 'lib2.so', direct=True, visibility=graph.get_deptype('Public')) add_edge(graph, 'lib2.so', 'lib3.so', direct=True, visibility=graph.get_deptype('Public')) @@ -159,12 +159,12 @@ def get_basic_mock_graph(): # \-lib6.so # nodes - add_node(graph, 'lib1.so', 'SharedLibrary', False) - add_node(graph, 'lib2.so', 'SharedLibrary', False) - add_node(graph, 'lib3.so', 'SharedLibrary', False) - add_node(graph, 'lib4.so', 'SharedLibrary', False) - add_node(graph, 'lib5.so', 'SharedLibrary', False) - add_node(graph, 'lib6.so', 'SharedLibrary', False) + add_node(graph, 'lib1.so', 'SharedLibrary') + add_node(graph, 'lib2.so', 'SharedLibrary') + add_node(graph, 'lib3.so', 'SharedLibrary') + add_node(graph, 'lib4.so', 'SharedLibrary') + add_node(graph, 'lib5.so', 'SharedLibrary') + add_node(graph, 'lib6.so', 'SharedLibrary') # direct edges add_edge(graph, 'lib1.so', 'lib2.so', direct=True, visibility=graph.get_deptype('Public')) @@ -428,7 +428,7 @@ class Tests(unittest.TestCase): expected_result = { "NODE": 6, "EDGE": 13, "DIR_EDGE": 7, "TRANS_EDGE": 6, "DIR_PUB_EDGE": 6, - "PUB_EDGE": 12, "PRIV_EDGE": 1, "IF_EDGE": 0, "SHIM": 0, "PROG": 0, "LIB": 6 + "PUB_EDGE": 12, "PRIV_EDGE": 1, "IF_EDGE": 0, "PROG": 0, "LIB": 6 } self.run_counts(expected_result, libdeps_graph) @@ -439,7 +439,7 @@ class Tests(unittest.TestCase): expected_result = { "NODE": 9, "EDGE": 34, "DIR_EDGE": 10, "TRANS_EDGE": 24, "DIR_PUB_EDGE": 10, - "PUB_EDGE": 34, "PRIV_EDGE": 0, "IF_EDGE": 0, "SHIM": 0, "PROG": 0, "LIB": 9 + "PUB_EDGE": 34, "PRIV_EDGE": 0, "IF_EDGE": 0, "PROG": 0, "LIB": 9 } self.run_counts(expected_result, libdeps_graph) diff --git a/buildscripts/libdeps/gacli.py b/buildscripts/libdeps/gacli.py index 6b0bafb467b..9b71e1544fe 100755 --- a/buildscripts/libdeps/gacli.py +++ b/buildscripts/libdeps/gacli.py @@ -103,7 +103,6 @@ class CustomFormatter(argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHe {help_text[CountTypes.PUB_EDGE.name]}count edges that are public {help_text[CountTypes.PRIV_EDGE.name]}count edges that are private {help_text[CountTypes.IF_EDGE.name]}count edges that are interface - {help_text[CountTypes.SHIM.name]}count shim nodes {help_text[CountTypes.LIB.name]}count library nodes {help_text[CountTypes.PROG.name]}count program nodes """) @@ -159,6 +158,11 @@ def setup_args_parser(): "[from_node] [to_node]: Print edges between two nodes, which if removed would break the dependency between those " + "nodes,.") + parser.add_argument( + '--indegree-one', action='store_true', default=False, help= + "Find candidate nodes for merging by searching the graph for nodes with only one node which depends on them." + ) + args = parser.parse_args() for arg_list in args.graph_paths: @@ -212,6 +216,9 @@ def main(): analysis.append( libdeps_analyzer.CriticalEdges(libdeps_graph, analyzer_args[0], analyzer_args[1])) + if args.indegree_one: + analysis.append(libdeps_analyzer.InDegreeOne(libdeps_graph)) + analysis += libdeps_analyzer.linter_factory(libdeps_graph, args.lint) if args.build_data: diff --git a/buildscripts/libdeps/libdeps/analyzer.py b/buildscripts/libdeps/libdeps/analyzer.py index 58f18413e0a..756d410d4e8 100644 --- a/buildscripts/libdeps/libdeps/analyzer.py +++ b/buildscripts/libdeps/libdeps/analyzer.py @@ -328,22 +328,6 @@ class InterfaceEdgeCounter(Counter): int(self.get_deptype('Interface'))) -class ShimCounter(Counter): - """Counts and reports number of shim nodes in the graph.""" - - def __init__(self, graph): - """Store graph and set type.""" - - super().__init__(graph) - self._count_type = CountTypes.SHIM.name - - @schema_check(schema_version=1) - def run(self): - """Count the graphs shim nodes.""" - - return self.node_type_count(NodeProps.shim.name, True) - - class LibCounter(Counter): """Counts and reports number of library nodes in the graph.""" @@ -388,7 +372,6 @@ def counter_factory(graph, counters, progressbar=True): CountTypes.PUB_EDGE.name: PublicEdgeCounter, CountTypes.PRIV_EDGE.name: PrivateEdgeCounter, CountTypes.IF_EDGE.name: InterfaceEdgeCounter, - CountTypes.SHIM.name: ShimCounter, CountTypes.LIB.name: LibCounter, CountTypes.PROG.name: ProgCounter, } @@ -492,6 +475,37 @@ class ExcludeDependents(Analyzer): report[DependsReportTypes.EXCLUDE_DEPENDS.name][tuple(self._nodes)] = self.run() +class InDegreeOne(Analyzer): + """ + Finds library nodes which have 1 or 0 dependers. + + Such libraries are good candidates for merging or deletion. + """ + + @schema_check(schema_version=1) + def run(self): + """Search the graph for in degree 1 or 0 nodes.""" + + in_degree_one_nodes = [] + for node, data in self._dependency_graph.nodes(data=True): + if (len(self._dependents_graph[node]) < 2 + and data[NodeProps.bin_type.name] == 'SharedLibrary'): + + if len(self._dependents_graph[node]) == 1: + depender = list(self._dependents_graph[node].items())[0][0] + else: + depender = None + + in_degree_one_nodes.append([node, depender]) + + return sorted(in_degree_one_nodes) + + def report(self, report): + """Add the indegree one list to the report.""" + + report[DependsReportTypes.IN_DEGREE_ONE.name] = self.run() + + class GraphPaths(Analyzer): """Finds all paths between two nodes in the graph.""" @@ -631,11 +645,10 @@ class UnusedPublicLinter(Analyzer): for edge in self._dependents_graph.edges: edge_attribs = self._dependents_graph[edge[0]][edge[1]] - if (edge_attribs.get(EdgeProps.direct.name) and edge_attribs.get( - EdgeProps.visibility.name) == int(self.get_deptype('Public')) - and not self._dependents_graph.nodes()[edge[0]].get(NodeProps.shim.name) - and self._dependents_graph.nodes()[edge[1]].get( - NodeProps.bin_type.name) == 'SharedLibrary'): + if (edge_attribs.get(EdgeProps.direct.name) + and edge_attribs.get(EdgeProps.visibility.name) == int( + self.get_deptype('Public')) and self._dependents_graph.nodes()[edge[1]].get( + NodeProps.bin_type.name) == 'SharedLibrary'): # First we will get all the transitive libdeps the dependent node # induces, while we are getting those we also check if the depender @@ -760,7 +773,6 @@ class GaPrettyPrinter(GaPrinter): CountTypes.PUB_EDGE.name: "Public Edges in Graph: {}", CountTypes.PRIV_EDGE.name: "Private Edges in Graph: {}", CountTypes.IF_EDGE.name: "Interface Edges in Graph: {}", - CountTypes.SHIM.name: "Shim Nodes in Graph: {}", CountTypes.LIB.name: "Library Nodes in Graph: {}", CountTypes.PROG.name: "Program Nodes in Graph: {}", } @@ -813,6 +825,11 @@ class GaPrettyPrinter(GaPrinter): f"=>critical edges between {nodes[0]} and {nodes[1]}:", results[DependsReportTypes.CRITICAL_EDGES.name][nodes]) + if DependsReportTypes.IN_DEGREE_ONE.name in results: + print("\nLibrary nodes with 1 or 0 dependers:") + for count, nodes in enumerate(results[DependsReportTypes.IN_DEGREE_ONE.name], start=1): + print(f" {count}: '{nodes[0]}' <- '{nodes[1]}'") + def print(self): """Print the result data.""" results = self._libdeps_graph_analysis.get_results() diff --git a/buildscripts/libdeps/libdeps/graph.py b/buildscripts/libdeps/libdeps/graph.py index 078b832ceb2..2e7dbbcd234 100644 --- a/buildscripts/libdeps/libdeps/graph.py +++ b/buildscripts/libdeps/libdeps/graph.py @@ -54,7 +54,6 @@ class CountTypes(Enum): PUB_EDGE = auto() PRIV_EDGE = auto() IF_EDGE = auto() - SHIM = auto() PROG = auto() LIB = auto() @@ -67,6 +66,7 @@ class DependsReportTypes(Enum): EXCLUDE_DEPENDS = auto() GRAPH_PATHS = auto() CRITICAL_EDGES = auto() + IN_DEGREE_ONE = auto() class LinterTypes(Enum): @@ -87,7 +87,6 @@ class EdgeProps(Enum): class NodeProps(Enum): """Enums for node properties.""" - shim = auto() bin_type = auto() diff --git a/site_scons/libdeps_next.py b/site_scons/libdeps_next.py index 194613d3e8a..402b3d265dd 100644 --- a/site_scons/libdeps_next.py +++ b/site_scons/libdeps_next.py @@ -964,7 +964,6 @@ def add_node_from(env, node): str(node.abspath), { NodeProps.bin_type.name: node.builder.get_name(env), - NodeProps.shim.name: getattr(node.attributes, "is_shim", False) })]) def add_edge_from(env, from_node, to_node, visibility, direct): @@ -1438,7 +1437,7 @@ def setup_environment(env, emitting_shared=False, debug='off', linting='on', san env['LIBDEPS_SYMBOL_DEP_FILES'] = symbol_deps env['LIBDEPS_GRAPH_FILE'] = env.File("${BUILD_DIR}/libdeps/libdeps.graphml") - env['LIBDEPS_GRAPH_SCHEMA_VERSION'] = 2 + env['LIBDEPS_GRAPH_SCHEMA_VERSION'] = 3 env["SYMBOLDEPSSUFFIX"] = '.symbol_deps' libdeps_graph = LibdepsGraph() |