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 self.force_build = force_build 58 if not os.path.isfile(module_file): 59 self.force_build = True 60 super().__init__(self.force_build, module_file) 61 62 def _load_module_info_file(self, module_file): 63 """Load the module file. 64 65 Args: 66 module_file: String of path to file to load up. Used for testing. 67 68 Returns: 69 Tuple of module_info_target and dict of json. 70 """ 71 if self.force_build: 72 self._discover_mod_file_and_target(True) 73 mod_info = common_util.get_json_dict(module_file) 74 NativeModuleInfo.c_lang_path = mod_info.get(_CLANG, '') 75 NativeModuleInfo.cpp_lang_path = mod_info.get(_CPPLANG, '') 76 name_to_module_info = mod_info.get(_MODULES, {}) 77 root_dir = common_util.get_android_root_dir() 78 module_info_target = os.path.relpath(module_file, root_dir) 79 return module_info_target, name_to_module_info 80 81 def get_module_names_in_targets_paths(self, targets): 82 """Gets module names exist in native_module_info. 83 84 Args: 85 targets: A list of build targets to be checked. 86 87 Returns: 88 A list of native projects' names if native projects exist otherwise 89 return None. 90 """ 91 projects = [] 92 for target in targets: 93 if target == constant.WHOLE_ANDROID_TREE_TARGET: 94 print('Do not deal with whole source tree in native projects.') 95 continue 96 rel_path, _ = common_util.get_related_paths(self, target) 97 for path in self.path_to_module_info: 98 if common_util.is_source_under_relative_path(path, rel_path): 99 projects.extend(self.get_module_names(path)) 100 return projects 101 102 def get_module_includes(self, mod_name): 103 """Gets module's include paths from module name. 104 105 The include paths contain in 'header_search_path' and 106 'system_search_path' of all flags in native module info. 107 108 Args: 109 mod_name: A string of module name. 110 111 Returns: 112 A set of module include paths relative to android root. 113 """ 114 includes = set() 115 mod_info = self.name_to_module_info.get(mod_name, {}) 116 if not mod_info: 117 logging.warning('%s module name %s does not exist.', 118 common_util.COLORED_INFO('Warning:'), mod_name) 119 return includes 120 for flag in mod_info: 121 for header in (constant.KEY_HEADER, constant.KEY_SYSTEM): 122 if header in mod_info[flag]: 123 includes.update(set(mod_info[flag][header])) 124 return includes 125 126 def is_module_need_build(self, mod_name): 127 """Checks if a module need to be built by its module name. 128 129 If a module's source files or include files contain a path looks like, 130 'out/soong/.intermediates/../gen/sysprop/charger.sysprop.cpp' or 131 'out/soong/.intermediates/../android.bufferhub@1.0_genc++_headers/gen' 132 and the paths do not exist, that means the module needs to be built to 133 generate relative source or include files. 134 135 Args: 136 mod_name: A string of module name. 137 138 Returns: 139 A boolean, True if it needs to be generated else False. 140 """ 141 mod_info = self.name_to_module_info.get(mod_name, {}) 142 if not mod_info: 143 logging.warning('%s module name %s does not exist.', 144 common_util.COLORED_INFO('Warning:'), mod_name) 145 return False 146 if self._is_source_need_build(mod_info): 147 return True 148 if self._is_include_need_build(mod_info): 149 return True 150 return False 151 152 def _is_source_need_build(self, mod_info): 153 """Checks if a module's source files need to be built. 154 155 If a module's source files contain a path looks like, 156 'out/soong/.intermediates/../gen/sysprop/charger.sysprop.cpp' 157 and the paths do not exist, that means the module needs to be built to 158 generate relative source files. 159 160 Args: 161 mod_info: A dictionary of module info to check. 162 163 Returns: 164 A boolean, True if it needs to be generated else False. 165 """ 166 if constant.KEY_SRCS not in mod_info: 167 return False 168 for src in mod_info[constant.KEY_SRCS]: 169 if re.search(_INC_GEN_CHECK, src) and not os.path.isfile(src): 170 return True 171 return False 172 173 def _is_include_need_build(self, mod_info): 174 """Checks if a module needs to be built by its module name. 175 176 If a module's include files contain a path looks like, 177 'out/soong/.intermediates/../android.bufferhub@1.0_genc++_headers/gen' 178 and the paths do not exist, that means the module needs to be built to 179 generate relative include files. 180 181 Args: 182 mod_info: A dictionary of module info to check. 183 184 Returns: 185 A boolean, True if it needs to be generated else False. 186 """ 187 for flag in mod_info: 188 for header in (constant.KEY_HEADER, constant.KEY_SYSTEM): 189 if header not in mod_info[flag]: 190 continue 191 for include in mod_info[flag][header]: 192 match = re.search(_INC_GEN_CHECK, include) 193 if match and not os.path.isdir(include): 194 return True 195 return False 196 197 def is_suite_in_compatibility_suites(self, suite, mod_info): 198 """Check if suite exists in the compatibility_suites of module-info. 199 200 Args: 201 suite: A string of suite name. 202 mod_info: Dict of module info to check. 203 204 Returns: 205 True if it exists in mod_info, False otherwise. 206 """ 207 raise NotImplementedError() 208 209 def get_testable_modules(self, suite=None): 210 """Return the testable modules of the given suite name. 211 212 Args: 213 suite: A string of suite name. Set to None to return all testable 214 modules. 215 216 Returns: 217 List of testable modules. Empty list if non-existent. 218 If suite is None, return all the testable modules in module-info. 219 """ 220 raise NotImplementedError() 221 222 def is_testable_module(self, mod_info): 223 """Check if module is something we can test. 224 225 A module is testable if: 226 - it's installed, or 227 - it's a robolectric module (or shares path with one). 228 229 Args: 230 mod_info: Dict of module info to check. 231 232 Returns: 233 True if we can test this module, False otherwise. 234 """ 235 raise NotImplementedError() 236 237 def has_test_config(self, mod_info): 238 """Validate if this module has a test config. 239 240 A module can have a test config in the following manner: 241 - AndroidTest.xml at the module path. 242 - test_config be set in module-info.json. 243 - Auto-generated config via the auto_test_config key in 244 module-info.json. 245 246 Args: 247 mod_info: Dict of module info to check. 248 249 Returns: 250 True if this module has a test config, False otherwise. 251 """ 252 raise NotImplementedError() 253 254 def get_robolectric_test_name(self, module_name): 255 """Returns runnable robolectric module name. 256 257 There are at least 2 modules in every robolectric module path, return 258 the module that we can run as a build target. 259 260 Arg: 261 module_name: String of module. 262 263 Returns: 264 String of module that is the runnable robolectric module, None if 265 none could be found. 266 """ 267 raise NotImplementedError() 268 269 def is_robolectric_test(self, module_name): 270 """Check if module is a robolectric test. 271 272 A module can be a robolectric test if the specified module has their 273 class set as ROBOLECTRIC (or shares their path with a module that does). 274 275 Args: 276 module_name: String of module to check. 277 278 Returns: 279 True if the module is a robolectric module, else False. 280 """ 281 raise NotImplementedError() 282 283 def is_auto_gen_test_config(self, module_name): 284 """Check if the test config file will be generated automatically. 285 286 Args: 287 module_name: A string of the module name. 288 289 Returns: 290 True if the test config file will be generated automatically. 291 """ 292 raise NotImplementedError() 293 294 def is_robolectric_module(self, mod_info): 295 """Check if a module is a robolectric module. 296 297 Args: 298 mod_info: ModuleInfo to check. 299 300 Returns: 301 True if module is a robolectric module, False otherwise. 302 """ 303 raise NotImplementedError() 304 305 def is_native_test(self, module_name): 306 """Check if the input module is a native test. 307 308 Args: 309 module_name: A string of the module name. 310 311 Returns: 312 True if the test is a native test, False otherwise. 313 """ 314 raise NotImplementedError() 315