• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    @staticmethod
153    def _is_source_need_build(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    @staticmethod
175    def _is_include_need_build(mod_info):
176        """Checks if a module needs to be built by its module name.
177
178        If a module's include files contain a path looks like,
179        'out/soong/.intermediates/../android.bufferhub@1.0_genc++_headers/gen'
180        and the paths do not exist, that means the module needs to be built to
181        generate relative include files.
182
183        Args:
184            mod_info: A dictionary of module info to check.
185
186        Returns:
187            A boolean, True if it needs to be generated else False.
188        """
189        for flag in mod_info:
190            for header in (constant.KEY_HEADER, constant.KEY_SYSTEM):
191                if header not in mod_info[flag]:
192                    continue
193                for include in mod_info[flag][header]:
194                    match = re.search(_INC_GEN_CHECK, include)
195                    if match and not os.path.isdir(include):
196                        return True
197        return False
198
199    def is_suite_in_compatibility_suites(self, suite, mod_info):
200        """Check if suite exists in the compatibility_suites of module-info.
201
202        Args:
203            suite: A string of suite name.
204            mod_info: Dict of module info to check.
205
206        Returns:
207            True if it exists in mod_info, False otherwise.
208        """
209        raise NotImplementedError()
210
211    def get_testable_modules(self, suite=None):
212        """Return the testable modules of the given suite name.
213
214        Args:
215            suite: A string of suite name. Set to None to return all testable
216            modules.
217
218        Returns:
219            List of testable modules. Empty list if non-existent.
220            If suite is None, return all the testable modules in module-info.
221        """
222        raise NotImplementedError()
223
224    def is_testable_module(self, mod_info):
225        """Check if module is something we can test.
226
227        A module is testable if:
228          - it's installed, or
229          - it's a robolectric module (or shares path with one).
230
231        Args:
232            mod_info: Dict of module info to check.
233
234        Returns:
235            True if we can test this module, False otherwise.
236        """
237        raise NotImplementedError()
238
239    def has_test_config(self, mod_info):
240        """Validate if this module has a test config.
241
242        A module can have a test config in the following manner:
243          - AndroidTest.xml at the module path.
244          - test_config be set in module-info.json.
245          - Auto-generated config via the auto_test_config key in
246            module-info.json.
247
248        Args:
249            mod_info: Dict of module info to check.
250
251        Returns:
252            True if this module has a test config, False otherwise.
253        """
254        raise NotImplementedError()
255
256    def get_robolectric_test_name(self, module_name):
257        """Returns runnable robolectric module name.
258
259        There are at least 2 modules in every robolectric module path, return
260        the module that we can run as a build target.
261
262        Arg:
263            module_name: String of module.
264
265        Returns:
266            String of module that is the runnable robolectric module, None if
267            none could be found.
268        """
269        raise NotImplementedError()
270
271    def is_robolectric_test(self, module_name):
272        """Check if module is a robolectric test.
273
274        A module can be a robolectric test if the specified module has their
275        class set as ROBOLECTRIC (or shares their path with a module that does).
276
277        Args:
278            module_name: String of module to check.
279
280        Returns:
281            True if the module is a robolectric module, else False.
282        """
283        raise NotImplementedError()
284
285    def is_auto_gen_test_config(self, module_name):
286        """Check if the test config file will be generated automatically.
287
288        Args:
289            module_name: A string of the module name.
290
291        Returns:
292            True if the test config file will be generated automatically.
293        """
294        raise NotImplementedError()
295
296    def is_robolectric_module(self, mod_info):
297        """Check if a module is a robolectric module.
298
299        Args:
300            mod_info: ModuleInfo to check.
301
302        Returns:
303            True if module is a robolectric module, False otherwise.
304        """
305        raise NotImplementedError()
306
307    def is_native_test(self, module_name):
308        """Check if the input module is a native test.
309
310        Args:
311            module_name: A string of the module name.
312
313        Returns:
314            True if the test is a native test, False otherwise.
315        """
316        raise NotImplementedError()
317