blob: 440b04f1343664ffa97553f9f9d6a93e9cad3ac3 [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
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'
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 get_gen_includes(self, mod_name):
"""Gets module's include paths which need to be generated.
Gets module's include paths which don't exist, e.g.,
'out/soong/../android.frameworks.bufferhub@1.0_genc++_headers/gen'
if the path doesn't exist we should generate the header files in it.
In this example, if module 'android.frameworks.bufferhub@1.0' exists in
native module info, we have to build it to generate include header files
for the native module.
Args:
mod_name: A string of module name.
Returns:
A set of rebuild target names.
"""
android_root_dir = common_util.get_android_root_dir()
includes = self.get_module_includes(mod_name)
mod_names = set()
for include in includes:
if not os.path.isdir(os.path.join(android_root_dir, include)):
target = os.path.basename(os.path.dirname(include))
target = target.rstrip(_INCLUDE_TAIL)
if target in self.name_to_module_info:
mod_names.add(target)
return mod_names
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()