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 22from __future__ import print_function 23 24import argparse 25import itertools 26import sys 27 28from blueprint import RecursiveParser, evaluate_defaults 29 30 31def _is_vndk(module): 32 """Get the `vndk.enabled` module property.""" 33 try: 34 return bool(module['vndk']['enabled']) 35 except KeyError: 36 return False 37 38 39def _is_vndk_sp(module): 40 """Get the `vndk.support_system_process` module property.""" 41 try: 42 return bool(module['vndk']['support_system_process']) 43 except KeyError: 44 return False 45 46 47def _is_vendor(module): 48 """Get the `vendor` module property.""" 49 try: 50 return module.get('vendor', False) or module.get('proprietary', False) 51 except KeyError: 52 return False 53 54 55def _is_vendor_available(module): 56 """Get the `vendor_available` module property.""" 57 try: 58 return bool(module['vendor_available']) 59 except KeyError: 60 return False 61 62 63def _has_vendor_variant(module): 64 """Check whether the module is VNDK or vendor available.""" 65 return _is_vndk(module) or _is_vendor_available(module) 66 67 68def _get_dependencies(module): 69 """Get module dependencies.""" 70 71 shared_libs = set(module.get('shared_libs', [])) 72 static_libs = set(module.get('static_libs', [])) 73 header_libs = set(module.get('header_libs', [])) 74 75 try: 76 target_vendor = module['target']['vendor'] 77 shared_libs -= set(target_vendor.get('exclude_shared_libs', [])) 78 static_libs -= set(target_vendor.get('exclude_static_libs', [])) 79 header_libs -= set(target_vendor.get('exclude_header_libs', [])) 80 except KeyError: 81 pass 82 83 return (sorted(shared_libs), sorted(static_libs), sorted(header_libs)) 84 85 86def _build_module_dict(modules): 87 """Build module dictionaries that map module names to modules.""" 88 all_libs = {} 89 llndk_libs = {} 90 91 for rule, module in modules: 92 name = module.get('name') 93 if name is None: 94 continue 95 96 if rule == 'llndk_library': 97 llndk_libs[name] = (rule, module) 98 if rule in {'llndk_library', 'ndk_library'}: 99 continue 100 101 if rule.endswith('_library') or \ 102 rule.endswith('_library_shared') or \ 103 rule.endswith('_library_static') or \ 104 rule.endswith('_headers'): 105 all_libs[name] = (rule, module) 106 107 if rule == 'hidl_interface': 108 all_libs[name] = (rule, module) 109 all_libs[name + '-adapter-helper'] = (rule, module) 110 module['vendor_available'] = True 111 112 return (all_libs, llndk_libs) 113 114 115def _check_module_deps(all_libs, llndk_libs, module): 116 """Check the dependencies of a module.""" 117 118 bad_deps = set() 119 shared_deps, static_deps, header_deps = _get_dependencies(module) 120 121 # Check vendor module dependencies requirements. 122 for dep_name in itertools.chain(shared_deps, static_deps, header_deps): 123 if dep_name in llndk_libs: 124 continue 125 dep_module = all_libs[dep_name][1] 126 if _is_vendor(dep_module): 127 continue 128 if _is_vendor_available(dep_module): 129 continue 130 if _is_vndk(dep_module) and not _is_vendor(module): 131 continue 132 bad_deps.add(dep_name) 133 134 # Check VNDK dependencies requirements. 135 if _is_vndk(module) and not _is_vendor(module): 136 is_vndk_sp = _is_vndk_sp(module) 137 for dep_name in shared_deps: 138 if dep_name in llndk_libs: 139 continue 140 dep_module = all_libs[dep_name][1] 141 if not _is_vndk(dep_module): 142 # VNDK must be self-contained. 143 bad_deps.add(dep_name) 144 break 145 if is_vndk_sp and not _is_vndk_sp(dep_module): 146 # VNDK-SP must be self-contained. 147 bad_deps.add(dep_name) 148 break 149 150 return bad_deps 151 152 153def _check_modules_deps(modules): 154 """Check the dependencies of modules.""" 155 156 all_libs, llndk_libs = _build_module_dict(modules) 157 158 # Check the dependencies of modules 159 all_bad_deps = [] 160 for name, (_, module) in all_libs.items(): 161 if not _has_vendor_variant(module) and not _is_vendor(module): 162 continue 163 164 bad_deps = _check_module_deps(all_libs, llndk_libs, module) 165 166 if bad_deps: 167 all_bad_deps.append((name, sorted(bad_deps))) 168 169 return sorted(all_bad_deps) 170 171 172def _parse_args(): 173 """Parse command line options.""" 174 parser = argparse.ArgumentParser() 175 parser.add_argument('root_bp', help='android source tree root') 176 return parser.parse_args() 177 178 179def main(): 180 """Main function.""" 181 182 args = _parse_args() 183 184 parser = RecursiveParser() 185 parser.parse_file(args.root_bp) 186 187 all_bad_deps = _check_modules_deps(evaluate_defaults(parser.modules)) 188 for name, bad_deps in all_bad_deps: 189 print('ERROR: {!r} must not depend on {}'.format(name, bad_deps), 190 file=sys.stderr) 191 192 if all_bad_deps: 193 sys.exit(1) 194 195 196if __name__ == '__main__': 197 main() 198