xref: /aosp_15_r20/tools/asuite/aidegen/project/project_splitter.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2020 - 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"""Separate the sources from multiple projects."""
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard Workerimport logging
20*c2e18aaaSAndroid Build Coastguard Workerimport os
21*c2e18aaaSAndroid Build Coastguard Workerimport shutil
22*c2e18aaaSAndroid Build Coastguard Worker
23*c2e18aaaSAndroid Build Coastguard Workerfrom pathlib import Path
24*c2e18aaaSAndroid Build Coastguard Worker
25*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import constant
26*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.idea import iml
27*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import common_util
28*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import project_config
29*c2e18aaaSAndroid Build Coastguard Worker
30*c2e18aaaSAndroid Build Coastguard Worker_KEY_SOURCE_PATH = 'source_folder_path'
31*c2e18aaaSAndroid Build Coastguard Worker_KEY_TEST_PATH = 'test_folder_path'
32*c2e18aaaSAndroid Build Coastguard Worker_SOURCE_FOLDERS = [_KEY_SOURCE_PATH, _KEY_TEST_PATH]
33*c2e18aaaSAndroid Build Coastguard Worker_KEY_SRCJAR_PATH = 'srcjar_path'
34*c2e18aaaSAndroid Build Coastguard Worker_KEY_R_PATH = 'r_java_path'
35*c2e18aaaSAndroid Build Coastguard Worker_KEY_JAR_PATH = 'jar_path'
36*c2e18aaaSAndroid Build Coastguard Worker_EXCLUDE_ITEM = '\n            <excludeFolder url="file://%s" />'
37*c2e18aaaSAndroid Build Coastguard Worker# Temporarily exclude test-dump and src_stub folders to prevent symbols from
38*c2e18aaaSAndroid Build Coastguard Worker# resolving failure by incorrect reference. These two folders should be removed
39*c2e18aaaSAndroid Build Coastguard Worker# after b/136982078 is resolved.
40*c2e18aaaSAndroid Build Coastguard Worker_EXCLUDE_FOLDERS = ['.idea', '.repo', 'art', 'bionic', 'bootable', 'build',
41*c2e18aaaSAndroid Build Coastguard Worker                    'dalvik', 'developers', 'device', 'hardware', 'kernel',
42*c2e18aaaSAndroid Build Coastguard Worker                    'libnativehelper', 'pdk', 'prebuilts', 'sdk', 'system',
43*c2e18aaaSAndroid Build Coastguard Worker                    'toolchain', 'tools', 'vendor', 'out', 'external',
44*c2e18aaaSAndroid Build Coastguard Worker                    'art/tools/ahat/src/test-dump',
45*c2e18aaaSAndroid Build Coastguard Worker                    'cts/common/device-side/device-info/src_stub',
46*c2e18aaaSAndroid Build Coastguard Worker                    'external/gson/gson/src/main/java'
47*c2e18aaaSAndroid Build Coastguard Worker                    ]
48*c2e18aaaSAndroid Build Coastguard Worker_PERMISSION_DEFINED_PATH = ('frameworks/base/core/res/framework-res/'
49*c2e18aaaSAndroid Build Coastguard Worker                            'android_common/gen/')
50*c2e18aaaSAndroid Build Coastguard Worker_ANDROID = 'android'
51*c2e18aaaSAndroid Build Coastguard Worker_R = 'R'
52*c2e18aaaSAndroid Build Coastguard Worker
53*c2e18aaaSAndroid Build Coastguard Worker
54*c2e18aaaSAndroid Build Coastguard Workerclass ProjectSplitter:
55*c2e18aaaSAndroid Build Coastguard Worker    """Splits the sources from multiple projects.
56*c2e18aaaSAndroid Build Coastguard Worker
57*c2e18aaaSAndroid Build Coastguard Worker    It's a specific solution to deal with the source folders in multiple
58*c2e18aaaSAndroid Build Coastguard Worker    project case. Since the IntelliJ does not allow duplicate source folders,
59*c2e18aaaSAndroid Build Coastguard Worker    AIDEGen needs to separate the source folders for each project. The single
60*c2e18aaaSAndroid Build Coastguard Worker    project case has no different with current structure.
61*c2e18aaaSAndroid Build Coastguard Worker
62*c2e18aaaSAndroid Build Coastguard Worker    Usage:
63*c2e18aaaSAndroid Build Coastguard Worker    project_splitter = ProjectSplitter(projects)
64*c2e18aaaSAndroid Build Coastguard Worker
65*c2e18aaaSAndroid Build Coastguard Worker    # Find the dependencies between the projects.
66*c2e18aaaSAndroid Build Coastguard Worker    project_splitter.get_dependencies()
67*c2e18aaaSAndroid Build Coastguard Worker
68*c2e18aaaSAndroid Build Coastguard Worker    # Clear the source folders for each project.
69*c2e18aaaSAndroid Build Coastguard Worker    project_splitter.revise_source_folders()
70*c2e18aaaSAndroid Build Coastguard Worker
71*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
72*c2e18aaaSAndroid Build Coastguard Worker        _projects: A list of ProjectInfo.
73*c2e18aaaSAndroid Build Coastguard Worker        _all_srcs: A dictionary contains all sources of multiple projects.
74*c2e18aaaSAndroid Build Coastguard Worker                   e.g.
75*c2e18aaaSAndroid Build Coastguard Worker                   {
76*c2e18aaaSAndroid Build Coastguard Worker                       'module_name': 'test',
77*c2e18aaaSAndroid Build Coastguard Worker                       'path': ['path/to/module'],
78*c2e18aaaSAndroid Build Coastguard Worker                       'srcs': ['src_folder1', 'src_folder2'],
79*c2e18aaaSAndroid Build Coastguard Worker                       'tests': ['test_folder1', 'test_folder2']
80*c2e18aaaSAndroid Build Coastguard Worker                       'jars': ['jar1.jar'],
81*c2e18aaaSAndroid Build Coastguard Worker                       'srcjars': ['1.srcjar', '2.srcjar'],
82*c2e18aaaSAndroid Build Coastguard Worker                       'dependencies': ['framework_srcjars', 'base'],
83*c2e18aaaSAndroid Build Coastguard Worker                       'iml_name': '/abs/path/to/iml.iml'
84*c2e18aaaSAndroid Build Coastguard Worker                   }
85*c2e18aaaSAndroid Build Coastguard Worker        _framework_exist: A boolean, True if framework is one of the projects.
86*c2e18aaaSAndroid Build Coastguard Worker        _framework_iml: A string, the name of the framework's iml.
87*c2e18aaaSAndroid Build Coastguard Worker        _full_repo: A boolean, True if loading with full Android sources.
88*c2e18aaaSAndroid Build Coastguard Worker        _full_repo_iml: A string, the name of the Android folder's iml.
89*c2e18aaaSAndroid Build Coastguard Worker        _permission_r_srcjar: A string, the absolute path of R.srcjar file where
90*c2e18aaaSAndroid Build Coastguard Worker                              the permission relative constants are defined.
91*c2e18aaaSAndroid Build Coastguard Worker        _permission_aapt2: A string, the absolute path of aapt2/R directory
92*c2e18aaaSAndroid Build Coastguard Worker                           where the permission relative constants are defined.
93*c2e18aaaSAndroid Build Coastguard Worker    """
94*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, projects):
95*c2e18aaaSAndroid Build Coastguard Worker        """ProjectSplitter initialize.
96*c2e18aaaSAndroid Build Coastguard Worker
97*c2e18aaaSAndroid Build Coastguard Worker        Args:
98*c2e18aaaSAndroid Build Coastguard Worker            projects: A list of ProjectInfo object.
99*c2e18aaaSAndroid Build Coastguard Worker        """
100*c2e18aaaSAndroid Build Coastguard Worker        self._projects = projects
101*c2e18aaaSAndroid Build Coastguard Worker        self._all_srcs = dict(projects[0].source_path)
102*c2e18aaaSAndroid Build Coastguard Worker        self._framework_iml = None
103*c2e18aaaSAndroid Build Coastguard Worker        self._framework_exist = any(
104*c2e18aaaSAndroid Build Coastguard Worker            {p.project_relative_path == constant.FRAMEWORK_PATH
105*c2e18aaaSAndroid Build Coastguard Worker             for p in self._projects})
106*c2e18aaaSAndroid Build Coastguard Worker        if self._framework_exist:
107*c2e18aaaSAndroid Build Coastguard Worker            self._framework_iml = iml.IMLGenerator.get_unique_iml_name(
108*c2e18aaaSAndroid Build Coastguard Worker                os.path.join(common_util.get_android_root_dir(),
109*c2e18aaaSAndroid Build Coastguard Worker                             constant.FRAMEWORK_PATH))
110*c2e18aaaSAndroid Build Coastguard Worker        self._full_repo = project_config.ProjectConfig.get_instance().full_repo
111*c2e18aaaSAndroid Build Coastguard Worker        if self._full_repo:
112*c2e18aaaSAndroid Build Coastguard Worker            self._full_repo_iml = os.path.basename(
113*c2e18aaaSAndroid Build Coastguard Worker                common_util.get_android_root_dir())
114*c2e18aaaSAndroid Build Coastguard Worker        self._permission_r_srcjar = _get_permission_r_srcjar_rel_path()
115*c2e18aaaSAndroid Build Coastguard Worker        self._permission_aapt2 = _get_permission_aapt2_rel_path()
116*c2e18aaaSAndroid Build Coastguard Worker
117*c2e18aaaSAndroid Build Coastguard Worker    def revise_source_folders(self):
118*c2e18aaaSAndroid Build Coastguard Worker        """Resets the source folders of each project.
119*c2e18aaaSAndroid Build Coastguard Worker
120*c2e18aaaSAndroid Build Coastguard Worker        There should be no duplicate source root path in IntelliJ. The issue
121*c2e18aaaSAndroid Build Coastguard Worker        doesn't happen in single project case. Once users choose multiple
122*c2e18aaaSAndroid Build Coastguard Worker        projects, there could be several same source paths of different
123*c2e18aaaSAndroid Build Coastguard Worker        projects. In order to prevent that, we should remove the source paths
124*c2e18aaaSAndroid Build Coastguard Worker        in dependencies.iml which are duplicate with the paths in [module].iml
125*c2e18aaaSAndroid Build Coastguard Worker        files.
126*c2e18aaaSAndroid Build Coastguard Worker
127*c2e18aaaSAndroid Build Coastguard Worker        Steps to prevent the duplicate source root path in IntelliJ:
128*c2e18aaaSAndroid Build Coastguard Worker        1. Copy all sources from sub-projects to main project.
129*c2e18aaaSAndroid Build Coastguard Worker        2. Delete the source and test folders which are not under the
130*c2e18aaaSAndroid Build Coastguard Worker           sub-projects.
131*c2e18aaaSAndroid Build Coastguard Worker        3. Delete the sub-projects' source and test paths from the main project.
132*c2e18aaaSAndroid Build Coastguard Worker        """
133*c2e18aaaSAndroid Build Coastguard Worker        self._collect_all_srcs()
134*c2e18aaaSAndroid Build Coastguard Worker        self._keep_local_sources()
135*c2e18aaaSAndroid Build Coastguard Worker        self._remove_duplicate_sources()
136*c2e18aaaSAndroid Build Coastguard Worker
137*c2e18aaaSAndroid Build Coastguard Worker    def _collect_all_srcs(self):
138*c2e18aaaSAndroid Build Coastguard Worker        """Copies all projects' sources to a dictionary."""
139*c2e18aaaSAndroid Build Coastguard Worker        for project in self._projects[1:]:
140*c2e18aaaSAndroid Build Coastguard Worker            for key, value in project.source_path.items():
141*c2e18aaaSAndroid Build Coastguard Worker                self._all_srcs[key].update(value)
142*c2e18aaaSAndroid Build Coastguard Worker
143*c2e18aaaSAndroid Build Coastguard Worker    def _keep_local_sources(self):
144*c2e18aaaSAndroid Build Coastguard Worker        """Removes source folders which are not under the project's path.
145*c2e18aaaSAndroid Build Coastguard Worker
146*c2e18aaaSAndroid Build Coastguard Worker        1. Remove the source folders which are not under the project.
147*c2e18aaaSAndroid Build Coastguard Worker        2. Remove the duplicate project's source folders from the _all_srcs.
148*c2e18aaaSAndroid Build Coastguard Worker        """
149*c2e18aaaSAndroid Build Coastguard Worker        for project in self._projects:
150*c2e18aaaSAndroid Build Coastguard Worker            srcs = project.source_path
151*c2e18aaaSAndroid Build Coastguard Worker            relpath = project.project_relative_path
152*c2e18aaaSAndroid Build Coastguard Worker            is_root = not relpath
153*c2e18aaaSAndroid Build Coastguard Worker            for key in _SOURCE_FOLDERS:
154*c2e18aaaSAndroid Build Coastguard Worker                srcs[key] = {s for s in srcs[key]
155*c2e18aaaSAndroid Build Coastguard Worker                             if common_util.is_source_under_relative_path(
156*c2e18aaaSAndroid Build Coastguard Worker                                 s, relpath) or is_root}
157*c2e18aaaSAndroid Build Coastguard Worker                self._all_srcs[key] -= srcs[key]
158*c2e18aaaSAndroid Build Coastguard Worker
159*c2e18aaaSAndroid Build Coastguard Worker    def _remove_duplicate_sources(self):
160*c2e18aaaSAndroid Build Coastguard Worker        """Removes the duplicate source folders from each sub-project.
161*c2e18aaaSAndroid Build Coastguard Worker
162*c2e18aaaSAndroid Build Coastguard Worker        Priority processing with the longest path length, e.g.
163*c2e18aaaSAndroid Build Coastguard Worker        frameworks/base/packages/SettingsLib must have priority over
164*c2e18aaaSAndroid Build Coastguard Worker        frameworks/base.
165*c2e18aaaSAndroid Build Coastguard Worker        (b/160303006): Remove the parent project's source and test paths under
166*c2e18aaaSAndroid Build Coastguard Worker        the child's project path.
167*c2e18aaaSAndroid Build Coastguard Worker        """
168*c2e18aaaSAndroid Build Coastguard Worker        root = common_util.get_android_root_dir()
169*c2e18aaaSAndroid Build Coastguard Worker        projects = sorted(self._projects, key=lambda k: len(
170*c2e18aaaSAndroid Build Coastguard Worker            k.project_relative_path), reverse=True)
171*c2e18aaaSAndroid Build Coastguard Worker        for child in projects:
172*c2e18aaaSAndroid Build Coastguard Worker            for parent in self._projects:
173*c2e18aaaSAndroid Build Coastguard Worker                is_root = not parent.project_relative_path
174*c2e18aaaSAndroid Build Coastguard Worker                if parent is child:
175*c2e18aaaSAndroid Build Coastguard Worker                    continue
176*c2e18aaaSAndroid Build Coastguard Worker                if (common_util.is_source_under_relative_path(
177*c2e18aaaSAndroid Build Coastguard Worker                        child.project_relative_path,
178*c2e18aaaSAndroid Build Coastguard Worker                        parent.project_relative_path) or is_root):
179*c2e18aaaSAndroid Build Coastguard Worker                    for key in _SOURCE_FOLDERS:
180*c2e18aaaSAndroid Build Coastguard Worker                        parent.source_path[key] -= child.source_path[key]
181*c2e18aaaSAndroid Build Coastguard Worker                        rm_paths = _remove_child_duplicate_sources_from_parent(
182*c2e18aaaSAndroid Build Coastguard Worker                            child, parent.source_path[key], root)
183*c2e18aaaSAndroid Build Coastguard Worker                        parent.source_path[key] -= rm_paths
184*c2e18aaaSAndroid Build Coastguard Worker
185*c2e18aaaSAndroid Build Coastguard Worker    def get_dependencies(self):
186*c2e18aaaSAndroid Build Coastguard Worker        """Gets the dependencies between the projects.
187*c2e18aaaSAndroid Build Coastguard Worker
188*c2e18aaaSAndroid Build Coastguard Worker        Check if the current project's source folder exists in other projects.
189*c2e18aaaSAndroid Build Coastguard Worker        If so, the current project is a dependency module to the other.
190*c2e18aaaSAndroid Build Coastguard Worker        """
191*c2e18aaaSAndroid Build Coastguard Worker        projects = sorted(self._projects, key=lambda k: len(
192*c2e18aaaSAndroid Build Coastguard Worker            k.project_relative_path))
193*c2e18aaaSAndroid Build Coastguard Worker        for project in projects:
194*c2e18aaaSAndroid Build Coastguard Worker            proj_path = project.project_relative_path
195*c2e18aaaSAndroid Build Coastguard Worker            project.dependencies = [constant.FRAMEWORK_SRCJARS]
196*c2e18aaaSAndroid Build Coastguard Worker            if self._framework_exist and proj_path != constant.FRAMEWORK_PATH:
197*c2e18aaaSAndroid Build Coastguard Worker                project.dependencies.append(self._framework_iml)
198*c2e18aaaSAndroid Build Coastguard Worker            if self._full_repo and proj_path:
199*c2e18aaaSAndroid Build Coastguard Worker                project.dependencies.append(self._full_repo_iml)
200*c2e18aaaSAndroid Build Coastguard Worker            srcs = (project.source_path[_KEY_SOURCE_PATH]
201*c2e18aaaSAndroid Build Coastguard Worker                    | project.source_path[_KEY_TEST_PATH])
202*c2e18aaaSAndroid Build Coastguard Worker            dep_projects = sorted(self._projects, key=lambda k: len(
203*c2e18aaaSAndroid Build Coastguard Worker                k.project_relative_path))
204*c2e18aaaSAndroid Build Coastguard Worker            for dep_proj in dep_projects:
205*c2e18aaaSAndroid Build Coastguard Worker                dep_path = dep_proj.project_relative_path
206*c2e18aaaSAndroid Build Coastguard Worker                is_root = not dep_path
207*c2e18aaaSAndroid Build Coastguard Worker                is_child = common_util.is_source_under_relative_path(dep_path,
208*c2e18aaaSAndroid Build Coastguard Worker                                                                     proj_path)
209*c2e18aaaSAndroid Build Coastguard Worker                is_dep = any({s for s in srcs
210*c2e18aaaSAndroid Build Coastguard Worker                              if common_util.is_source_under_relative_path(
211*c2e18aaaSAndroid Build Coastguard Worker                                  s, dep_path) or is_root})
212*c2e18aaaSAndroid Build Coastguard Worker                if dep_proj is project or is_child or not is_dep:
213*c2e18aaaSAndroid Build Coastguard Worker                    continue
214*c2e18aaaSAndroid Build Coastguard Worker                dep = iml.IMLGenerator.get_unique_iml_name(os.path.join(
215*c2e18aaaSAndroid Build Coastguard Worker                    common_util.get_android_root_dir(), dep_path))
216*c2e18aaaSAndroid Build Coastguard Worker                if dep not in project.dependencies:
217*c2e18aaaSAndroid Build Coastguard Worker                    project.dependencies.append(dep)
218*c2e18aaaSAndroid Build Coastguard Worker            project.dependencies.append(constant.KEY_DEPENDENCIES)
219*c2e18aaaSAndroid Build Coastguard Worker
220*c2e18aaaSAndroid Build Coastguard Worker    def gen_framework_srcjars_iml(self):
221*c2e18aaaSAndroid Build Coastguard Worker        """Generates the framework_srcjars.iml.
222*c2e18aaaSAndroid Build Coastguard Worker
223*c2e18aaaSAndroid Build Coastguard Worker        Create the iml file with only the srcjars of module framework-all. These
224*c2e18aaaSAndroid Build Coastguard Worker        srcjars will be separated from the modules under frameworks/base.
225*c2e18aaaSAndroid Build Coastguard Worker
226*c2e18aaaSAndroid Build Coastguard Worker        Returns:
227*c2e18aaaSAndroid Build Coastguard Worker            A string of the framework_srcjars.iml's absolute path.
228*c2e18aaaSAndroid Build Coastguard Worker        """
229*c2e18aaaSAndroid Build Coastguard Worker        self._remove_permission_definition_srcjar_path()
230*c2e18aaaSAndroid Build Coastguard Worker        mod = dict(self._projects[0].dep_modules[constant.FRAMEWORK_ALL])
231*c2e18aaaSAndroid Build Coastguard Worker        mod[constant.KEY_DEPENDENCIES] = []
232*c2e18aaaSAndroid Build Coastguard Worker        mod[constant.KEY_IML_NAME] = constant.FRAMEWORK_SRCJARS
233*c2e18aaaSAndroid Build Coastguard Worker        if self._framework_exist:
234*c2e18aaaSAndroid Build Coastguard Worker            mod[constant.KEY_DEPENDENCIES].append(self._framework_iml)
235*c2e18aaaSAndroid Build Coastguard Worker        if self._full_repo:
236*c2e18aaaSAndroid Build Coastguard Worker            mod[constant.KEY_DEPENDENCIES].append(self._full_repo_iml)
237*c2e18aaaSAndroid Build Coastguard Worker        mod[constant.KEY_DEPENDENCIES].append(constant.KEY_DEPENDENCIES)
238*c2e18aaaSAndroid Build Coastguard Worker        permission_src = self._get_permission_defined_source_path()
239*c2e18aaaSAndroid Build Coastguard Worker        if permission_src:
240*c2e18aaaSAndroid Build Coastguard Worker            mod[constant.KEY_SRCS] = [permission_src]
241*c2e18aaaSAndroid Build Coastguard Worker            srcjar_dict = {constant.KEY_DEP_SRCS: True,
242*c2e18aaaSAndroid Build Coastguard Worker                           constant.KEY_SRCJARS: True,
243*c2e18aaaSAndroid Build Coastguard Worker                           constant.KEY_DEPENDENCIES: True}
244*c2e18aaaSAndroid Build Coastguard Worker        else:
245*c2e18aaaSAndroid Build Coastguard Worker            logging.warning('The permission definition relative paths are '
246*c2e18aaaSAndroid Build Coastguard Worker                            'missing.')
247*c2e18aaaSAndroid Build Coastguard Worker            srcjar_dict = {constant.KEY_SRCJARS: True,
248*c2e18aaaSAndroid Build Coastguard Worker                           constant.KEY_DEPENDENCIES: True}
249*c2e18aaaSAndroid Build Coastguard Worker        framework_srcjars_iml = iml.IMLGenerator(mod)
250*c2e18aaaSAndroid Build Coastguard Worker        framework_srcjars_iml.create(srcjar_dict)
251*c2e18aaaSAndroid Build Coastguard Worker        self._all_srcs[_KEY_SRCJAR_PATH] -= set(mod.get(constant.KEY_SRCJARS,
252*c2e18aaaSAndroid Build Coastguard Worker                                                        []))
253*c2e18aaaSAndroid Build Coastguard Worker        return framework_srcjars_iml.iml_path
254*c2e18aaaSAndroid Build Coastguard Worker
255*c2e18aaaSAndroid Build Coastguard Worker    def _get_permission_defined_source_path(self):
256*c2e18aaaSAndroid Build Coastguard Worker        """Gets the source path where permission relative constants are defined.
257*c2e18aaaSAndroid Build Coastguard Worker
258*c2e18aaaSAndroid Build Coastguard Worker        For the definition permission constants, the priority is,
259*c2e18aaaSAndroid Build Coastguard Worker        1) If framework-res/android_common/gen/aapt2/R directory exists, return
260*c2e18aaaSAndroid Build Coastguard Worker           it.
261*c2e18aaaSAndroid Build Coastguard Worker        2) If the framework-res/android_common/gen/android/R.srcjar file exists,
262*c2e18aaaSAndroid Build Coastguard Worker           unzip it to 'aidegen_r.srcjar' folder and return the path.
263*c2e18aaaSAndroid Build Coastguard Worker
264*c2e18aaaSAndroid Build Coastguard Worker        Returns:
265*c2e18aaaSAndroid Build Coastguard Worker            A string of the path of aapt2/R or android/aidegen_r.srcjar folder,
266*c2e18aaaSAndroid Build Coastguard Worker            else None.
267*c2e18aaaSAndroid Build Coastguard Worker        """
268*c2e18aaaSAndroid Build Coastguard Worker        if os.path.isdir(self._permission_aapt2):
269*c2e18aaaSAndroid Build Coastguard Worker            return self._permission_aapt2
270*c2e18aaaSAndroid Build Coastguard Worker        if os.path.isfile(self._permission_r_srcjar):
271*c2e18aaaSAndroid Build Coastguard Worker            dest = os.path.join(
272*c2e18aaaSAndroid Build Coastguard Worker                os.path.dirname(self._permission_r_srcjar),
273*c2e18aaaSAndroid Build Coastguard Worker                ''.join([constant.UNZIP_SRCJAR_PATH_HEAD,
274*c2e18aaaSAndroid Build Coastguard Worker                         os.path.basename(self._permission_r_srcjar).lower()]))
275*c2e18aaaSAndroid Build Coastguard Worker            if os.path.isdir(dest):
276*c2e18aaaSAndroid Build Coastguard Worker                shutil.rmtree(dest)
277*c2e18aaaSAndroid Build Coastguard Worker            common_util.unzip_file(self._permission_r_srcjar, dest)
278*c2e18aaaSAndroid Build Coastguard Worker            return dest
279*c2e18aaaSAndroid Build Coastguard Worker        return None
280*c2e18aaaSAndroid Build Coastguard Worker
281*c2e18aaaSAndroid Build Coastguard Worker    def _gen_dependencies_iml(self):
282*c2e18aaaSAndroid Build Coastguard Worker        """Generates the dependencies.iml."""
283*c2e18aaaSAndroid Build Coastguard Worker        rel_project_soong_paths = self._get_rel_project_soong_paths()
284*c2e18aaaSAndroid Build Coastguard Worker        self._unzip_all_scrjars()
285*c2e18aaaSAndroid Build Coastguard Worker        mod = {
286*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_SRCS: _get_real_dependencies_jars(
287*c2e18aaaSAndroid Build Coastguard Worker                rel_project_soong_paths, self._all_srcs[_KEY_SOURCE_PATH]),
288*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_TESTS: _get_real_dependencies_jars(
289*c2e18aaaSAndroid Build Coastguard Worker                rel_project_soong_paths, self._all_srcs[_KEY_TEST_PATH]),
290*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_JARS: _get_real_dependencies_jars(
291*c2e18aaaSAndroid Build Coastguard Worker                rel_project_soong_paths, self._all_srcs[_KEY_JAR_PATH]),
292*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_SRCJARS: _get_real_dependencies_jars(
293*c2e18aaaSAndroid Build Coastguard Worker                rel_project_soong_paths,
294*c2e18aaaSAndroid Build Coastguard Worker                self._all_srcs[_KEY_R_PATH] | self._all_srcs[_KEY_SRCJAR_PATH]),
295*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_DEPENDENCIES: _get_real_dependencies_jars(
296*c2e18aaaSAndroid Build Coastguard Worker                rel_project_soong_paths, [constant.FRAMEWORK_SRCJARS]),
297*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_PATH: [self._projects[0].project_relative_path],
298*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_MODULE_NAME: constant.KEY_DEPENDENCIES,
299*c2e18aaaSAndroid Build Coastguard Worker            constant.KEY_IML_NAME: constant.KEY_DEPENDENCIES
300*c2e18aaaSAndroid Build Coastguard Worker        }
301*c2e18aaaSAndroid Build Coastguard Worker        if self._framework_exist:
302*c2e18aaaSAndroid Build Coastguard Worker            mod[constant.KEY_DEPENDENCIES].append(self._framework_iml)
303*c2e18aaaSAndroid Build Coastguard Worker        if self._full_repo:
304*c2e18aaaSAndroid Build Coastguard Worker            mod[constant.KEY_DEPENDENCIES].append(self._full_repo_iml)
305*c2e18aaaSAndroid Build Coastguard Worker        dep_iml = iml.IMLGenerator(mod)
306*c2e18aaaSAndroid Build Coastguard Worker        dep_iml.create({constant.KEY_DEP_SRCS: True,
307*c2e18aaaSAndroid Build Coastguard Worker                        constant.KEY_SRCJARS: True,
308*c2e18aaaSAndroid Build Coastguard Worker                        constant.KEY_JARS: True,
309*c2e18aaaSAndroid Build Coastguard Worker                        constant.KEY_DEPENDENCIES: True})
310*c2e18aaaSAndroid Build Coastguard Worker
311*c2e18aaaSAndroid Build Coastguard Worker    def _unzip_all_scrjars(self):
312*c2e18aaaSAndroid Build Coastguard Worker        """Unzips all scrjar files to a specific folder 'aidegen_r.srcjar'.
313*c2e18aaaSAndroid Build Coastguard Worker
314*c2e18aaaSAndroid Build Coastguard Worker        For some versions of IntelliJ no more supports unzipping srcjar files
315*c2e18aaaSAndroid Build Coastguard Worker        automatically, we have to unzip it to a 'aidegen_r.srcjar' directory.
316*c2e18aaaSAndroid Build Coastguard Worker        The rules of the unzip process are,
317*c2e18aaaSAndroid Build Coastguard Worker        1) If it's a aapt2/R type jar or other directory type sources, add them
318*c2e18aaaSAndroid Build Coastguard Worker           into self._all_srcs[_KEY_SOURCE_PATH].
319*c2e18aaaSAndroid Build Coastguard Worker        2) If it's an R.srcjar file, check if the same path of aapt2/R directory
320*c2e18aaaSAndroid Build Coastguard Worker           exists if so add aapt2/R path into the
321*c2e18aaaSAndroid Build Coastguard Worker           self._all_srcs[_KEY_SOURCE_PATH], otherwise unzip R.srcjar into
322*c2e18aaaSAndroid Build Coastguard Worker           the 'aidegen_r.srcjar' directory and add the unzipped path into
323*c2e18aaaSAndroid Build Coastguard Worker           self._all_srcs[_KEY_SOURCE_PATH].
324*c2e18aaaSAndroid Build Coastguard Worker        """
325*c2e18aaaSAndroid Build Coastguard Worker        sjars = self._all_srcs[_KEY_R_PATH] | self._all_srcs[_KEY_SRCJAR_PATH]
326*c2e18aaaSAndroid Build Coastguard Worker        self._all_srcs[_KEY_R_PATH] = set()
327*c2e18aaaSAndroid Build Coastguard Worker        self._all_srcs[_KEY_SRCJAR_PATH] = set()
328*c2e18aaaSAndroid Build Coastguard Worker        for sjar in sjars:
329*c2e18aaaSAndroid Build Coastguard Worker            if not os.path.exists(sjar):
330*c2e18aaaSAndroid Build Coastguard Worker                continue
331*c2e18aaaSAndroid Build Coastguard Worker            if os.path.isdir(sjar):
332*c2e18aaaSAndroid Build Coastguard Worker                self._all_srcs[_KEY_SOURCE_PATH].add(sjar)
333*c2e18aaaSAndroid Build Coastguard Worker                continue
334*c2e18aaaSAndroid Build Coastguard Worker            sjar_dir = os.path.dirname(sjar)
335*c2e18aaaSAndroid Build Coastguard Worker            sjar_name = os.path.basename(sjar).lower()
336*c2e18aaaSAndroid Build Coastguard Worker            aapt2 = os.path.join(
337*c2e18aaaSAndroid Build Coastguard Worker                os.path.dirname(sjar_dir), constant.NAME_AAPT2, _R)
338*c2e18aaaSAndroid Build Coastguard Worker            if os.path.isdir(aapt2):
339*c2e18aaaSAndroid Build Coastguard Worker                self._all_srcs[_KEY_SOURCE_PATH].add(aapt2)
340*c2e18aaaSAndroid Build Coastguard Worker                continue
341*c2e18aaaSAndroid Build Coastguard Worker            dest = os.path.join(
342*c2e18aaaSAndroid Build Coastguard Worker                sjar_dir, ''.join([constant.UNZIP_SRCJAR_PATH_HEAD, sjar_name]))
343*c2e18aaaSAndroid Build Coastguard Worker            if os.path.isdir(dest):
344*c2e18aaaSAndroid Build Coastguard Worker                shutil.rmtree(dest)
345*c2e18aaaSAndroid Build Coastguard Worker            common_util.unzip_file(sjar, dest)
346*c2e18aaaSAndroid Build Coastguard Worker            self._all_srcs[_KEY_SOURCE_PATH].add(dest)
347*c2e18aaaSAndroid Build Coastguard Worker
348*c2e18aaaSAndroid Build Coastguard Worker    def gen_projects_iml(self):
349*c2e18aaaSAndroid Build Coastguard Worker        """Generates the projects' iml file."""
350*c2e18aaaSAndroid Build Coastguard Worker        root_path = common_util.get_android_root_dir()
351*c2e18aaaSAndroid Build Coastguard Worker        excludes = project_config.ProjectConfig.get_instance().exclude_paths
352*c2e18aaaSAndroid Build Coastguard Worker        for project in self._projects:
353*c2e18aaaSAndroid Build Coastguard Worker            relpath = project.project_relative_path
354*c2e18aaaSAndroid Build Coastguard Worker            exclude_folders = []
355*c2e18aaaSAndroid Build Coastguard Worker            if not relpath:
356*c2e18aaaSAndroid Build Coastguard Worker                exclude_folders.extend(get_exclude_content(root_path))
357*c2e18aaaSAndroid Build Coastguard Worker            if excludes:
358*c2e18aaaSAndroid Build Coastguard Worker                exclude_folders.extend(get_exclude_content(root_path, excludes))
359*c2e18aaaSAndroid Build Coastguard Worker            mod_info = {
360*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_EXCLUDES: ''.join(exclude_folders),
361*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_SRCS: project.source_path[_KEY_SOURCE_PATH],
362*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_TESTS: project.source_path[_KEY_TEST_PATH],
363*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_DEPENDENCIES: project.dependencies,
364*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_PATH: [relpath],
365*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_MODULE_NAME: project.module_name,
366*c2e18aaaSAndroid Build Coastguard Worker                constant.KEY_IML_NAME: iml.IMLGenerator.get_unique_iml_name(
367*c2e18aaaSAndroid Build Coastguard Worker                    str(Path(root_path, relpath)))
368*c2e18aaaSAndroid Build Coastguard Worker            }
369*c2e18aaaSAndroid Build Coastguard Worker            dep_iml = iml.IMLGenerator(mod_info)
370*c2e18aaaSAndroid Build Coastguard Worker            dep_iml.create({constant.KEY_SRCS: True,
371*c2e18aaaSAndroid Build Coastguard Worker                            constant.KEY_DEPENDENCIES: True})
372*c2e18aaaSAndroid Build Coastguard Worker            project.iml_path = dep_iml.iml_path
373*c2e18aaaSAndroid Build Coastguard Worker        self._gen_dependencies_iml()
374*c2e18aaaSAndroid Build Coastguard Worker
375*c2e18aaaSAndroid Build Coastguard Worker    def _get_rel_project_soong_paths(self):
376*c2e18aaaSAndroid Build Coastguard Worker        """Gets relative projects' paths in 'out/soong/.intermediates' folder.
377*c2e18aaaSAndroid Build Coastguard Worker
378*c2e18aaaSAndroid Build Coastguard Worker        Gets relative projects' paths in the 'out/soong/.intermediates'
379*c2e18aaaSAndroid Build Coastguard Worker        directory. For example, if the self.projects = ['frameworks/base'] the
380*c2e18aaaSAndroid Build Coastguard Worker        returned list should be ['out/soong/.intermediates/frameworks/base/'].
381*c2e18aaaSAndroid Build Coastguard Worker
382*c2e18aaaSAndroid Build Coastguard Worker        Returns:
383*c2e18aaaSAndroid Build Coastguard Worker            A list of relative projects' paths in out/soong/.intermediates.
384*c2e18aaaSAndroid Build Coastguard Worker        """
385*c2e18aaaSAndroid Build Coastguard Worker        out_soong_dir = os.path.relpath(common_util.get_soong_out_path(),
386*c2e18aaaSAndroid Build Coastguard Worker                                        common_util.get_android_root_dir())
387*c2e18aaaSAndroid Build Coastguard Worker        rel_project_soong_paths = []
388*c2e18aaaSAndroid Build Coastguard Worker        for project in self._projects:
389*c2e18aaaSAndroid Build Coastguard Worker            relpath = project.project_relative_path
390*c2e18aaaSAndroid Build Coastguard Worker            rel_project_soong_paths.append(os.sep.join(
391*c2e18aaaSAndroid Build Coastguard Worker                [out_soong_dir, constant.INTERMEDIATES, relpath]) + os.sep)
392*c2e18aaaSAndroid Build Coastguard Worker        return rel_project_soong_paths
393*c2e18aaaSAndroid Build Coastguard Worker
394*c2e18aaaSAndroid Build Coastguard Worker    def _remove_permission_definition_srcjar_path(self):
395*c2e18aaaSAndroid Build Coastguard Worker        """Removes android.Manifest.permission definition srcjar path.
396*c2e18aaaSAndroid Build Coastguard Worker
397*c2e18aaaSAndroid Build Coastguard Worker        If framework-res/android_common/gen/aapt2/R directory or
398*c2e18aaaSAndroid Build Coastguard Worker        framework-res/android_common/gen/android/R.srcjar file exists in
399*c2e18aaaSAndroid Build Coastguard Worker        self._all_srcs[_KEY_SRCJAR_PATH], remove them.
400*c2e18aaaSAndroid Build Coastguard Worker        """
401*c2e18aaaSAndroid Build Coastguard Worker        if self._permission_aapt2 in self._all_srcs[_KEY_SRCJAR_PATH]:
402*c2e18aaaSAndroid Build Coastguard Worker            self._all_srcs[_KEY_SRCJAR_PATH].remove(self._permission_aapt2)
403*c2e18aaaSAndroid Build Coastguard Worker        if self._permission_r_srcjar in self._all_srcs[_KEY_SRCJAR_PATH]:
404*c2e18aaaSAndroid Build Coastguard Worker            self._all_srcs[_KEY_SRCJAR_PATH].remove(self._permission_r_srcjar)
405*c2e18aaaSAndroid Build Coastguard Worker
406*c2e18aaaSAndroid Build Coastguard Worker
407*c2e18aaaSAndroid Build Coastguard Workerdef _get_real_dependencies_jars(list_to_check, list_to_be_checked):
408*c2e18aaaSAndroid Build Coastguard Worker    """Gets real dependencies' jar from the input list.
409*c2e18aaaSAndroid Build Coastguard Worker
410*c2e18aaaSAndroid Build Coastguard Worker    There are jar files which have the same source codes as the
411*c2e18aaaSAndroid Build Coastguard Worker    self.projects should be removed from dependencies. Otherwise these files
412*c2e18aaaSAndroid Build Coastguard Worker    will cause the duplicated codes in IDE and lead to issues: b/158583214 is an
413*c2e18aaaSAndroid Build Coastguard Worker    example.
414*c2e18aaaSAndroid Build Coastguard Worker
415*c2e18aaaSAndroid Build Coastguard Worker    Args:
416*c2e18aaaSAndroid Build Coastguard Worker        list_to_check: A list of relative projects' paths in the folder
417*c2e18aaaSAndroid Build Coastguard Worker                       out/soong/.intermediates to be checked if are contained
418*c2e18aaaSAndroid Build Coastguard Worker                       in the list_to_be_checked list.
419*c2e18aaaSAndroid Build Coastguard Worker        list_to_be_checked: A list of dependencies' paths to be checked.
420*c2e18aaaSAndroid Build Coastguard Worker
421*c2e18aaaSAndroid Build Coastguard Worker    Returns:
422*c2e18aaaSAndroid Build Coastguard Worker        A list of dependency jar paths after duplicated ones removed.
423*c2e18aaaSAndroid Build Coastguard Worker    """
424*c2e18aaaSAndroid Build Coastguard Worker    file_exts = [constant.JAR_EXT]
425*c2e18aaaSAndroid Build Coastguard Worker    real_jars = list_to_be_checked.copy()
426*c2e18aaaSAndroid Build Coastguard Worker    for jar in list_to_be_checked:
427*c2e18aaaSAndroid Build Coastguard Worker        ext = os.path.splitext(jar)[-1]
428*c2e18aaaSAndroid Build Coastguard Worker        for check_path in list_to_check:
429*c2e18aaaSAndroid Build Coastguard Worker            if check_path in jar and ext in file_exts:
430*c2e18aaaSAndroid Build Coastguard Worker                real_jars.remove(jar)
431*c2e18aaaSAndroid Build Coastguard Worker                break
432*c2e18aaaSAndroid Build Coastguard Worker    return real_jars
433*c2e18aaaSAndroid Build Coastguard Worker
434*c2e18aaaSAndroid Build Coastguard Worker
435*c2e18aaaSAndroid Build Coastguard Workerdef get_exclude_content(root_path, excludes=None):
436*c2e18aaaSAndroid Build Coastguard Worker    """Get the exclude folder content list.
437*c2e18aaaSAndroid Build Coastguard Worker
438*c2e18aaaSAndroid Build Coastguard Worker    It returns the exclude folders content list.
439*c2e18aaaSAndroid Build Coastguard Worker    e.g.
440*c2e18aaaSAndroid Build Coastguard Worker    ['<excludeFolder url="file://a/.idea" />',
441*c2e18aaaSAndroid Build Coastguard Worker    '<excludeFolder url="file://a/.repo" />']
442*c2e18aaaSAndroid Build Coastguard Worker
443*c2e18aaaSAndroid Build Coastguard Worker    Args:
444*c2e18aaaSAndroid Build Coastguard Worker        root_path: Android source file path.
445*c2e18aaaSAndroid Build Coastguard Worker        excludes: A list of exclusive directories, the default value is None but
446*c2e18aaaSAndroid Build Coastguard Worker                  will be assigned to _EXCLUDE_FOLDERS.
447*c2e18aaaSAndroid Build Coastguard Worker
448*c2e18aaaSAndroid Build Coastguard Worker    Returns:
449*c2e18aaaSAndroid Build Coastguard Worker        String: exclude folder content list.
450*c2e18aaaSAndroid Build Coastguard Worker    """
451*c2e18aaaSAndroid Build Coastguard Worker    exclude_items = []
452*c2e18aaaSAndroid Build Coastguard Worker    if not excludes:
453*c2e18aaaSAndroid Build Coastguard Worker        excludes = _EXCLUDE_FOLDERS
454*c2e18aaaSAndroid Build Coastguard Worker    for folder in excludes:
455*c2e18aaaSAndroid Build Coastguard Worker        folder_path = os.path.join(root_path, folder)
456*c2e18aaaSAndroid Build Coastguard Worker        if os.path.isdir(folder_path):
457*c2e18aaaSAndroid Build Coastguard Worker            exclude_items.append(_EXCLUDE_ITEM % folder_path)
458*c2e18aaaSAndroid Build Coastguard Worker    return exclude_items
459*c2e18aaaSAndroid Build Coastguard Worker
460*c2e18aaaSAndroid Build Coastguard Worker
461*c2e18aaaSAndroid Build Coastguard Workerdef _remove_child_duplicate_sources_from_parent(child, parent_sources, root):
462*c2e18aaaSAndroid Build Coastguard Worker    """Removes the child's duplicate source folders from the parent source list.
463*c2e18aaaSAndroid Build Coastguard Worker
464*c2e18aaaSAndroid Build Coastguard Worker    Remove all the child's subdirectories from the parent's source list if there
465*c2e18aaaSAndroid Build Coastguard Worker    is any.
466*c2e18aaaSAndroid Build Coastguard Worker
467*c2e18aaaSAndroid Build Coastguard Worker    Args:
468*c2e18aaaSAndroid Build Coastguard Worker        child: A child project of ProjectInfo instance.
469*c2e18aaaSAndroid Build Coastguard Worker        parent_sources: The parent project sources of the ProjectInfo instance.
470*c2e18aaaSAndroid Build Coastguard Worker        root: A string of the Android root.
471*c2e18aaaSAndroid Build Coastguard Worker
472*c2e18aaaSAndroid Build Coastguard Worker    Returns:
473*c2e18aaaSAndroid Build Coastguard Worker        A set of the sources to be removed.
474*c2e18aaaSAndroid Build Coastguard Worker    """
475*c2e18aaaSAndroid Build Coastguard Worker    rm_paths = set()
476*c2e18aaaSAndroid Build Coastguard Worker    for path in parent_sources:
477*c2e18aaaSAndroid Build Coastguard Worker        if (common_util.is_source_under_relative_path(
478*c2e18aaaSAndroid Build Coastguard Worker                os.path.relpath(path, root), child.project_relative_path)):
479*c2e18aaaSAndroid Build Coastguard Worker            rm_paths.add(path)
480*c2e18aaaSAndroid Build Coastguard Worker    return rm_paths
481*c2e18aaaSAndroid Build Coastguard Worker
482*c2e18aaaSAndroid Build Coastguard Worker
483*c2e18aaaSAndroid Build Coastguard Workerdef _get_permission_aapt2_rel_path():
484*c2e18aaaSAndroid Build Coastguard Worker    """Gets android.Manifest.permission definition srcjar path."""
485*c2e18aaaSAndroid Build Coastguard Worker    out_soong_dir = os.path.relpath(common_util.get_soong_out_path(),
486*c2e18aaaSAndroid Build Coastguard Worker                                    common_util.get_android_root_dir())
487*c2e18aaaSAndroid Build Coastguard Worker    return os.path.join(out_soong_dir, constant.INTERMEDIATES,
488*c2e18aaaSAndroid Build Coastguard Worker                        _PERMISSION_DEFINED_PATH, constant.NAME_AAPT2, _R)
489*c2e18aaaSAndroid Build Coastguard Worker
490*c2e18aaaSAndroid Build Coastguard Worker
491*c2e18aaaSAndroid Build Coastguard Workerdef _get_permission_r_srcjar_rel_path():
492*c2e18aaaSAndroid Build Coastguard Worker    """Gets android.Manifest.permission definition srcjar path."""
493*c2e18aaaSAndroid Build Coastguard Worker    out_soong_dir = os.path.relpath(common_util.get_soong_out_path(),
494*c2e18aaaSAndroid Build Coastguard Worker                                    common_util.get_android_root_dir())
495*c2e18aaaSAndroid Build Coastguard Worker    return os.path.join(out_soong_dir, constant.INTERMEDIATES,
496*c2e18aaaSAndroid Build Coastguard Worker                        _PERMISSION_DEFINED_PATH, _ANDROID,
497*c2e18aaaSAndroid Build Coastguard Worker                        constant.TARGET_R_SRCJAR)
498