• 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    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