xref: /aosp_15_r20/tools/asuite/aidegen/lib/project_info.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2018 - The Android Open Source Project
4*c2e18aaaSAndroid Build Coastguard Worker#
5*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*c2e18aaaSAndroid Build Coastguard Worker#
9*c2e18aaaSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*c2e18aaaSAndroid Build Coastguard Worker#
11*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License.
16*c2e18aaaSAndroid Build Coastguard Worker
17*c2e18aaaSAndroid Build Coastguard Worker"""Project information."""
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import absolute_import
20*c2e18aaaSAndroid Build Coastguard Worker
21*c2e18aaaSAndroid Build Coastguard Workerimport logging
22*c2e18aaaSAndroid Build Coastguard Workerimport os
23*c2e18aaaSAndroid Build Coastguard Workerimport time
24*c2e18aaaSAndroid Build Coastguard Worker
25*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import constant
26*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import aidegen_metrics
27*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import common_util
28*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import errors
29*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import module_info
30*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import project_config
31*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import source_locator
32*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.idea import iml
33*c2e18aaaSAndroid Build Coastguard Worker
34*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils
35*c2e18aaaSAndroid Build Coastguard Worker
36*c2e18aaaSAndroid Build Coastguard Worker_CONVERT_MK_URL = ('https://android.googlesource.com/platform/build/soong/'
37*c2e18aaaSAndroid Build Coastguard Worker                   '#convert-android_mk-files')
38*c2e18aaaSAndroid Build Coastguard Worker_ROBOLECTRIC_MODULE = 'Robolectric_all'
39*c2e18aaaSAndroid Build Coastguard Worker_NOT_TARGET = ('The module %s does not contain any Java or Kotlin file, '
40*c2e18aaaSAndroid Build Coastguard Worker               'therefore we skip this module in the project.')
41*c2e18aaaSAndroid Build Coastguard Worker# The module fake-framework have the same package name with framework but empty
42*c2e18aaaSAndroid Build Coastguard Worker# content. It will impact the dependency for framework when referencing the
43*c2e18aaaSAndroid Build Coastguard Worker# package from fake-framework in IntelliJ.
44*c2e18aaaSAndroid Build Coastguard Worker_EXCLUDE_MODULES = ['fake-framework']
45*c2e18aaaSAndroid Build Coastguard Worker# When we use atest_utils.build(), there is a command length limit on
46*c2e18aaaSAndroid Build Coastguard Worker# soong_ui.bash. We reserve 5000 characters for rewriting the command line
47*c2e18aaaSAndroid Build Coastguard Worker# in soong_ui.bash.
48*c2e18aaaSAndroid Build Coastguard Worker_CMD_LENGTH_BUFFER = 5000
49*c2e18aaaSAndroid Build Coastguard Worker# For each argument, it needs a space to separate following argument.
50*c2e18aaaSAndroid Build Coastguard Worker_BLANK_SIZE = 1
51*c2e18aaaSAndroid Build Coastguard Worker_CORE_MODULES = [constant.FRAMEWORK_ALL, constant.CORE_ALL,
52*c2e18aaaSAndroid Build Coastguard Worker                 'org.apache.http.legacy.stubs.system']
53*c2e18aaaSAndroid Build Coastguard Worker_BUILD_BP_JSON_ENV_ON = {
54*c2e18aaaSAndroid Build Coastguard Worker    constant.GEN_JAVA_DEPS: 'true',
55*c2e18aaaSAndroid Build Coastguard Worker    constant.GEN_CC_DEPS: 'true',
56*c2e18aaaSAndroid Build Coastguard Worker    constant.GEN_COMPDB: 'true',
57*c2e18aaaSAndroid Build Coastguard Worker    constant.GEN_RUST: 'true'
58*c2e18aaaSAndroid Build Coastguard Worker}
59*c2e18aaaSAndroid Build Coastguard Worker
60*c2e18aaaSAndroid Build Coastguard Worker
61*c2e18aaaSAndroid Build Coastguard Workerclass ProjectInfo:
62*c2e18aaaSAndroid Build Coastguard Worker    """Project information.
63*c2e18aaaSAndroid Build Coastguard Worker
64*c2e18aaaSAndroid Build Coastguard Worker    Users should call config_project first before starting using ProjectInfo.
65*c2e18aaaSAndroid Build Coastguard Worker
66*c2e18aaaSAndroid Build Coastguard Worker    Class attributes:
67*c2e18aaaSAndroid Build Coastguard Worker        modules_info: An AidegenModuleInfo instance whose name_to_module_info is
68*c2e18aaaSAndroid Build Coastguard Worker                      combining module-info.json with module_bp_java_deps.json.
69*c2e18aaaSAndroid Build Coastguard Worker        projects: A list of instances of ProjectInfo that are generated in an
70*c2e18aaaSAndroid Build Coastguard Worker                  AIDEGen command.
71*c2e18aaaSAndroid Build Coastguard Worker
72*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
73*c2e18aaaSAndroid Build Coastguard Worker        project_absolute_path: The absolute path of the project.
74*c2e18aaaSAndroid Build Coastguard Worker        project_relative_path: The relative path of the project to
75*c2e18aaaSAndroid Build Coastguard Worker                               common_util.get_android_root_dir().
76*c2e18aaaSAndroid Build Coastguard Worker        project_module_names: A set of module names under project_absolute_path
77*c2e18aaaSAndroid Build Coastguard Worker                              directory or it's subdirectories.
78*c2e18aaaSAndroid Build Coastguard Worker        dep_modules: A dict has recursively dependent modules of
79*c2e18aaaSAndroid Build Coastguard Worker                     project_module_names.
80*c2e18aaaSAndroid Build Coastguard Worker        iml_path: The project's iml file path.
81*c2e18aaaSAndroid Build Coastguard Worker        source_path: A dictionary to keep following data:
82*c2e18aaaSAndroid Build Coastguard Worker                     source_folder_path: A set contains the source folder
83*c2e18aaaSAndroid Build Coastguard Worker                                         relative paths.
84*c2e18aaaSAndroid Build Coastguard Worker                     test_folder_path: A set contains the test folder relative
85*c2e18aaaSAndroid Build Coastguard Worker                                       paths.
86*c2e18aaaSAndroid Build Coastguard Worker                     jar_path: A set contains the jar file paths.
87*c2e18aaaSAndroid Build Coastguard Worker                     jar_module_path: A dictionary contains the jar file and
88*c2e18aaaSAndroid Build Coastguard Worker                                      the module's path mapping, only used in
89*c2e18aaaSAndroid Build Coastguard Worker                                      Eclipse.
90*c2e18aaaSAndroid Build Coastguard Worker                     r_java_path: A set contains the relative path to the
91*c2e18aaaSAndroid Build Coastguard Worker                                  R.java files, only used in Eclipse.
92*c2e18aaaSAndroid Build Coastguard Worker                     srcjar_path: A source content descriptor only used in
93*c2e18aaaSAndroid Build Coastguard Worker                                  IntelliJ.
94*c2e18aaaSAndroid Build Coastguard Worker                                  e.g. out/.../aapt2.srcjar!/
95*c2e18aaaSAndroid Build Coastguard Worker                                  The "!/" is a content descriptor for
96*c2e18aaaSAndroid Build Coastguard Worker                                  compressed files in IntelliJ.
97*c2e18aaaSAndroid Build Coastguard Worker        is_main_project: A boolean to verify the project is main project.
98*c2e18aaaSAndroid Build Coastguard Worker        dependencies: A list of dependency projects' iml file names, e.g. base,
99*c2e18aaaSAndroid Build Coastguard Worker                      framework-all.
100*c2e18aaaSAndroid Build Coastguard Worker        iml_name: The iml project file name of this project.
101*c2e18aaaSAndroid Build Coastguard Worker        rel_out_soong_jar_path: A string of relative project path in the
102*c2e18aaaSAndroid Build Coastguard Worker                                'out/soong/.intermediates' directory, e.g., if
103*c2e18aaaSAndroid Build Coastguard Worker                                self.project_relative_path = 'frameworks/base'
104*c2e18aaaSAndroid Build Coastguard Worker                                the rel_out_soong_jar_path should be
105*c2e18aaaSAndroid Build Coastguard Worker                                'out/soong/.intermediates/frameworks/base/'.
106*c2e18aaaSAndroid Build Coastguard Worker    """
107*c2e18aaaSAndroid Build Coastguard Worker
108*c2e18aaaSAndroid Build Coastguard Worker    modules_info = None
109*c2e18aaaSAndroid Build Coastguard Worker    projects = []
110*c2e18aaaSAndroid Build Coastguard Worker
111*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, target=None, is_main_project=False):
112*c2e18aaaSAndroid Build Coastguard Worker        """ProjectInfo initialize.
113*c2e18aaaSAndroid Build Coastguard Worker
114*c2e18aaaSAndroid Build Coastguard Worker        Args:
115*c2e18aaaSAndroid Build Coastguard Worker            target: Includes target module or project path from user input, when
116*c2e18aaaSAndroid Build Coastguard Worker                    locating the target, project with matching module name of
117*c2e18aaaSAndroid Build Coastguard Worker                    the given target has a higher priority than project path.
118*c2e18aaaSAndroid Build Coastguard Worker            is_main_project: A boolean, default is False. True if the target is
119*c2e18aaaSAndroid Build Coastguard Worker                             the main project, otherwise False.
120*c2e18aaaSAndroid Build Coastguard Worker        """
121*c2e18aaaSAndroid Build Coastguard Worker        rel_path, abs_path = common_util.get_related_paths(
122*c2e18aaaSAndroid Build Coastguard Worker            self.modules_info, target)
123*c2e18aaaSAndroid Build Coastguard Worker        self.module_name = self.get_target_name(target, abs_path)
124*c2e18aaaSAndroid Build Coastguard Worker        self.is_main_project = is_main_project
125*c2e18aaaSAndroid Build Coastguard Worker        self.project_module_names = set(
126*c2e18aaaSAndroid Build Coastguard Worker            self.modules_info.get_module_names(rel_path))
127*c2e18aaaSAndroid Build Coastguard Worker        self.project_relative_path = rel_path
128*c2e18aaaSAndroid Build Coastguard Worker        self.project_absolute_path = abs_path
129*c2e18aaaSAndroid Build Coastguard Worker        self.iml_path = ''
130*c2e18aaaSAndroid Build Coastguard Worker        self._set_default_modules()
131*c2e18aaaSAndroid Build Coastguard Worker        self._init_source_path()
132*c2e18aaaSAndroid Build Coastguard Worker        if target == constant.FRAMEWORK_ALL:
133*c2e18aaaSAndroid Build Coastguard Worker            self.dep_modules = self.get_dep_modules([target])
134*c2e18aaaSAndroid Build Coastguard Worker        else:
135*c2e18aaaSAndroid Build Coastguard Worker            self.dep_modules = self.get_dep_modules()
136*c2e18aaaSAndroid Build Coastguard Worker        self._filter_out_modules()
137*c2e18aaaSAndroid Build Coastguard Worker        self.dependencies = []
138*c2e18aaaSAndroid Build Coastguard Worker        self.iml_name = iml.IMLGenerator.get_unique_iml_name(abs_path)
139*c2e18aaaSAndroid Build Coastguard Worker        self.rel_out_soong_jar_path = self._get_rel_project_out_soong_jar_path()
140*c2e18aaaSAndroid Build Coastguard Worker
141*c2e18aaaSAndroid Build Coastguard Worker    def _set_default_modules(self):
142*c2e18aaaSAndroid Build Coastguard Worker        """Append default hard-code modules, source paths and jar files.
143*c2e18aaaSAndroid Build Coastguard Worker
144*c2e18aaaSAndroid Build Coastguard Worker        1. framework: Framework module is always needed for dependencies, but it
145*c2e18aaaSAndroid Build Coastguard Worker            might not always be located by module dependency.
146*c2e18aaaSAndroid Build Coastguard Worker        2. org.apache.http.legacy.stubs.system: The module can't be located
147*c2e18aaaSAndroid Build Coastguard Worker            through module dependency. Without it, a lot of java files will have
148*c2e18aaaSAndroid Build Coastguard Worker            error of "cannot resolve symbol" in IntelliJ since they import
149*c2e18aaaSAndroid Build Coastguard Worker            packages android.Manifest and com.android.internal.R.
150*c2e18aaaSAndroid Build Coastguard Worker        """
151*c2e18aaaSAndroid Build Coastguard Worker        # Set the default modules framework-all and core-all as the core
152*c2e18aaaSAndroid Build Coastguard Worker        # dependency modules.
153*c2e18aaaSAndroid Build Coastguard Worker        self.project_module_names.update(_CORE_MODULES)
154*c2e18aaaSAndroid Build Coastguard Worker
155*c2e18aaaSAndroid Build Coastguard Worker    def _init_source_path(self):
156*c2e18aaaSAndroid Build Coastguard Worker        """Initialize source_path dictionary."""
157*c2e18aaaSAndroid Build Coastguard Worker        self.source_path = {
158*c2e18aaaSAndroid Build Coastguard Worker            'source_folder_path': set(),
159*c2e18aaaSAndroid Build Coastguard Worker            'test_folder_path': set(),
160*c2e18aaaSAndroid Build Coastguard Worker            'jar_path': set(),
161*c2e18aaaSAndroid Build Coastguard Worker            'jar_module_path': {},
162*c2e18aaaSAndroid Build Coastguard Worker            'r_java_path': set(),
163*c2e18aaaSAndroid Build Coastguard Worker            'srcjar_path': set()
164*c2e18aaaSAndroid Build Coastguard Worker        }
165*c2e18aaaSAndroid Build Coastguard Worker
166*c2e18aaaSAndroid Build Coastguard Worker    def _search_android_make_files(self):
167*c2e18aaaSAndroid Build Coastguard Worker        """Search project and dependency modules contain Android.mk files.
168*c2e18aaaSAndroid Build Coastguard Worker
169*c2e18aaaSAndroid Build Coastguard Worker        If there is only Android.mk but no Android.bp, we'll show the warning
170*c2e18aaaSAndroid Build Coastguard Worker        message, otherwise we won't.
171*c2e18aaaSAndroid Build Coastguard Worker
172*c2e18aaaSAndroid Build Coastguard Worker        Yields:
173*c2e18aaaSAndroid Build Coastguard Worker            A string: the relative path of Android.mk.
174*c2e18aaaSAndroid Build Coastguard Worker        """
175*c2e18aaaSAndroid Build Coastguard Worker        if (common_util.exist_android_mk(self.project_absolute_path) and
176*c2e18aaaSAndroid Build Coastguard Worker                not common_util.exist_android_bp(self.project_absolute_path)):
177*c2e18aaaSAndroid Build Coastguard Worker            yield '\t' + os.path.join(self.project_relative_path,
178*c2e18aaaSAndroid Build Coastguard Worker                                      constant.ANDROID_MK)
179*c2e18aaaSAndroid Build Coastguard Worker        for mod_name in self.dep_modules:
180*c2e18aaaSAndroid Build Coastguard Worker            rel_path, abs_path = common_util.get_related_paths(
181*c2e18aaaSAndroid Build Coastguard Worker                self.modules_info, mod_name)
182*c2e18aaaSAndroid Build Coastguard Worker            if rel_path and abs_path:
183*c2e18aaaSAndroid Build Coastguard Worker                if (common_util.exist_android_mk(abs_path)
184*c2e18aaaSAndroid Build Coastguard Worker                        and not common_util.exist_android_bp(abs_path)):
185*c2e18aaaSAndroid Build Coastguard Worker                    yield '\t' + os.path.join(rel_path, constant.ANDROID_MK)
186*c2e18aaaSAndroid Build Coastguard Worker
187*c2e18aaaSAndroid Build Coastguard Worker    def _get_modules_under_project_path(self, rel_path):
188*c2e18aaaSAndroid Build Coastguard Worker        """Find qualified modules under the rel_path.
189*c2e18aaaSAndroid Build Coastguard Worker
190*c2e18aaaSAndroid Build Coastguard Worker        Find modules which contain any Java or Kotlin file as a target module.
191*c2e18aaaSAndroid Build Coastguard Worker        If it's the whole source tree project, add all modules into it.
192*c2e18aaaSAndroid Build Coastguard Worker
193*c2e18aaaSAndroid Build Coastguard Worker        Args:
194*c2e18aaaSAndroid Build Coastguard Worker            rel_path: A string, the project's relative path.
195*c2e18aaaSAndroid Build Coastguard Worker
196*c2e18aaaSAndroid Build Coastguard Worker        Returns:
197*c2e18aaaSAndroid Build Coastguard Worker            A set of module names.
198*c2e18aaaSAndroid Build Coastguard Worker        """
199*c2e18aaaSAndroid Build Coastguard Worker        logging.info('Find modules contain any Java or Kotlin file under %s.',
200*c2e18aaaSAndroid Build Coastguard Worker                     rel_path)
201*c2e18aaaSAndroid Build Coastguard Worker        if rel_path == '':
202*c2e18aaaSAndroid Build Coastguard Worker            return self.modules_info.name_to_module_info.keys()
203*c2e18aaaSAndroid Build Coastguard Worker        modules = set()
204*c2e18aaaSAndroid Build Coastguard Worker        root_dir = common_util.get_android_root_dir()
205*c2e18aaaSAndroid Build Coastguard Worker        for name, data in self.modules_info.name_to_module_info.items():
206*c2e18aaaSAndroid Build Coastguard Worker            if module_info.AidegenModuleInfo.is_project_path_relative_module(
207*c2e18aaaSAndroid Build Coastguard Worker                    data, rel_path):
208*c2e18aaaSAndroid Build Coastguard Worker                if common_util.check_java_or_kotlin_file_exists(
209*c2e18aaaSAndroid Build Coastguard Worker                        os.path.join(root_dir, data[constant.KEY_PATH][0])):
210*c2e18aaaSAndroid Build Coastguard Worker                    modules.add(name)
211*c2e18aaaSAndroid Build Coastguard Worker                else:
212*c2e18aaaSAndroid Build Coastguard Worker                    logging.debug(_NOT_TARGET, name)
213*c2e18aaaSAndroid Build Coastguard Worker        return modules
214*c2e18aaaSAndroid Build Coastguard Worker
215*c2e18aaaSAndroid Build Coastguard Worker    def _get_robolectric_dep_module(self, modules):
216*c2e18aaaSAndroid Build Coastguard Worker        """Return the robolectric module set as dependency if any module is a
217*c2e18aaaSAndroid Build Coastguard Worker           robolectric test.
218*c2e18aaaSAndroid Build Coastguard Worker
219*c2e18aaaSAndroid Build Coastguard Worker        Args:
220*c2e18aaaSAndroid Build Coastguard Worker            modules: A set of modules.
221*c2e18aaaSAndroid Build Coastguard Worker
222*c2e18aaaSAndroid Build Coastguard Worker        Returns:
223*c2e18aaaSAndroid Build Coastguard Worker            A set with a robolectric_all module name if one of the modules
224*c2e18aaaSAndroid Build Coastguard Worker            needs the robolectric test module. Otherwise return empty list.
225*c2e18aaaSAndroid Build Coastguard Worker        """
226*c2e18aaaSAndroid Build Coastguard Worker        for module in modules:
227*c2e18aaaSAndroid Build Coastguard Worker            if self.modules_info.is_robolectric_test(module):
228*c2e18aaaSAndroid Build Coastguard Worker                return {_ROBOLECTRIC_MODULE}
229*c2e18aaaSAndroid Build Coastguard Worker        return set()
230*c2e18aaaSAndroid Build Coastguard Worker
231*c2e18aaaSAndroid Build Coastguard Worker    def _filter_out_modules(self):
232*c2e18aaaSAndroid Build Coastguard Worker        """Filter out unnecessary modules."""
233*c2e18aaaSAndroid Build Coastguard Worker        for module in _EXCLUDE_MODULES:
234*c2e18aaaSAndroid Build Coastguard Worker            self.dep_modules.pop(module, None)
235*c2e18aaaSAndroid Build Coastguard Worker
236*c2e18aaaSAndroid Build Coastguard Worker    def get_dep_modules(self, module_names=None, depth=0):
237*c2e18aaaSAndroid Build Coastguard Worker        """Recursively find dependent modules of the project.
238*c2e18aaaSAndroid Build Coastguard Worker
239*c2e18aaaSAndroid Build Coastguard Worker        Find dependent modules by dependencies parameter of each module.
240*c2e18aaaSAndroid Build Coastguard Worker        For example:
241*c2e18aaaSAndroid Build Coastguard Worker            The module_names is ['m1'].
242*c2e18aaaSAndroid Build Coastguard Worker            The modules_info is
243*c2e18aaaSAndroid Build Coastguard Worker            {
244*c2e18aaaSAndroid Build Coastguard Worker                'm1': {'dependencies': ['m2'], 'path': ['path_to_m1']},
245*c2e18aaaSAndroid Build Coastguard Worker                'm2': {'path': ['path_to_m4']},
246*c2e18aaaSAndroid Build Coastguard Worker                'm3': {'path': ['path_to_m1']}
247*c2e18aaaSAndroid Build Coastguard Worker                'm4': {'path': []}
248*c2e18aaaSAndroid Build Coastguard Worker            }
249*c2e18aaaSAndroid Build Coastguard Worker            The result dependent modules are:
250*c2e18aaaSAndroid Build Coastguard Worker            {
251*c2e18aaaSAndroid Build Coastguard Worker                'm1': {'dependencies': ['m2'], 'path': ['path_to_m1']
252*c2e18aaaSAndroid Build Coastguard Worker                       'depth': 0},
253*c2e18aaaSAndroid Build Coastguard Worker                'm2': {'path': ['path_to_m4'], 'depth': 1},
254*c2e18aaaSAndroid Build Coastguard Worker                'm3': {'path': ['path_to_m1'], 'depth': 0}
255*c2e18aaaSAndroid Build Coastguard Worker            }
256*c2e18aaaSAndroid Build Coastguard Worker            Note that:
257*c2e18aaaSAndroid Build Coastguard Worker                1. m4 is not in the result as it's not among dependent modules.
258*c2e18aaaSAndroid Build Coastguard Worker                2. m3 is in the result as it has the same path to m1.
259*c2e18aaaSAndroid Build Coastguard Worker
260*c2e18aaaSAndroid Build Coastguard Worker        Args:
261*c2e18aaaSAndroid Build Coastguard Worker            module_names: A set of module names.
262*c2e18aaaSAndroid Build Coastguard Worker            depth: An integer shows the depth of module dependency referenced by
263*c2e18aaaSAndroid Build Coastguard Worker                   source. Zero means the max module depth.
264*c2e18aaaSAndroid Build Coastguard Worker
265*c2e18aaaSAndroid Build Coastguard Worker        Returns:
266*c2e18aaaSAndroid Build Coastguard Worker            deps: A dict contains all dependent modules data of given modules.
267*c2e18aaaSAndroid Build Coastguard Worker        """
268*c2e18aaaSAndroid Build Coastguard Worker        dep = {}
269*c2e18aaaSAndroid Build Coastguard Worker        children = set()
270*c2e18aaaSAndroid Build Coastguard Worker        if not module_names:
271*c2e18aaaSAndroid Build Coastguard Worker            module_names = self.project_module_names
272*c2e18aaaSAndroid Build Coastguard Worker            module_names.update(
273*c2e18aaaSAndroid Build Coastguard Worker                self._get_modules_under_project_path(
274*c2e18aaaSAndroid Build Coastguard Worker                    self.project_relative_path))
275*c2e18aaaSAndroid Build Coastguard Worker            module_names.update(self._get_robolectric_dep_module(module_names))
276*c2e18aaaSAndroid Build Coastguard Worker            self.project_module_names = set()
277*c2e18aaaSAndroid Build Coastguard Worker        for name in module_names:
278*c2e18aaaSAndroid Build Coastguard Worker            if (name in self.modules_info.name_to_module_info
279*c2e18aaaSAndroid Build Coastguard Worker                    and name not in self.project_module_names):
280*c2e18aaaSAndroid Build Coastguard Worker                dep[name] = self.modules_info.name_to_module_info[name]
281*c2e18aaaSAndroid Build Coastguard Worker                dep[name][constant.KEY_DEPTH] = depth
282*c2e18aaaSAndroid Build Coastguard Worker                self.project_module_names.add(name)
283*c2e18aaaSAndroid Build Coastguard Worker                if (constant.KEY_DEPENDENCIES in dep[name]
284*c2e18aaaSAndroid Build Coastguard Worker                        and dep[name][constant.KEY_DEPENDENCIES]):
285*c2e18aaaSAndroid Build Coastguard Worker                    children.update(dep[name][constant.KEY_DEPENDENCIES])
286*c2e18aaaSAndroid Build Coastguard Worker        if children:
287*c2e18aaaSAndroid Build Coastguard Worker            dep.update(self.get_dep_modules(children, depth + 1))
288*c2e18aaaSAndroid Build Coastguard Worker        return dep
289*c2e18aaaSAndroid Build Coastguard Worker
290*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
291*c2e18aaaSAndroid Build Coastguard Worker    def generate_projects(targets):
292*c2e18aaaSAndroid Build Coastguard Worker        """Generate a list of projects in one time by a list of module names.
293*c2e18aaaSAndroid Build Coastguard Worker
294*c2e18aaaSAndroid Build Coastguard Worker        Args:
295*c2e18aaaSAndroid Build Coastguard Worker            targets: A list of target modules or project paths from user input,
296*c2e18aaaSAndroid Build Coastguard Worker                     when locating the target, project with matched module name
297*c2e18aaaSAndroid Build Coastguard Worker                     of the target has a higher priority than project path.
298*c2e18aaaSAndroid Build Coastguard Worker
299*c2e18aaaSAndroid Build Coastguard Worker        Returns:
300*c2e18aaaSAndroid Build Coastguard Worker            List: A list of ProjectInfo instances.
301*c2e18aaaSAndroid Build Coastguard Worker        """
302*c2e18aaaSAndroid Build Coastguard Worker        return [ProjectInfo(target, i == 0) for i, target in enumerate(targets)]
303*c2e18aaaSAndroid Build Coastguard Worker
304*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
305*c2e18aaaSAndroid Build Coastguard Worker    def get_target_name(target, abs_path):
306*c2e18aaaSAndroid Build Coastguard Worker        """Get target name from target's absolute path.
307*c2e18aaaSAndroid Build Coastguard Worker
308*c2e18aaaSAndroid Build Coastguard Worker        If the project is for entire Android source tree, change the target to
309*c2e18aaaSAndroid Build Coastguard Worker        source tree's root folder name. In this way, we give IDE project file
310*c2e18aaaSAndroid Build Coastguard Worker        a more specific name. e.g, master.iml.
311*c2e18aaaSAndroid Build Coastguard Worker
312*c2e18aaaSAndroid Build Coastguard Worker        Args:
313*c2e18aaaSAndroid Build Coastguard Worker            target: Includes target module or project path from user input, when
314*c2e18aaaSAndroid Build Coastguard Worker                    locating the target, project with matching module name of
315*c2e18aaaSAndroid Build Coastguard Worker                    the given target has a higher priority than project path.
316*c2e18aaaSAndroid Build Coastguard Worker            abs_path: A string, target's absolute path.
317*c2e18aaaSAndroid Build Coastguard Worker
318*c2e18aaaSAndroid Build Coastguard Worker        Returns:
319*c2e18aaaSAndroid Build Coastguard Worker            A string, the target name.
320*c2e18aaaSAndroid Build Coastguard Worker        """
321*c2e18aaaSAndroid Build Coastguard Worker        if abs_path == common_util.get_android_root_dir():
322*c2e18aaaSAndroid Build Coastguard Worker            return os.path.basename(abs_path)
323*c2e18aaaSAndroid Build Coastguard Worker        return target
324*c2e18aaaSAndroid Build Coastguard Worker
325*c2e18aaaSAndroid Build Coastguard Worker    def locate_source(self, build=True):
326*c2e18aaaSAndroid Build Coastguard Worker        """Locate the paths of dependent source folders and jar files.
327*c2e18aaaSAndroid Build Coastguard Worker
328*c2e18aaaSAndroid Build Coastguard Worker        Try to reference source folder path as dependent module unless the
329*c2e18aaaSAndroid Build Coastguard Worker        dependent module should be referenced to a jar file, such as modules
330*c2e18aaaSAndroid Build Coastguard Worker        have jars and jarjar_rules parameter.
331*c2e18aaaSAndroid Build Coastguard Worker        For example:
332*c2e18aaaSAndroid Build Coastguard Worker            Module: asm-6.0
333*c2e18aaaSAndroid Build Coastguard Worker                java_import {
334*c2e18aaaSAndroid Build Coastguard Worker                    name: 'asm-6.0',
335*c2e18aaaSAndroid Build Coastguard Worker                    host_supported: true,
336*c2e18aaaSAndroid Build Coastguard Worker                    jars: ['asm-6.0.jar'],
337*c2e18aaaSAndroid Build Coastguard Worker                }
338*c2e18aaaSAndroid Build Coastguard Worker            Module: bouncycastle
339*c2e18aaaSAndroid Build Coastguard Worker                java_library {
340*c2e18aaaSAndroid Build Coastguard Worker                    name: 'bouncycastle',
341*c2e18aaaSAndroid Build Coastguard Worker                    ...
342*c2e18aaaSAndroid Build Coastguard Worker                    target: {
343*c2e18aaaSAndroid Build Coastguard Worker                        android: {
344*c2e18aaaSAndroid Build Coastguard Worker                            jarjar_rules: 'jarjar-rules.txt',
345*c2e18aaaSAndroid Build Coastguard Worker                        },
346*c2e18aaaSAndroid Build Coastguard Worker                    },
347*c2e18aaaSAndroid Build Coastguard Worker                }
348*c2e18aaaSAndroid Build Coastguard Worker
349*c2e18aaaSAndroid Build Coastguard Worker        Args:
350*c2e18aaaSAndroid Build Coastguard Worker            build: A boolean default to true. If false, skip building jar and
351*c2e18aaaSAndroid Build Coastguard Worker                   srcjar files, otherwise build them.
352*c2e18aaaSAndroid Build Coastguard Worker
353*c2e18aaaSAndroid Build Coastguard Worker        Example usage:
354*c2e18aaaSAndroid Build Coastguard Worker            project.source_path = project.locate_source()
355*c2e18aaaSAndroid Build Coastguard Worker            E.g.
356*c2e18aaaSAndroid Build Coastguard Worker                project.source_path = {
357*c2e18aaaSAndroid Build Coastguard Worker                    'source_folder_path': ['path/to/source/folder1',
358*c2e18aaaSAndroid Build Coastguard Worker                                           'path/to/source/folder2', ...],
359*c2e18aaaSAndroid Build Coastguard Worker                    'test_folder_path': ['path/to/test/folder', ...],
360*c2e18aaaSAndroid Build Coastguard Worker                    'jar_path': ['path/to/jar/file1', 'path/to/jar/file2', ...]
361*c2e18aaaSAndroid Build Coastguard Worker                }
362*c2e18aaaSAndroid Build Coastguard Worker        """
363*c2e18aaaSAndroid Build Coastguard Worker        if not hasattr(self, 'dep_modules') or not self.dep_modules:
364*c2e18aaaSAndroid Build Coastguard Worker            raise errors.EmptyModuleDependencyError(
365*c2e18aaaSAndroid Build Coastguard Worker                'Dependent modules dictionary is empty.')
366*c2e18aaaSAndroid Build Coastguard Worker        rebuild_targets = set()
367*c2e18aaaSAndroid Build Coastguard Worker        for module_name, module_data in self.dep_modules.items():
368*c2e18aaaSAndroid Build Coastguard Worker            module = self._generate_moduledata(module_name, module_data)
369*c2e18aaaSAndroid Build Coastguard Worker            module.locate_sources_path()
370*c2e18aaaSAndroid Build Coastguard Worker            self.source_path['source_folder_path'].update(set(module.src_dirs))
371*c2e18aaaSAndroid Build Coastguard Worker            self.source_path['test_folder_path'].update(set(module.test_dirs))
372*c2e18aaaSAndroid Build Coastguard Worker            self.source_path['r_java_path'].update(set(module.r_java_paths))
373*c2e18aaaSAndroid Build Coastguard Worker            self.source_path['srcjar_path'].update(set(module.srcjar_paths))
374*c2e18aaaSAndroid Build Coastguard Worker            self._append_jars_as_dependencies(module)
375*c2e18aaaSAndroid Build Coastguard Worker            rebuild_targets.update(module.build_targets)
376*c2e18aaaSAndroid Build Coastguard Worker        config = project_config.ProjectConfig.get_instance()
377*c2e18aaaSAndroid Build Coastguard Worker        if config.is_skip_build:
378*c2e18aaaSAndroid Build Coastguard Worker            return
379*c2e18aaaSAndroid Build Coastguard Worker        if rebuild_targets:
380*c2e18aaaSAndroid Build Coastguard Worker            if build:
381*c2e18aaaSAndroid Build Coastguard Worker                logging.info('\nThe batch_build_dependencies function is '
382*c2e18aaaSAndroid Build Coastguard Worker                             'called by ProjectInfo\'s locate_source method.')
383*c2e18aaaSAndroid Build Coastguard Worker                batch_build_dependencies(rebuild_targets)
384*c2e18aaaSAndroid Build Coastguard Worker                self.locate_source(build=False)
385*c2e18aaaSAndroid Build Coastguard Worker            else:
386*c2e18aaaSAndroid Build Coastguard Worker                logging.warning('Jar or srcjar files build skipped:\n\t%s.',
387*c2e18aaaSAndroid Build Coastguard Worker                                '\n\t'.join(rebuild_targets))
388*c2e18aaaSAndroid Build Coastguard Worker
389*c2e18aaaSAndroid Build Coastguard Worker    def _generate_moduledata(self, module_name, module_data):
390*c2e18aaaSAndroid Build Coastguard Worker        """Generate a module class to collect dependencies in IDE.
391*c2e18aaaSAndroid Build Coastguard Worker
392*c2e18aaaSAndroid Build Coastguard Worker        The rules of initialize a module data instance: if ide_object isn't None
393*c2e18aaaSAndroid Build Coastguard Worker        and its ide_name is 'eclipse', we'll create an EclipseModuleData
394*c2e18aaaSAndroid Build Coastguard Worker        instance otherwise create a ModuleData instance.
395*c2e18aaaSAndroid Build Coastguard Worker
396*c2e18aaaSAndroid Build Coastguard Worker        Args:
397*c2e18aaaSAndroid Build Coastguard Worker            module_name: Name of the module.
398*c2e18aaaSAndroid Build Coastguard Worker            module_data: A dictionary holding a module information.
399*c2e18aaaSAndroid Build Coastguard Worker
400*c2e18aaaSAndroid Build Coastguard Worker        Returns:
401*c2e18aaaSAndroid Build Coastguard Worker            A ModuleData class.
402*c2e18aaaSAndroid Build Coastguard Worker        """
403*c2e18aaaSAndroid Build Coastguard Worker        ide_name = project_config.ProjectConfig.get_instance().ide_name
404*c2e18aaaSAndroid Build Coastguard Worker        if ide_name == constant.IDE_ECLIPSE:
405*c2e18aaaSAndroid Build Coastguard Worker            return source_locator.EclipseModuleData(
406*c2e18aaaSAndroid Build Coastguard Worker                module_name, module_data, self.project_relative_path)
407*c2e18aaaSAndroid Build Coastguard Worker        depth = project_config.ProjectConfig.get_instance().depth
408*c2e18aaaSAndroid Build Coastguard Worker        return source_locator.ModuleData(module_name, module_data, depth)
409*c2e18aaaSAndroid Build Coastguard Worker
410*c2e18aaaSAndroid Build Coastguard Worker    def _append_jars_as_dependencies(self, module):
411*c2e18aaaSAndroid Build Coastguard Worker        """Add given module's jar files into dependent_data as dependencies.
412*c2e18aaaSAndroid Build Coastguard Worker
413*c2e18aaaSAndroid Build Coastguard Worker        Args:
414*c2e18aaaSAndroid Build Coastguard Worker            module: A ModuleData instance.
415*c2e18aaaSAndroid Build Coastguard Worker        """
416*c2e18aaaSAndroid Build Coastguard Worker        if module.jar_files:
417*c2e18aaaSAndroid Build Coastguard Worker            self.source_path['jar_path'].update(module.jar_files)
418*c2e18aaaSAndroid Build Coastguard Worker            for jar in list(module.jar_files):
419*c2e18aaaSAndroid Build Coastguard Worker                self.source_path['jar_module_path'].update({
420*c2e18aaaSAndroid Build Coastguard Worker                    jar:
421*c2e18aaaSAndroid Build Coastguard Worker                    module.module_path
422*c2e18aaaSAndroid Build Coastguard Worker                })
423*c2e18aaaSAndroid Build Coastguard Worker        # Collecting the jar files of default core modules as dependencies.
424*c2e18aaaSAndroid Build Coastguard Worker        if constant.KEY_DEPENDENCIES in module.module_data:
425*c2e18aaaSAndroid Build Coastguard Worker            self.source_path['jar_path'].update([
426*c2e18aaaSAndroid Build Coastguard Worker                x for x in module.module_data[constant.KEY_DEPENDENCIES]
427*c2e18aaaSAndroid Build Coastguard Worker                if common_util.is_target(x, constant.TARGET_LIBS)
428*c2e18aaaSAndroid Build Coastguard Worker            ])
429*c2e18aaaSAndroid Build Coastguard Worker
430*c2e18aaaSAndroid Build Coastguard Worker    def _get_rel_project_out_soong_jar_path(self):
431*c2e18aaaSAndroid Build Coastguard Worker        """Gets the projects' jar path in 'out/soong/.intermediates' folder.
432*c2e18aaaSAndroid Build Coastguard Worker
433*c2e18aaaSAndroid Build Coastguard Worker        Gets the relative project's jar path in the 'out/soong/.intermediates'
434*c2e18aaaSAndroid Build Coastguard Worker        directory. For example, if the self.project_relative_path is
435*c2e18aaaSAndroid Build Coastguard Worker        'frameworks/base', the returned value should be
436*c2e18aaaSAndroid Build Coastguard Worker        'out/soong/.intermediates/frameworks/base/'.
437*c2e18aaaSAndroid Build Coastguard Worker
438*c2e18aaaSAndroid Build Coastguard Worker        Returns:
439*c2e18aaaSAndroid Build Coastguard Worker            A string of relative project path in out/soong/.intermediates/
440*c2e18aaaSAndroid Build Coastguard Worker            directory, e.g. 'out/soong/.intermediates/frameworks/base/'.
441*c2e18aaaSAndroid Build Coastguard Worker        """
442*c2e18aaaSAndroid Build Coastguard Worker        rdir = os.path.relpath(common_util.get_soong_out_path(),
443*c2e18aaaSAndroid Build Coastguard Worker                               common_util.get_android_root_dir())
444*c2e18aaaSAndroid Build Coastguard Worker        return os.sep.join(
445*c2e18aaaSAndroid Build Coastguard Worker            [rdir, constant.INTERMEDIATES, self.project_relative_path]) + os.sep
446*c2e18aaaSAndroid Build Coastguard Worker
447*c2e18aaaSAndroid Build Coastguard Worker    @classmethod
448*c2e18aaaSAndroid Build Coastguard Worker    def multi_projects_locate_source(cls, projects):
449*c2e18aaaSAndroid Build Coastguard Worker        """Locate the paths of dependent source folders and jar files.
450*c2e18aaaSAndroid Build Coastguard Worker
451*c2e18aaaSAndroid Build Coastguard Worker        Args:
452*c2e18aaaSAndroid Build Coastguard Worker            projects: A list of ProjectInfo instances. Information of a project
453*c2e18aaaSAndroid Build Coastguard Worker                      such as project relative path, project real path, project
454*c2e18aaaSAndroid Build Coastguard Worker                      dependencies.
455*c2e18aaaSAndroid Build Coastguard Worker        """
456*c2e18aaaSAndroid Build Coastguard Worker        cls.projects = projects
457*c2e18aaaSAndroid Build Coastguard Worker        for project in projects:
458*c2e18aaaSAndroid Build Coastguard Worker            project.locate_source()
459*c2e18aaaSAndroid Build Coastguard Worker            _update_iml_dep_modules(project)
460*c2e18aaaSAndroid Build Coastguard Worker
461*c2e18aaaSAndroid Build Coastguard Worker
462*c2e18aaaSAndroid Build Coastguard Workerclass MultiProjectsInfo(ProjectInfo):
463*c2e18aaaSAndroid Build Coastguard Worker    """Multiple projects info.
464*c2e18aaaSAndroid Build Coastguard Worker
465*c2e18aaaSAndroid Build Coastguard Worker    Usage example:
466*c2e18aaaSAndroid Build Coastguard Worker        if folder_base:
467*c2e18aaaSAndroid Build Coastguard Worker            project = MultiProjectsInfo(['module_name'])
468*c2e18aaaSAndroid Build Coastguard Worker            project.collect_all_dep_modules()
469*c2e18aaaSAndroid Build Coastguard Worker            project.gen_folder_base_dependencies()
470*c2e18aaaSAndroid Build Coastguard Worker        else:
471*c2e18aaaSAndroid Build Coastguard Worker            ProjectInfo.generate_projects(['module_name'])
472*c2e18aaaSAndroid Build Coastguard Worker
473*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
474*c2e18aaaSAndroid Build Coastguard Worker        _targets: A list of module names or project paths.
475*c2e18aaaSAndroid Build Coastguard Worker        path_to_sources: A dictionary of modules' sources, the module's path
476*c2e18aaaSAndroid Build Coastguard Worker                         as key and the sources as value.
477*c2e18aaaSAndroid Build Coastguard Worker                         e.g.
478*c2e18aaaSAndroid Build Coastguard Worker                         {
479*c2e18aaaSAndroid Build Coastguard Worker                             'frameworks/base': {
480*c2e18aaaSAndroid Build Coastguard Worker                                 'src_dirs': [],
481*c2e18aaaSAndroid Build Coastguard Worker                                 'test_dirs': [],
482*c2e18aaaSAndroid Build Coastguard Worker                                 'r_java_paths': [],
483*c2e18aaaSAndroid Build Coastguard Worker                                 'srcjar_paths': [],
484*c2e18aaaSAndroid Build Coastguard Worker                                 'jar_files': [],
485*c2e18aaaSAndroid Build Coastguard Worker                                 'dep_paths': [],
486*c2e18aaaSAndroid Build Coastguard Worker                             }
487*c2e18aaaSAndroid Build Coastguard Worker                         }
488*c2e18aaaSAndroid Build Coastguard Worker    """
489*c2e18aaaSAndroid Build Coastguard Worker
490*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, targets=None):
491*c2e18aaaSAndroid Build Coastguard Worker        """MultiProjectsInfo initialize.
492*c2e18aaaSAndroid Build Coastguard Worker
493*c2e18aaaSAndroid Build Coastguard Worker        Args:
494*c2e18aaaSAndroid Build Coastguard Worker            targets: A list of module names or project paths from user's input.
495*c2e18aaaSAndroid Build Coastguard Worker        """
496*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(targets[0], True)
497*c2e18aaaSAndroid Build Coastguard Worker        self._targets = targets
498*c2e18aaaSAndroid Build Coastguard Worker        self.path_to_sources = {}
499*c2e18aaaSAndroid Build Coastguard Worker
500*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
501*c2e18aaaSAndroid Build Coastguard Worker    def _clear_srcjar_paths(module):
502*c2e18aaaSAndroid Build Coastguard Worker        """Clears the srcjar_paths.
503*c2e18aaaSAndroid Build Coastguard Worker
504*c2e18aaaSAndroid Build Coastguard Worker        Args:
505*c2e18aaaSAndroid Build Coastguard Worker            module: A ModuleData instance.
506*c2e18aaaSAndroid Build Coastguard Worker        """
507*c2e18aaaSAndroid Build Coastguard Worker        module.srcjar_paths = []
508*c2e18aaaSAndroid Build Coastguard Worker
509*c2e18aaaSAndroid Build Coastguard Worker    def _collect_framework_srcjar_info(self, module):
510*c2e18aaaSAndroid Build Coastguard Worker        """Clears the framework's srcjars.
511*c2e18aaaSAndroid Build Coastguard Worker
512*c2e18aaaSAndroid Build Coastguard Worker        Args:
513*c2e18aaaSAndroid Build Coastguard Worker            module: A ModuleData instance.
514*c2e18aaaSAndroid Build Coastguard Worker        """
515*c2e18aaaSAndroid Build Coastguard Worker        if module.module_path == constant.FRAMEWORK_PATH:
516*c2e18aaaSAndroid Build Coastguard Worker            framework_srcjar_path = os.path.join(constant.FRAMEWORK_PATH,
517*c2e18aaaSAndroid Build Coastguard Worker                                                 constant.FRAMEWORK_SRCJARS)
518*c2e18aaaSAndroid Build Coastguard Worker            if module.module_name == constant.FRAMEWORK_ALL:
519*c2e18aaaSAndroid Build Coastguard Worker                self.path_to_sources[framework_srcjar_path] = {
520*c2e18aaaSAndroid Build Coastguard Worker                    'src_dirs': [],
521*c2e18aaaSAndroid Build Coastguard Worker                    'test_dirs': [],
522*c2e18aaaSAndroid Build Coastguard Worker                    'r_java_paths': [],
523*c2e18aaaSAndroid Build Coastguard Worker                    'srcjar_paths': module.srcjar_paths,
524*c2e18aaaSAndroid Build Coastguard Worker                    'jar_files': [],
525*c2e18aaaSAndroid Build Coastguard Worker                    'dep_paths': [constant.FRAMEWORK_PATH],
526*c2e18aaaSAndroid Build Coastguard Worker                }
527*c2e18aaaSAndroid Build Coastguard Worker            # In the folder base case, AIDEGen has to ignore all module's srcjar
528*c2e18aaaSAndroid Build Coastguard Worker            # files under the frameworks/base except the framework-all. Because
529*c2e18aaaSAndroid Build Coastguard Worker            # there are too many duplicate srcjars of modules under the
530*c2e18aaaSAndroid Build Coastguard Worker            # frameworks/base. So that AIDEGen keeps the srcjar files only from
531*c2e18aaaSAndroid Build Coastguard Worker            # the framework-all module. Other modules' srcjar files will be
532*c2e18aaaSAndroid Build Coastguard Worker            # removed. However, when users choose the module base case, srcjar
533*c2e18aaaSAndroid Build Coastguard Worker            # files will be collected by the ProjectInfo class, so that the
534*c2e18aaaSAndroid Build Coastguard Worker            # removing srcjar_paths in this class does not impact the
535*c2e18aaaSAndroid Build Coastguard Worker            # srcjar_paths collection of modules in the ProjectInfo class.
536*c2e18aaaSAndroid Build Coastguard Worker            self._clear_srcjar_paths(module)
537*c2e18aaaSAndroid Build Coastguard Worker
538*c2e18aaaSAndroid Build Coastguard Worker    def collect_all_dep_modules(self):
539*c2e18aaaSAndroid Build Coastguard Worker        """Collects all dependency modules for the projects."""
540*c2e18aaaSAndroid Build Coastguard Worker        self.project_module_names.clear()
541*c2e18aaaSAndroid Build Coastguard Worker        module_names = set(_CORE_MODULES)
542*c2e18aaaSAndroid Build Coastguard Worker        for target in self._targets:
543*c2e18aaaSAndroid Build Coastguard Worker            relpath, _ = common_util.get_related_paths(self.modules_info,
544*c2e18aaaSAndroid Build Coastguard Worker                                                       target)
545*c2e18aaaSAndroid Build Coastguard Worker            module_names.update(self._get_modules_under_project_path(relpath))
546*c2e18aaaSAndroid Build Coastguard Worker        module_names.update(self._get_robolectric_dep_module(module_names))
547*c2e18aaaSAndroid Build Coastguard Worker        self.dep_modules = self.get_dep_modules(module_names)
548*c2e18aaaSAndroid Build Coastguard Worker
549*c2e18aaaSAndroid Build Coastguard Worker    def gen_folder_base_dependencies(self, module):
550*c2e18aaaSAndroid Build Coastguard Worker        """Generates the folder base dependencies dictionary.
551*c2e18aaaSAndroid Build Coastguard Worker
552*c2e18aaaSAndroid Build Coastguard Worker        Args:
553*c2e18aaaSAndroid Build Coastguard Worker            module: A ModuleData instance.
554*c2e18aaaSAndroid Build Coastguard Worker        """
555*c2e18aaaSAndroid Build Coastguard Worker        mod_path = module.module_path
556*c2e18aaaSAndroid Build Coastguard Worker        if not mod_path:
557*c2e18aaaSAndroid Build Coastguard Worker            logging.debug('The %s\'s path is empty.', module.module_name)
558*c2e18aaaSAndroid Build Coastguard Worker            return
559*c2e18aaaSAndroid Build Coastguard Worker        self._collect_framework_srcjar_info(module)
560*c2e18aaaSAndroid Build Coastguard Worker        if mod_path not in self.path_to_sources:
561*c2e18aaaSAndroid Build Coastguard Worker            self.path_to_sources[mod_path] = {
562*c2e18aaaSAndroid Build Coastguard Worker                'src_dirs': module.src_dirs,
563*c2e18aaaSAndroid Build Coastguard Worker                'test_dirs': module.test_dirs,
564*c2e18aaaSAndroid Build Coastguard Worker                'r_java_paths': module.r_java_paths,
565*c2e18aaaSAndroid Build Coastguard Worker                'srcjar_paths': module.srcjar_paths,
566*c2e18aaaSAndroid Build Coastguard Worker                'jar_files': module.jar_files,
567*c2e18aaaSAndroid Build Coastguard Worker                'dep_paths': module.dep_paths,
568*c2e18aaaSAndroid Build Coastguard Worker            }
569*c2e18aaaSAndroid Build Coastguard Worker        else:
570*c2e18aaaSAndroid Build Coastguard Worker            for key, val in self.path_to_sources[mod_path].items():
571*c2e18aaaSAndroid Build Coastguard Worker                val.extend([v for v in getattr(module, key) if v not in val])
572*c2e18aaaSAndroid Build Coastguard Worker
573*c2e18aaaSAndroid Build Coastguard Worker
574*c2e18aaaSAndroid Build Coastguard Workerdef batch_build_dependencies(rebuild_targets):
575*c2e18aaaSAndroid Build Coastguard Worker    """Batch build the jar or srcjar files of the modules if they don't exist.
576*c2e18aaaSAndroid Build Coastguard Worker
577*c2e18aaaSAndroid Build Coastguard Worker    Command line has the max length limit, MAX_ARG_STRLEN, and
578*c2e18aaaSAndroid Build Coastguard Worker    MAX_ARG_STRLEN = (PAGE_SIZE * 32).
579*c2e18aaaSAndroid Build Coastguard Worker    If the build command is longer than MAX_ARG_STRLEN, this function will
580*c2e18aaaSAndroid Build Coastguard Worker    separate the rebuild_targets into chunks with size less or equal to
581*c2e18aaaSAndroid Build Coastguard Worker    MAX_ARG_STRLEN to make sure it can be built successfully.
582*c2e18aaaSAndroid Build Coastguard Worker
583*c2e18aaaSAndroid Build Coastguard Worker    Args:
584*c2e18aaaSAndroid Build Coastguard Worker        rebuild_targets: A set of jar or srcjar files which do not exist.
585*c2e18aaaSAndroid Build Coastguard Worker    """
586*c2e18aaaSAndroid Build Coastguard Worker    start_time = time.time()
587*c2e18aaaSAndroid Build Coastguard Worker    logging.info('Ready to build the jar or srcjar files. Files count = %s',
588*c2e18aaaSAndroid Build Coastguard Worker                 str(len(rebuild_targets)))
589*c2e18aaaSAndroid Build Coastguard Worker    arg_max = os.sysconf('SC_PAGE_SIZE') * 32 - _CMD_LENGTH_BUFFER
590*c2e18aaaSAndroid Build Coastguard Worker    rebuild_targets = list(rebuild_targets)
591*c2e18aaaSAndroid Build Coastguard Worker    for start, end in iter(_separate_build_targets(rebuild_targets, arg_max)):
592*c2e18aaaSAndroid Build Coastguard Worker        _build_target(rebuild_targets[start:end])
593*c2e18aaaSAndroid Build Coastguard Worker    duration = time.time() - start_time
594*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Build Time,  duration = %s', str(duration))
595*c2e18aaaSAndroid Build Coastguard Worker    aidegen_metrics.performance_metrics(constant.TYPE_AIDEGEN_BUILD_TIME,
596*c2e18aaaSAndroid Build Coastguard Worker                                        duration)
597*c2e18aaaSAndroid Build Coastguard Worker
598*c2e18aaaSAndroid Build Coastguard Worker
599*c2e18aaaSAndroid Build Coastguard Workerdef _build_target(targets):
600*c2e18aaaSAndroid Build Coastguard Worker    """Build the jar or srcjar files.
601*c2e18aaaSAndroid Build Coastguard Worker
602*c2e18aaaSAndroid Build Coastguard Worker    Use -k to keep going when some targets can't be built or build failed.
603*c2e18aaaSAndroid Build Coastguard Worker    Use -j to speed up building.
604*c2e18aaaSAndroid Build Coastguard Worker
605*c2e18aaaSAndroid Build Coastguard Worker    Args:
606*c2e18aaaSAndroid Build Coastguard Worker        targets: A list of jar or srcjar files which need to build.
607*c2e18aaaSAndroid Build Coastguard Worker    """
608*c2e18aaaSAndroid Build Coastguard Worker    build_cmd = ['-k', '-j']
609*c2e18aaaSAndroid Build Coastguard Worker    build_cmd.extend(list(targets))
610*c2e18aaaSAndroid Build Coastguard Worker    atest_utils.update_build_env(_BUILD_BP_JSON_ENV_ON)
611*c2e18aaaSAndroid Build Coastguard Worker    if not atest_utils.build(build_cmd):
612*c2e18aaaSAndroid Build Coastguard Worker        message = ('Build failed!\n{}\nAIDEGen will proceed but dependency '
613*c2e18aaaSAndroid Build Coastguard Worker                   'correctness is not guaranteed if not all targets being '
614*c2e18aaaSAndroid Build Coastguard Worker                   'built successfully.'.format('\n'.join(targets)))
615*c2e18aaaSAndroid Build Coastguard Worker        print('\n{} {}\n'.format(common_util.COLORED_INFO('Warning:'), message))
616*c2e18aaaSAndroid Build Coastguard Worker
617*c2e18aaaSAndroid Build Coastguard Worker
618*c2e18aaaSAndroid Build Coastguard Workerdef _separate_build_targets(build_targets, max_length):
619*c2e18aaaSAndroid Build Coastguard Worker    """Separate the build_targets by limit the command size to max command
620*c2e18aaaSAndroid Build Coastguard Worker    length.
621*c2e18aaaSAndroid Build Coastguard Worker
622*c2e18aaaSAndroid Build Coastguard Worker    Args:
623*c2e18aaaSAndroid Build Coastguard Worker        build_targets: A list to be separated.
624*c2e18aaaSAndroid Build Coastguard Worker        max_length: The max number of each build command length.
625*c2e18aaaSAndroid Build Coastguard Worker
626*c2e18aaaSAndroid Build Coastguard Worker    Yields:
627*c2e18aaaSAndroid Build Coastguard Worker        The start index and end index of build_targets.
628*c2e18aaaSAndroid Build Coastguard Worker    """
629*c2e18aaaSAndroid Build Coastguard Worker    arg_len = 0
630*c2e18aaaSAndroid Build Coastguard Worker    first_item_index = 0
631*c2e18aaaSAndroid Build Coastguard Worker    for i, item in enumerate(build_targets):
632*c2e18aaaSAndroid Build Coastguard Worker        arg_len = arg_len + len(item) + _BLANK_SIZE
633*c2e18aaaSAndroid Build Coastguard Worker        if arg_len > max_length:
634*c2e18aaaSAndroid Build Coastguard Worker            yield first_item_index, i
635*c2e18aaaSAndroid Build Coastguard Worker            first_item_index = i
636*c2e18aaaSAndroid Build Coastguard Worker            arg_len = len(item) + _BLANK_SIZE
637*c2e18aaaSAndroid Build Coastguard Worker    if first_item_index < len(build_targets):
638*c2e18aaaSAndroid Build Coastguard Worker        yield first_item_index, len(build_targets)
639*c2e18aaaSAndroid Build Coastguard Worker
640*c2e18aaaSAndroid Build Coastguard Worker
641*c2e18aaaSAndroid Build Coastguard Workerdef _update_iml_dep_modules(project):
642*c2e18aaaSAndroid Build Coastguard Worker    """Gets the dependent modules in the project's iml file.
643*c2e18aaaSAndroid Build Coastguard Worker
644*c2e18aaaSAndroid Build Coastguard Worker    The jar files which have the same source codes as cls.projects' source files
645*c2e18aaaSAndroid Build Coastguard Worker    should be removed from the dependencies.iml file's jar paths. The codes are
646*c2e18aaaSAndroid Build Coastguard Worker    written in aidegen.project.project_splitter.py.
647*c2e18aaaSAndroid Build Coastguard Worker    We should also add the jar project's unique iml name into self.dependencies
648*c2e18aaaSAndroid Build Coastguard Worker    which later will be written into its own iml project file. If we don't
649*c2e18aaaSAndroid Build Coastguard Worker    remove these files in dependencies.iml, it will cause the duplicated codes
650*c2e18aaaSAndroid Build Coastguard Worker    in IDE and raise issues. For example, when users do 'refactor' and rename a
651*c2e18aaaSAndroid Build Coastguard Worker    class in the IDE, it will search all sources and dependencies' jar paths and
652*c2e18aaaSAndroid Build Coastguard Worker    lead to the error.
653*c2e18aaaSAndroid Build Coastguard Worker    """
654*c2e18aaaSAndroid Build Coastguard Worker    keys = ('source_folder_path', 'test_folder_path', 'r_java_path',
655*c2e18aaaSAndroid Build Coastguard Worker            'srcjar_path', 'jar_path')
656*c2e18aaaSAndroid Build Coastguard Worker    for key in keys:
657*c2e18aaaSAndroid Build Coastguard Worker        for jar in project.source_path[key]:
658*c2e18aaaSAndroid Build Coastguard Worker            for prj in ProjectInfo.projects:
659*c2e18aaaSAndroid Build Coastguard Worker                if prj is project:
660*c2e18aaaSAndroid Build Coastguard Worker                    continue
661*c2e18aaaSAndroid Build Coastguard Worker                if (prj.rel_out_soong_jar_path in jar and
662*c2e18aaaSAndroid Build Coastguard Worker                        jar.endswith(constant.JAR_EXT)):
663*c2e18aaaSAndroid Build Coastguard Worker                    if prj.iml_name not in project.dependencies:
664*c2e18aaaSAndroid Build Coastguard Worker                        project.dependencies.append(prj.iml_name)
665*c2e18aaaSAndroid Build Coastguard Worker                    break
666