• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""A script to produce a csv report of all modules of a given type.
17
18There is one output row per module of the input type, each column corresponds
19to one of the fields of the _ModuleTypeInfo named tuple described below.
20The script allows to ignore certain dependency edges based on the target module
21name, or the dependency tag name.
22
23Usage:
24  ./bp2build-module-dep-infos.py -m <module type>
25                                 --ignore-by-name <modules to ignore>
26
27"""
28
29import argparse
30import collections
31import csv
32import dependency_analysis
33import sys
34
35_ModuleTypeInfo = collections.namedtuple(
36    "_ModuleTypeInfo",
37    [
38        # map of module type to the set of properties used by modules
39        # of the given type in the dependency tree.
40        "type_to_properties",
41        # [java modules only] list of source file extensions used by this module.
42        "java_source_extensions",
43    ],
44)
45
46def _get_java_source_extensions(module):
47  out = set()
48  if "Module" not in module:
49    return out
50  if "Java" not in module["Module"]:
51    return out
52  if "SourceExtensions" not in module["Module"]["Java"]:
53    return out
54  if module["Module"]["Java"]["SourceExtensions"]:
55    out.update(module["Module"]["Java"]["SourceExtensions"])
56  return out
57
58
59def module_type_info_from_json(
60    module_graph, module_type, ignored_dep_names, ignore_java_auto_deps
61):
62  """Builds a map of module name to _ModuleTypeInfo for each module of module_type.
63
64     Dependency edges pointing to modules in ignored_dep_names are not followed.
65  """
66
67  modules_of_type = set()
68
69  def filter_by_type(json):
70    if json["Type"] == module_type:
71      modules_of_type.add(json["Name"])
72      return True
73    return False
74
75  # dictionary of module name to _ModuleTypeInfo.
76
77  type_infos = {}
78
79  def update_infos(module, deps):
80    module_name = module["Name"]
81    info = type_infos.get(
82        module_name,
83        _ModuleTypeInfo(
84            java_source_extensions=set(),
85            type_to_properties=collections.defaultdict(set),
86        ))
87
88    java_source_extensions = _get_java_source_extensions(module)
89
90    if module["Type"]:
91      info.type_to_properties[module["Type"]].update(
92          dependency_analysis.get_property_names(module))
93
94    for dep_name in deps:
95      for dep_type, dep_type_properties in type_infos[
96          dep_name].type_to_properties.items():
97        info.type_to_properties[dep_type].update(dep_type_properties)
98        java_source_extensions.update(
99            type_infos[dep_name].java_source_extensions)
100
101    info.java_source_extensions.update(java_source_extensions)
102    # for a module, collect all properties and java source extensions specified by
103    # transitive dependencies and the module itself
104    type_infos[module_name] = info
105
106  dependency_analysis.visit_json_module_graph_post_order(
107      module_graph, ignored_dep_names, ignore_java_auto_deps, filter_by_type, update_infos)
108
109  return {
110      name: info for name, info in type_infos.items() if name in modules_of_type
111  }
112
113
114def _write_output(file_handle, type_infos):
115  writer = csv.writer(file_handle)
116  writer.writerow([
117      "module name",
118      "properties",
119      "java source extensions",
120  ])
121  for module, module_type_info in type_infos.items():
122    writer.writerow([
123        module,
124        ("[\"%s\"]" % '"\n"'.join([
125            "%s: %s" % (mtype, ",".join(properties)) for mtype, properties in
126            module_type_info.type_to_properties.items()
127        ]) if len(module_type_info.type_to_properties) else "[]"),
128        ("[\"%s\"]" % '", "'.join(module_type_info.java_source_extensions)
129         if len(module_type_info.java_source_extensions) else "[]"),
130    ])
131
132
133def main():
134  parser = argparse.ArgumentParser()
135  parser.add_argument("--module-type", "-m", help="name of Soong module type.")
136  parser.add_argument(
137      "--ignore-by-name",
138      default="",
139      help=(
140          "Comma-separated list. When building the tree of transitive"
141          " dependencies, will not follow dependency edges pointing to module"
142          " names listed by this flag."
143      ),
144  )
145  parser.add_argument(
146      "--ignore-java-auto-deps",
147      action="store_true",
148      help="whether to ignore automatically added java deps",
149  )
150  args = parser.parse_args()
151
152  module_type = args.module_type
153  ignore_by_name = args.ignore_by_name
154
155  module_graph = dependency_analysis.get_json_module_type_info(module_type)
156  type_infos = module_type_info_from_json(
157      module_graph,
158      module_type,
159      ignore_by_name.split(","),
160      args.ignore_java_auto_deps,
161  )
162
163  _write_output(sys.stdout, type_infos)
164
165
166if __name__ == "__main__":
167  main()
168