blob: 6388e91054649a2db29cf500517a69743deefd06 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2019 - 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.
"""native_module_info
Module Info class used to hold cached module_bp_cc_deps.json.
"""
import logging
import os
import re
from aidegen import constant
from aidegen.lib import common_util
from aidegen.lib import module_info
_CLANG = 'clang'
_CPPLANG = 'clang++'
_MODULES = 'modules'
_INCLUDE_TAIL = '_genc++_headers'
_SRC_GEN_CHECK = r'^out/soong/.intermediates/.+/gen/.+\.(c|cc|cpp)'
_INC_GEN_CHECK = r'^out/soong/.intermediates/.+/gen($|/.+)'
class NativeModuleInfo(module_info.AidegenModuleInfo):
"""Class that offers fast/easy lookup for module related details.
Class Attributes:
c_lang_path: Make C files compiler path.
cpp_lang_path: Make C++ files compiler path.
"""
c_lang_path = ''
cpp_lang_path = ''
def __init__(self, force_build=False, module_file=None):
"""Initialize the NativeModuleInfo object.
Load up the module_bp_cc_deps.json file and initialize the helper vars.
"""
if not module_file:
module_file = common_util.get_blueprint_json_path(
constant.BLUEPRINT_CC_JSONFILE_NAME)
if not os.path.isfile(module_file):
force_build = True
super().__init__(force_build, module_file)
def _load_module_info_file(self, force_build, module_file):
"""Load the module file.
Args:
force_build: Boolean to indicate if we should rebuild the
module_info file regardless if it's created or not.
module_file: String of path to file to load up. Used for testing.
Returns:
Tuple of module_info_target and dict of json.
"""
if force_build:
self._discover_mod_file_and_target(True)
mod_info = common_util.get_json_dict(module_file)
NativeModuleInfo.c_lang_path = mod_info.get(_CLANG, '')
NativeModuleInfo.cpp_lang_path = mod_info.get(_CPPLANG, '')
name_to_module_info = mod_info.get(_MODULES, {})
root_dir = common_util.get_android_root_dir()
module_info_target = os.path.relpath(module_file, root_dir)
return module_info_target, name_to_module_info
def get_module_names_in_targets_paths(self, targets):
"""Gets module names exist in native_module_info.
Args:
targets: A list of build targets to be checked.
Returns:
A list of native projects' names if native projects exist otherwise
return None.
"""
projects = []
for target in targets:
if target == constant.WHOLE_ANDROID_TREE_TARGET:
print('Do not deal with whole source tree in native projects.')
continue
rel_path, _ = common_util.get_related_paths(self, target)
for path in self.path_to_module_info:
if path.startswith(rel_path):
projects.extend(self.get_module_names(path))
return projects
def get_module_includes(self, mod_name):
"""Gets module's include paths from module name.
The include paths contain in 'header_search_path' and
'system_search_path' of all flags in native module info.
Args:
mod_name: A string of module name.
Returns:
A set of module include paths relative to android root.
"""
includes = set()
mod_info = self.name_to_module_info.get(mod_name, {})
if not mod_info:
logging.warning('%s module name %s does not exist.',
common_util.COLORED_INFO('Warning:'), mod_name)
return includes
for flag in mod_info:
for header in (constant.KEY_HEADER, constant.KEY_SYSTEM):
if header in mod_info[flag]:
includes.update(set(mod_info[flag][header]))
return includes
def is_module_need_build(self, mod_name):
"""Checks if a module need to be built by its module name.
If a module's source files or include files contain a path looks like,
'out/soong/.intermediates/../gen/sysprop/charger.sysprop.cpp' or
'out/soong/.intermediates/../android.bufferhub@1.0_genc++_headers/gen'
and the paths do not exist, that means the module needs to be built to
generate relative source or include files.
Args:
mod_name: A string of module name.
Returns:
A boolean, True if it needs to be generated else False.
"""
mod_info = self.name_to_module_info.get(mod_name, {})
if not mod_info:
logging.warning('%s module name %s does not exist.',
common_util.COLORED_INFO('Warning:'), mod_name)
return False
if self._is_source_need_build(mod_info):
return True
if self._is_include_need_build(mod_info):
return True
return False
def _is_source_need_build(self, mod_info):
"""Checks if a module's source files need to be built.
If a module's source files contain a path looks like,
'out/soong/.intermediates/../gen/sysprop/charger.sysprop.cpp'
and the paths do not exist, that means the module needs to be built to
generate relative source files.
Args:
mod_info: A dictionary of module info to check.
Returns:
A boolean, True if it needs to be generated else False.
"""
if constant.KEY_SRCS not in mod_info:
return False
for src in mod_info[constant.KEY_SRCS]:
if re.search(_INC_GEN_CHECK, src) and not os.path.isfile(src):
return True
return False
def _is_include_need_build(self, mod_info):
"""Checks if a module needs to be built by its module name.
If a module's include files contain a path looks like,
'out/soong/.intermediates/../android.bufferhub@1.0_genc++_headers/gen'
and the paths do not exist, that means the module needs to be built to
generate relative include files.
Args:
mod_info: A dictionary of module info to check.
Returns:
A boolean, True if it needs to be generated else False.
"""
for flag in mod_info:
for header in (constant.KEY_HEADER, constant.KEY_SYSTEM):
if header not in mod_info[flag]:
continue
for include in mod_info[flag][header]:
match = re.search(_INC_GEN_CHECK, include)
if match and not os.path.isdir(include):
return True
return False
def is_suite_in_compatibility_suites(self, suite, mod_info):
"""Check if suite exists in the compatibility_suites of module-info.
Args:
suite: A string of suite name.
mod_info: Dict of module info to check.
Returns:
True if it exists in mod_info, False otherwise.
"""
raise NotImplementedError()
def get_testable_modules(self, suite=None):
"""Return the testable modules of the given suite name.
Args:
suite: A string of suite name. Set to None to return all testable
modules.
Returns:
List of testable modules. Empty list if non-existent.
If suite is None, return all the testable modules in module-info.
"""
raise NotImplementedError()
def is_testable_module(self, mod_info):
"""Check if module is something we can test.
A module is testable if:
- it's installed, or
- it's a robolectric module (or shares path with one).
Args:
mod_info: Dict of module info to check.
Returns:
True if we can test this module, False otherwise.
"""
raise NotImplementedError()
def has_test_config(self, mod_info):
"""Validate if this module has a test config.
A module can have a test config in the following manner:
- AndroidTest.xml at the module path.
- test_config be set in module-info.json.
- Auto-generated config via the auto_test_config key in
module-info.json.
Args:
mod_info: Dict of module info to check.
Returns:
True if this module has a test config, False otherwise.
"""
raise NotImplementedError()
def get_robolectric_test_name(self, module_name):
"""Returns runnable robolectric module name.
There are at least 2 modules in every robolectric module path, return
the module that we can run as a build target.
Arg:
module_name: String of module.
Returns:
String of module that is the runnable robolectric module, None if
none could be found.
"""
raise NotImplementedError()
def is_robolectric_test(self, module_name):
"""Check if module is a robolectric test.
A module can be a robolectric test if the specified module has their
class set as ROBOLECTRIC (or shares their path with a module that does).
Args:
module_name: String of module to check.
Returns:
True if the module is a robolectric module, else False.
"""
raise NotImplementedError()
def is_auto_gen_test_config(self, module_name):
"""Check if the test config file will be generated automatically.
Args:
module_name: A string of the module name.
Returns:
True if the test config file will be generated automatically.
"""
raise NotImplementedError()
def is_robolectric_module(self, mod_info):
"""Check if a module is a robolectric module.
Args:
mod_info: ModuleInfo to check.
Returns:
True if module is a robolectric module, False otherwise.
"""
raise NotImplementedError()
def is_native_test(self, module_name):
"""Check if the input module is a native test.
Args:
module_name: A string of the module name.
Returns:
True if the test is a native test, False otherwise.
"""
raise NotImplementedError()