178 lines
6.2 KiB
Python
Executable File
178 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (C) 2022 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""A script to produce a csv report of all modules of a given type.
|
|
|
|
There is one output row per module of the input type, each column corresponds
|
|
to one of the fields of the _ModuleTypeInfo named tuple described below.
|
|
The script allows to ignore certain dependency edges based on the target module
|
|
name, or the dependency tag name.
|
|
|
|
Usage:
|
|
./bp2build-module-dep-infos.py -m <module type>
|
|
--ignore_by_name <modules to ignore>
|
|
--ignore_by_tag <dependency tags to ignore>
|
|
|
|
"""
|
|
|
|
import argparse
|
|
import collections
|
|
import csv
|
|
import dependency_analysis
|
|
import sys
|
|
|
|
_ModuleTypeInfo = collections.namedtuple(
|
|
"_ModuleTypeInfo",
|
|
[
|
|
# map of module type to the set of properties used by modules
|
|
# of the given type in the dependency tree.
|
|
"type_to_properties",
|
|
# [java modules only] list of source file extensions used by this module.
|
|
"java_source_extensions",
|
|
])
|
|
|
|
_DependencyRelation = collections.namedtuple("_DependencyRelation", [
|
|
"transitive_dependency",
|
|
"top_level_module",
|
|
])
|
|
|
|
|
|
def _get_java_source_extensions(module):
|
|
out = set()
|
|
if "Module" not in module:
|
|
return out
|
|
if "Java" not in module["Module"]:
|
|
return out
|
|
if "SourceExtensions" not in module["Module"]["Java"]:
|
|
return out
|
|
if module["Module"]["Java"]["SourceExtensions"]:
|
|
out.update(module["Module"]["Java"]["SourceExtensions"])
|
|
return out
|
|
|
|
|
|
def _get_set_properties(module):
|
|
set_properties = set()
|
|
if "Module" not in module:
|
|
return set_properties
|
|
if "Android" not in module["Module"]:
|
|
return set_properties
|
|
if "SetProperties" not in module["Module"]["Android"]:
|
|
return set_properties
|
|
for prop in module["Module"]["Android"]["SetProperties"]:
|
|
set_properties.add(prop["Name"])
|
|
return set_properties
|
|
|
|
|
|
def _should_ignore(module, ignored_names):
|
|
return (dependency_analysis.is_windows_variation(module) or
|
|
module["Name"] in ignored_names or
|
|
dependency_analysis.ignore_kind(module["Type"]))
|
|
|
|
def _update_infos(module_name, type_infos, module_graph_map, ignored_dep_names):
|
|
module = module_graph_map[module_name]
|
|
if _should_ignore(module, ignored_dep_names) or module_name in type_infos:
|
|
return
|
|
for dep in module["Deps"]:
|
|
dep_name = dep["Name"]
|
|
if dep_name == module_name:
|
|
continue
|
|
_update_infos(dep_name, type_infos, module_graph_map, ignored_dep_names)
|
|
|
|
java_source_extensions = _get_java_source_extensions(module)
|
|
type_to_properties = collections.defaultdict(set)
|
|
if module["Type"]:
|
|
type_to_properties[module["Type"]].update(_get_set_properties(module))
|
|
for dep in module["Deps"]:
|
|
dep_name = dep["Name"]
|
|
if _should_ignore(module_graph_map[dep_name], ignored_dep_names):
|
|
continue
|
|
if dep_name == module_name:
|
|
continue
|
|
for dep_type, dep_type_properties in type_infos[dep_name].type_to_properties.items():
|
|
type_to_properties[dep_type].update(dep_type_properties)
|
|
java_source_extensions.update(type_infos[dep_name].java_source_extensions)
|
|
type_infos[module_name] = _ModuleTypeInfo(
|
|
type_to_properties=type_to_properties,
|
|
java_source_extensions=java_source_extensions)
|
|
|
|
|
|
def module_type_info_from_json(module_graph, module_type, ignored_dep_names):
|
|
"""Builds a map of module name to _ModuleTypeInfo for each module of module_type.
|
|
|
|
Dependency edges pointing to modules in ignored_dep_names are not followed.
|
|
"""
|
|
module_graph_map = dict()
|
|
module_stack = []
|
|
for module in module_graph:
|
|
# Windows variants have incomplete dependency information in the json module graph.
|
|
if dependency_analysis.is_windows_variation(module):
|
|
continue
|
|
module_graph_map[module["Name"]] = module
|
|
if module["Type"] == module_type:
|
|
module_stack.append(module["Name"])
|
|
# dictionary of module name to _ModuleTypeInfo.
|
|
type_infos = {}
|
|
for module_name in module_stack:
|
|
# post-order traversal of the dependency graph builds the type_infos
|
|
# dictionary from the leaves so that common dependencies are visited
|
|
# only once.
|
|
_update_infos(module_name, type_infos, module_graph_map, ignored_dep_names)
|
|
|
|
return {
|
|
name: info
|
|
for name, info in type_infos.items()
|
|
if module_graph_map[name]["Type"] == module_type
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="")
|
|
parser.add_argument("--module_type", "-m", help="name of Soong module type.")
|
|
parser.add_argument(
|
|
"--ignore_by_name",
|
|
type=str,
|
|
default="",
|
|
required=False,
|
|
help="Comma-separated list. When building the tree of transitive dependencies, will not follow dependency edges pointing to module names listed by this flag."
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
module_type = args.module_type
|
|
ignore_by_name = args.ignore_by_name
|
|
|
|
module_graph = dependency_analysis.get_json_module_type_info(module_type)
|
|
type_infos = module_type_info_from_json(module_graph, module_type,
|
|
ignore_by_name.split(","))
|
|
writer = csv.writer(sys.stdout)
|
|
writer.writerow([
|
|
"module name",
|
|
"properties",
|
|
"java source extensions",
|
|
])
|
|
for module, module_type_info in type_infos.items():
|
|
writer.writerow([
|
|
module,
|
|
("[\"%s\"]" % '"\n"'.join([
|
|
"%s: %s" % (mtype, ",".join(properties)) for mtype, properties in
|
|
module_type_info.type_to_properties.items()
|
|
]) if len(module_type_info.type_to_properties) else "[]"),
|
|
("[\"%s\"]" % '", "'.join(module_type_info.java_source_extensions)
|
|
if len(module_type_info.java_source_extensions) else "[]"),
|
|
])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|