xref: /aosp_15_r20/external/grpc-grpc/tools/buildgen/plugins/transitive_dependencies.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Buildgen transitive dependencies
15
16This takes the list of libs, node_modules, and targets from our
17yaml dictionary, and adds to each the transitive closure
18of the list of dependencies.
19"""
20
21
22def transitive_deps(lib_map, node):
23    """Returns a list of transitive dependencies from node.
24
25    Recursively iterate all dependent node in a depth-first fashion and
26    list a result using a topological sorting.
27    """
28    result = []
29    seen = set()
30    start = node
31
32    def recursive_helper(node):
33        for dep in node.get("deps", []):
34            if dep not in seen:
35                seen.add(dep)
36                next_node = lib_map.get(dep)
37                if next_node:
38                    recursive_helper(next_node)
39                else:
40                    # For some deps, the corrensponding library entry doesn't exist,
41                    # but we still want to preserve the dependency so that the build
42                    # system can provide custom handling for that depdendency.
43                    result.append(dep)
44        if node is not start:
45            result.insert(0, node["name"])
46
47    recursive_helper(node)
48    return result
49
50
51def mako_plugin(dictionary):
52    """The exported plugin code for transitive_dependencies.
53
54    Iterate over each list and check each item for a deps list. We add a
55    transitive_deps property to each with the transitive closure of those
56    dependency lists. The result list is sorted in a topological ordering.
57    """
58    lib_map = {lib["name"]: lib for lib in dictionary.get("libs")}
59
60    for target_name, target_list in list(dictionary.items()):
61        for target in target_list:
62            if isinstance(target, dict):
63                if "deps" in target or target_name == "libs":
64                    if not "deps" in target:
65                        # make sure all the libs have the "deps" field populated
66                        target["deps"] = []
67                    target["transitive_deps"] = transitive_deps(lib_map, target)
68
69    python_dependencies = dictionary.get("python_dependencies")
70    python_dependencies["transitive_deps"] = transitive_deps(
71        lib_map, python_dependencies
72    )
73