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
195 lines
6.4 KiB
4 months ago
|
#!/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
|