|  | # Copyright 2022 The Chromium Authors | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  | """Implementation of the graph module for a build target dependency graph.""" | 
|  |  | 
|  | import class_dependency | 
|  | import graph | 
|  | import group_json_consts | 
|  | import java_group | 
|  |  | 
|  | # Usually a class is in exactly one target, but due to jar_excluded_patterns and | 
|  | # android_library_factory some are in two or three. If there is a class that is | 
|  | # in more than 3 build targets, it will be removed from the build graph. Some | 
|  | # examples include: | 
|  | # - org.chromium.base.natives.GEN_JNI (in 100+ targets) | 
|  | # - org.chromium.android_webview.ProductConfig (in 15+ targets) | 
|  | # - org.chromium.content.R (in 60+ targets) | 
|  | _MAX_CONCURRENT_BUILD_TARGETS = 3 | 
|  |  | 
|  |  | 
|  | class JavaTarget(java_group.JavaGroup): | 
|  | """A representation of a Java target.""" | 
|  |  | 
|  |  | 
|  | class JavaTargetDependencyGraph(graph.Graph[JavaTarget]): | 
|  | """A graph representation of the dependencies between Java build targets. | 
|  |  | 
|  | A directed edge A -> B indicates that A depends on B. | 
|  | """ | 
|  |  | 
|  | def __init__(self, class_graph: class_dependency.JavaClassDependencyGraph): | 
|  | """Initializes a new target-level dependency graph | 
|  | by "collapsing" a class-level dependency graph into its targets. | 
|  |  | 
|  | Args: | 
|  | class_graph: A class-level graph to collapse to a target-level one. | 
|  | """ | 
|  | super().__init__() | 
|  |  | 
|  | # Create list of all targets using class nodes | 
|  | # so we don't miss targets with no dependencies (edges). | 
|  | for class_node in class_graph.nodes: | 
|  | if len(class_node.build_targets) > _MAX_CONCURRENT_BUILD_TARGETS: | 
|  | continue | 
|  | for build_target in class_node.build_targets: | 
|  | self.add_node_if_new(build_target) | 
|  |  | 
|  | for begin_class, end_class in class_graph.edges: | 
|  | if len(begin_class.build_targets) > _MAX_CONCURRENT_BUILD_TARGETS: | 
|  | continue | 
|  | if len(end_class.build_targets) > _MAX_CONCURRENT_BUILD_TARGETS: | 
|  | continue | 
|  | for begin_target in begin_class.build_targets: | 
|  | for end_target in end_class.build_targets: | 
|  | # Avoid intra-target deps. | 
|  | if begin_target == end_target: | 
|  | continue | 
|  |  | 
|  | self.add_edge_if_new(begin_target, end_target) | 
|  |  | 
|  | begin_target_node = self.get_node_by_key(begin_target) | 
|  | end_target_node = self.get_node_by_key(end_target) | 
|  | assert begin_target_node is not None | 
|  | assert end_target_node is not None | 
|  | begin_target_node.add_class(begin_class) | 
|  | end_target_node.add_class(end_class) | 
|  | begin_target_node.add_class_dependency_edge( | 
|  | end_target_node, begin_class, end_class) | 
|  |  | 
|  | def create_node_from_key(self, key: str): | 
|  | """Create a JavaTarget node from the given key (target name).""" | 
|  | return JavaTarget(key) | 
|  |  | 
|  | def get_edge_metadata(self, begin_node, end_node): | 
|  | """Generates JSON metadata for the current edge. | 
|  |  | 
|  | The list of edges is sorted in order to help with testing. | 
|  | Structure: | 
|  | { | 
|  | 'class_edges': [ | 
|  | [begin_key, end_key], ... | 
|  | ], | 
|  | } | 
|  | """ | 
|  | return { | 
|  | group_json_consts.CLASS_EDGES: | 
|  | sorted( | 
|  | [begin.name, end.name] for begin, end in | 
|  | begin_node.get_class_dependencies_in_outbound_edge(end_node)), | 
|  | } |