1#!/usr/bin/env python3 2 3# 4# Copyright (C) 2018 The Android Open Source Project 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19"""This script scans all Android.bp in an android source tree and check the 20correctness of dependencies.""" 21 22import copy 23 24from blueprint import RecursiveParser, evaluate_defaults, fill_module_namespaces 25 26 27class Module(object): 28 """The class for Blueprint module definition.""" 29 30 def __init__(self, rule, attrs): 31 """Initialize from a module definition.""" 32 self.rule = rule 33 self._attrs = attrs 34 35 36 def get_property(self, *names, **kwargs): 37 """Get a property in the module definition.""" 38 try: 39 result = self._attrs 40 for name in names: 41 result = result[name] 42 return result 43 except KeyError: 44 return kwargs.get('default', None) 45 46 47 def is_vndk(self): 48 """Check whether this module is a VNDK shared library.""" 49 return bool(self.get_property('vndk', 'enabled')) 50 51 52 def is_vndk_sp(self): 53 """Check whether this module is a VNDK-SP shared library.""" 54 return bool(self.get_property('vndk', 'support_system_process')) 55 56 57 def is_vendor(self): 58 """Check whether this module is a vendor module.""" 59 return bool(self.get_property('vendor') or 60 self.get_property('proprietary')) 61 62 63 def is_vendor_available(self): 64 """Check whether this module is vendor available.""" 65 return bool(self.get_property('vendor_available')) 66 67 68 def has_vendor_variant(self): 69 """Check whether the module is VNDK or vendor available.""" 70 return self.is_vndk() or self.is_vendor_available() 71 72 73 def get_name(self): 74 """Get the module name.""" 75 return self.get_property('name') 76 77 78 def get_dependencies(self): 79 """Get module dependencies.""" 80 81 shared_libs = set(self.get_property('shared_libs', default=[])) 82 static_libs = set(self.get_property('static_libs', default=[])) 83 header_libs = set(self.get_property('header_libs', default=[])) 84 85 target_vendor = self.get_property('target', 'vendor') 86 if target_vendor: 87 shared_libs -= set(target_vendor.get('exclude_shared_libs', [])) 88 static_libs -= set(target_vendor.get('exclude_static_libs', [])) 89 header_libs -= set(target_vendor.get('exclude_header_libs', [])) 90 91 return (sorted(shared_libs), sorted(static_libs), sorted(header_libs)) 92 93 94class ModuleClassifier(object): 95 """Dictionaries (all_libs, vndk_libs, vndk_sp_libs, 96 vendor_available_libs, and llndk_libs) for modules.""" 97 98 99 def __init__(self): 100 self.all_libs = {} 101 self.vndk_libs = {} 102 self.vndk_sp_libs = {} 103 self.vendor_available_libs = {} 104 self.llndk_libs = {} 105 106 107 def add_module(self, name, module): 108 """Add a module to one or more dictionaries.""" 109 110 # If this is an llndk_library, add the module to llndk_libs and return. 111 if module.rule == 'llndk_library': 112 if name in self.llndk_libs: 113 raise ValueError('lldnk name {!r} conflicts'.format(name)) 114 self.llndk_libs[name] = module 115 return 116 117 # Check the module name uniqueness. 118 prev_module = self.all_libs.get(name) 119 if prev_module: 120 # If there are two modules with the same module name, pick the one 121 # without _prebuilt_library_shared. 122 if module.rule.endswith('_prebuilt_library_shared'): 123 return 124 if not prev_module.rule.endswith('_prebuilt_library_shared'): 125 raise ValueError('module name {!r} conflicts'.format(name)) 126 127 # Add the module to dictionaries. 128 self.all_libs[name] = module 129 130 if module.is_vndk(): 131 self.vndk_libs[name] = module 132 133 if module.is_vndk_sp(): 134 self.vndk_sp_libs[name] = module 135 136 if module.is_vendor_available(): 137 self.vendor_available_libs[name] = module 138 139 140 def _add_modules_from_parsed_pairs(self, parsed_items, namespaces): 141 """Add modules from the parsed (rule, attrs) pairs.""" 142 143 for rule, attrs in parsed_items: 144 name = attrs.get('name') 145 if name is None: 146 continue 147 148 namespace = attrs.get('_namespace') 149 if namespace not in namespaces: 150 continue 151 152 if rule == 'llndk_library': 153 self.add_module(name, Module(rule, attrs)) 154 if rule in {'llndk_library', 'ndk_library'}: 155 continue 156 157 if rule.endswith('_library') or \ 158 rule.endswith('_library_shared') or \ 159 rule.endswith('_library_static') or \ 160 rule.endswith('_headers'): 161 self.add_module(name, Module(rule, attrs)) 162 continue 163 164 if rule == 'hidl_interface': 165 attrs['vendor_available'] = True 166 self.add_module(name, Module(rule, attrs)) 167 168 adapter_module_name = name + '-adapter-helper' 169 adapter_module_dict = copy.deepcopy(attrs) 170 adapter_module_dict['name'] = adapter_module_name 171 self.add_module(adapter_module_name, 172 Module(rule, adapter_module_dict)) 173 continue 174 175 176 def parse_root_bp(self, root_bp_path, namespaces=None): 177 """Parse blueprint files and add module definitions.""" 178 179 namespaces = {''} if namespaces is None else set(namespaces) 180 181 parser = RecursiveParser() 182 parser.parse_file(root_bp_path) 183 parsed_items = evaluate_defaults(parser.modules) 184 parsed_items = fill_module_namespaces(root_bp_path, parsed_items) 185 186 self._add_modules_from_parsed_pairs(parsed_items, namespaces) 187 188 189 @classmethod 190 def create_from_root_bp(cls, root_bp_path, namespaces=None): 191 """Create a ModuleClassifier from a root blueprint file.""" 192 result = cls() 193 result.parse_root_bp(root_bp_path, namespaces) 194 return result 195