1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""native_module_info 18 19Module Info class used to hold cached module_bp_cc_deps.json. 20""" 21 22import logging 23import os 24import re 25 26from aidegen import constant 27from aidegen.lib import common_util 28from aidegen.lib import module_info 29 30_CLANG = 'clang' 31_CPPLANG = 'clang++' 32_MODULES = 'modules' 33_INCLUDE_TAIL = '_genc++_headers' 34_SRC_GEN_CHECK = r'^out/soong/.intermediates/.+/gen/.+\.(c|cc|cpp)' 35_INC_GEN_CHECK = r'^out/soong/.intermediates/.+/gen($|/.+)' 36 37 38class NativeModuleInfo(module_info.AidegenModuleInfo): 39 """Class that offers fast/easy lookup for module related details. 40 41 Class Attributes: 42 c_lang_path: Make C files compiler path. 43 cpp_lang_path: Make C++ files compiler path. 44 """ 45 46 c_lang_path = '' 47 cpp_lang_path = '' 48 49 def __init__(self, force_build=False, module_file=None): 50 """Initialize the NativeModuleInfo object. 51 52 Load up the module_bp_cc_deps.json file and initialize the helper vars. 53 """ 54 if not module_file: 55 module_file = common_util.get_blueprint_json_path( 56 constant.BLUEPRINT_CC_JSONFILE_NAME) 57 if not os.path.isfile(module_file): 58 force_build = True 59 super().__init__(force_build, module_file) 60 61 def _load_module_info_file(self, force_build, module_file): 62 """Load the module file. 63 64 Args: 65 force_build: Boolean to indicate if we should rebuild the 66 module_info file regardless if it's created or not. 67 module_file: String of path to file to load up. Used for testing. 68 69 Returns: 70 Tuple of module_info_target and dict of json. 71 """ 72 if force_build: 73 self._discover_mod_file_and_target(True) 74 mod_info = common_util.get_json_dict(module_file) 75 NativeModuleInfo.c_lang_path = mod_info.get(_CLANG, '') 76 NativeModuleInfo.cpp_lang_path = mod_info.get(_CPPLANG, '') 77 name_to_module_info = mod_info.get(_MODULES, {}) 78 root_dir = common_util.get_android_root_dir() 79 module_info_target = os.path.relpath(module_file, root_dir) 80 return module_info_target, name_to_module_info 81 82 def get_module_names_in_targets_paths(self, targets): 83 """Gets module names exist in native_module_info. 84 85 Args: 86 targets: A list of build targets to be checked. 87 88 Returns: 89 A list of native projects' names if native projects exist otherwise 90 return None. 91 """ 92 projects = [] 93 for target in targets: 94 if target == constant.WHOLE_ANDROID_TREE_TARGET: 95 print('Do not deal with whole source tree in native projects.') 96 continue 97 rel_path, _ = common_util.get_related_paths(self, target) 98 for path in self.path_to_module_info: 99 if common_util.is_source_under_relative_path(path, rel_path): 100 projects.extend(self.get_module_names(path)) 101 return projects 102 103 def get_module_includes(self, mod_name): 104 """Gets module's include paths from module name. 105 106 The include paths contain in 'header_search_path' and 107 'system_search_path' of all flags in native module info. 108 109 Args: 110 mod_name: A string of module name. 111 112 Returns: 113 A set of module include paths relative to android root. 114 """ 115 includes = set() 116 mod_info = self.name_to_module_info.get(mod_name, {}) 117 if not mod_info: 118 logging.warning('%s module name %s does not exist.', 119 common_util.COLORED_INFO('Warning:'), mod_name) 120 return includes 121 for flag in mod_info: 122 for header in (constant.KEY_HEADER, constant.KEY_SYSTEM): 123 if header in mod_info[flag]: 124 includes.update(set(mod_info[flag][header])) 125 return includes 126 127 def is_module_need_build(self, mod_name): 128 """Checks if a module need to be built by its module name. 129 130 If a module's source files or include files contain a path looks like, 131 'out/soong/.intermediates/../gen/sysprop/charger.sysprop.cpp' or 132 'out/soong/.intermediates/../[email protected]_genc++_headers/gen' 133 and the paths do not exist, that means the module needs to be built to 134 generate relative source or include files. 135 136 Args: 137 mod_name: A string of module name. 138 139 Returns: 140 A boolean, True if it needs to be generated else False. 141 """ 142 mod_info = self.name_to_module_info.get(mod_name, {}) 143 if not mod_info: 144 logging.warning('%s module name %s does not exist.', 145 common_util.COLORED_INFO('Warning:'), mod_name) 146 return False 147 if self._is_source_need_build(mod_info): 148 return True 149 if self._is_include_need_build(mod_info): 150 return True 151 return False 152 153 def _is_source_need_build(self, mod_info): 154 """Checks if a module's source files need to be built. 155 156 If a module's source files contain a path looks like, 157 'out/soong/.intermediates/../gen/sysprop/charger.sysprop.cpp' 158 and the paths do not exist, that means the module needs to be built to 159 generate relative source files. 160 161 Args: 162 mod_info: A dictionary of module info to check. 163 164 Returns: 165 A boolean, True if it needs to be generated else False. 166 """ 167 if constant.KEY_SRCS not in mod_info: 168 return False 169 for src in mod_info[constant.KEY_SRCS]: 170 if re.search(_INC_GEN_CHECK, src) and not os.path.isfile(src): 171 return True 172 return False 173 174 def _is_include_need_build(self, mod_info): 175 """Checks if a module needs to be built by its module name. 176 177 If a module's include files contain a path looks like, 178 'out/soong/.intermediates/../[email protected]_genc++_headers/gen' 179 and the paths do not exist, that means the module needs to be built to 180 generate relative include files. 181 182 Args: 183 mod_info: A dictionary of module info to check. 184 185 Returns: 186 A boolean, True if it needs to be generated else False. 187 """ 188 for flag in mod_info: 189 for header in (constant.KEY_HEADER, constant.KEY_SYSTEM): 190 if header not in mod_info[flag]: 191 continue 192 for include in mod_info[flag][header]: 193 match = re.search(_INC_GEN_CHECK, include) 194 if match and not os.path.isdir(include): 195 return True 196 return False 197 198 def is_suite_in_compatibility_suites(self, suite, mod_info): 199 """Check if suite exists in the compatibility_suites of module-info. 200 201 Args: 202 suite: A string of suite name. 203 mod_info: Dict of module info to check. 204 205 Returns: 206 True if it exists in mod_info, False otherwise. 207 """ 208 raise NotImplementedError() 209 210 def get_testable_modules(self, suite=None): 211 """Return the testable modules of the given suite name. 212 213 Args: 214 suite: A string of suite name. Set to None to return all testable 215 modules. 216 217 Returns: 218 List of testable modules. Empty list if non-existent. 219 If suite is None, return all the testable modules in module-info. 220 """ 221 raise NotImplementedError() 222 223 def is_testable_module(self, mod_info): 224 """Check if module is something we can test. 225 226 A module is testable if: 227 - it's installed, or 228 - it's a robolectric module (or shares path with one). 229 230 Args: 231 mod_info: Dict of module info to check. 232 233 Returns: 234 True if we can test this module, False otherwise. 235 """ 236 raise NotImplementedError() 237 238 def has_test_config(self, mod_info): 239 """Validate if this module has a test config. 240 241 A module can have a test config in the following manner: 242 - AndroidTest.xml at the module path. 243 - test_config be set in module-info.json. 244 - Auto-generated config via the auto_test_config key in 245 module-info.json. 246 247 Args: 248 mod_info: Dict of module info to check. 249 250 Returns: 251 True if this module has a test config, False otherwise. 252 """ 253 raise NotImplementedError() 254 255 def get_robolectric_test_name(self, module_name): 256 """Returns runnable robolectric module name. 257 258 There are at least 2 modules in every robolectric module path, return 259 the module that we can run as a build target. 260 261 Arg: 262 module_name: String of module. 263 264 Returns: 265 String of module that is the runnable robolectric module, None if 266 none could be found. 267 """ 268 raise NotImplementedError() 269 270 def is_robolectric_test(self, module_name): 271 """Check if module is a robolectric test. 272 273 A module can be a robolectric test if the specified module has their 274 class set as ROBOLECTRIC (or shares their path with a module that does). 275 276 Args: 277 module_name: String of module to check. 278 279 Returns: 280 True if the module is a robolectric module, else False. 281 """ 282 raise NotImplementedError() 283 284 def is_auto_gen_test_config(self, module_name): 285 """Check if the test config file will be generated automatically. 286 287 Args: 288 module_name: A string of the module name. 289 290 Returns: 291 True if the test config file will be generated automatically. 292 """ 293 raise NotImplementedError() 294 295 def is_robolectric_module(self, mod_info): 296 """Check if a module is a robolectric module. 297 298 Args: 299 mod_info: ModuleInfo to check. 300 301 Returns: 302 True if module is a robolectric module, False otherwise. 303 """ 304 raise NotImplementedError() 305 306 def is_native_test(self, module_name): 307 """Check if the input module is a native test. 308 309 Args: 310 module_name: A string of the module name. 311 312 Returns: 313 True if the test is a native test, False otherwise. 314 """ 315 raise NotImplementedError() 316