You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
6.4 KiB

#!/usr/bin/env python3
#
# Copyright (C) 2018 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.
#
"""This script scans all Android.bp in an android source tree and check the
correctness of dependencies."""
import copy
from blueprint import RecursiveParser, evaluate_defaults, fill_module_namespaces
class Module(object):
"""The class for Blueprint module definition."""
def __init__(self, rule, attrs):
"""Initialize from a module definition."""
self.rule = rule
self._attrs = attrs
def get_property(self, *names, **kwargs):
"""Get a property in the module definition."""
try:
result = self._attrs
for name in names:
result = result[name]
return result
except KeyError:
return kwargs.get('default', None)
def is_vndk(self):
"""Check whether this module is a VNDK shared library."""
return bool(self.get_property('vndk', 'enabled'))
def is_vndk_sp(self):
"""Check whether this module is a VNDK-SP shared library."""
return bool(self.get_property('vndk', 'support_system_process'))
def is_vendor(self):
"""Check whether this module is a vendor module."""
return bool(self.get_property('vendor') or
self.get_property('proprietary'))
def is_vendor_available(self):
"""Check whether this module is vendor available."""
return bool(self.get_property('vendor_available'))
def has_vendor_variant(self):
"""Check whether the module is VNDK or vendor available."""
return self.is_vndk() or self.is_vendor_available()
def get_name(self):
"""Get the module name."""
return self.get_property('name')
def get_dependencies(self):
"""Get module dependencies."""
shared_libs = set(self.get_property('shared_libs', default=[]))
static_libs = set(self.get_property('static_libs', default=[]))
header_libs = set(self.get_property('header_libs', default=[]))
target_vendor = self.get_property('target', 'vendor')
if target_vendor:
shared_libs -= set(target_vendor.get('exclude_shared_libs', []))
static_libs -= set(target_vendor.get('exclude_static_libs', []))
header_libs -= set(target_vendor.get('exclude_header_libs', []))
return (sorted(shared_libs), sorted(static_libs), sorted(header_libs))
class ModuleClassifier(object):
"""Dictionaries (all_libs, vndk_libs, vndk_sp_libs,
vendor_available_libs, and llndk_libs) for modules."""
def __init__(self):
self.all_libs = {}
self.vndk_libs = {}
self.vndk_sp_libs = {}
self.vendor_available_libs = {}
self.llndk_libs = {}
def add_module(self, name, module):
"""Add a module to one or more dictionaries."""
# If this is an llndk_library, add the module to llndk_libs and return.
if module.rule == 'llndk_library':
if name in self.llndk_libs:
raise ValueError('lldnk name {!r} conflicts'.format(name))
self.llndk_libs[name] = module
return
# Check the module name uniqueness.
prev_module = self.all_libs.get(name)
if prev_module:
# If there are two modules with the same module name, pick the one
# without _prebuilt_library_shared.
if module.rule.endswith('_prebuilt_library_shared'):
return
if not prev_module.rule.endswith('_prebuilt_library_shared'):
raise ValueError('module name {!r} conflicts'.format(name))
# Add the module to dictionaries.
self.all_libs[name] = module
if module.is_vndk():
self.vndk_libs[name] = module
if module.is_vndk_sp():
self.vndk_sp_libs[name] = module
if module.is_vendor_available():
self.vendor_available_libs[name] = module
def _add_modules_from_parsed_pairs(self, parsed_items, namespaces):
"""Add modules from the parsed (rule, attrs) pairs."""
for rule, attrs in parsed_items:
name = attrs.get('name')
if name is None:
continue
namespace = attrs.get('_namespace')
if namespace not in namespaces:
continue
if rule == 'llndk_library':
self.add_module(name, Module(rule, attrs))
if rule in {'llndk_library', 'ndk_library'}:
continue
if rule.endswith('_library') or \
rule.endswith('_library_shared') or \
rule.endswith('_library_static') or \
rule.endswith('_headers'):
self.add_module(name, Module(rule, attrs))
continue
if rule == 'hidl_interface':
attrs['vendor_available'] = True
self.add_module(name, Module(rule, attrs))
adapter_module_name = name + '-adapter-helper'
adapter_module_dict = copy.deepcopy(attrs)
adapter_module_dict['name'] = adapter_module_name
self.add_module(adapter_module_name,
Module(rule, adapter_module_dict))
continue
def parse_root_bp(self, root_bp_path, namespaces=None):
"""Parse blueprint files and add module definitions."""
namespaces = {''} if namespaces is None else set(namespaces)
parser = RecursiveParser()
parser.parse_file(root_bp_path)
parsed_items = evaluate_defaults(parser.modules)
parsed_items = fill_module_namespaces(root_bp_path, parsed_items)
self._add_modules_from_parsed_pairs(parsed_items, namespaces)
@classmethod
def create_from_root_bp(cls, root_bp_path, namespaces=None):
"""Create a ModuleClassifier from a root blueprint file."""
result = cls()
result.parse_root_bp(root_bp_path, namespaces)
return result