xref: /aosp_15_r20/tools/asuite/aidegen/idea/iml.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"""Creates the iml file for each module.
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard WorkerThis class is used to create the iml file for each module. So far, only generate
20*c2e18aaaSAndroid Build Coastguard Workerthe create_srcjar() for the framework-all module.
21*c2e18aaaSAndroid Build Coastguard Worker
22*c2e18aaaSAndroid Build Coastguard WorkerUsage example:
23*c2e18aaaSAndroid Build Coastguard Worker    modules_info = project_info.ProjectInfo.modules_info
24*c2e18aaaSAndroid Build Coastguard Worker    mod_info = modules_info.name_to_module_info['module']
25*c2e18aaaSAndroid Build Coastguard Worker    iml = IMLGenerator(mod_info)
26*c2e18aaaSAndroid Build Coastguard Worker    iml.create()
27*c2e18aaaSAndroid Build Coastguard Worker"""
28*c2e18aaaSAndroid Build Coastguard Worker
29*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import absolute_import
30*c2e18aaaSAndroid Build Coastguard Worker
31*c2e18aaaSAndroid Build Coastguard Workerimport logging
32*c2e18aaaSAndroid Build Coastguard Workerimport os
33*c2e18aaaSAndroid Build Coastguard Worker
34*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import constant
35*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import templates
36*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import common_util
37*c2e18aaaSAndroid Build Coastguard Worker
38*c2e18aaaSAndroid Build Coastguard Worker
39*c2e18aaaSAndroid Build Coastguard Workerclass IMLGenerator:
40*c2e18aaaSAndroid Build Coastguard Worker    """Creates the iml file for each module.
41*c2e18aaaSAndroid Build Coastguard Worker
42*c2e18aaaSAndroid Build Coastguard Worker    Class attributes:
43*c2e18aaaSAndroid Build Coastguard Worker        _USED_NAME_CACHE: A dict to cache already used iml project file names
44*c2e18aaaSAndroid Build Coastguard Worker                          and prevent duplicated iml names from breaking IDEA.
45*c2e18aaaSAndroid Build Coastguard Worker
46*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
47*c2e18aaaSAndroid Build Coastguard Worker        _mod_info: A dictionary of the module's data from module-info.json.
48*c2e18aaaSAndroid Build Coastguard Worker        _android_root: A string ot the Android root's absolute path.
49*c2e18aaaSAndroid Build Coastguard Worker        _mod_path: A string of the module's absolute path.
50*c2e18aaaSAndroid Build Coastguard Worker        _iml_path: A string of the module's iml absolute path.
51*c2e18aaaSAndroid Build Coastguard Worker        _facet: A string of the facet setting.
52*c2e18aaaSAndroid Build Coastguard Worker        _excludes: A string of the exclude relative paths.
53*c2e18aaaSAndroid Build Coastguard Worker        _srcs: A string of the source urls.
54*c2e18aaaSAndroid Build Coastguard Worker        _jars: A list of the jar urls.
55*c2e18aaaSAndroid Build Coastguard Worker        _srcjars: A list of srcjar urls.
56*c2e18aaaSAndroid Build Coastguard Worker        _deps: A list of the dependency module urls.
57*c2e18aaaSAndroid Build Coastguard Worker    """
58*c2e18aaaSAndroid Build Coastguard Worker    # b/121256503: Prevent duplicated iml names from breaking IDEA.
59*c2e18aaaSAndroid Build Coastguard Worker    # Use a map to cache in-using(already used) iml project file names.
60*c2e18aaaSAndroid Build Coastguard Worker    USED_NAME_CACHE = {}
61*c2e18aaaSAndroid Build Coastguard Worker
62*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, mod_info):
63*c2e18aaaSAndroid Build Coastguard Worker        """Initializes IMLGenerator.
64*c2e18aaaSAndroid Build Coastguard Worker
65*c2e18aaaSAndroid Build Coastguard Worker        Args:
66*c2e18aaaSAndroid Build Coastguard Worker            mod_info: A dictionary of the module's data from module-info.json.
67*c2e18aaaSAndroid Build Coastguard Worker        """
68*c2e18aaaSAndroid Build Coastguard Worker        self._mod_info = mod_info
69*c2e18aaaSAndroid Build Coastguard Worker        self._android_root = common_util.get_android_root_dir()
70*c2e18aaaSAndroid Build Coastguard Worker        self._mod_path = os.path.join(self._android_root,
71*c2e18aaaSAndroid Build Coastguard Worker                                      mod_info[constant.KEY_PATH][0])
72*c2e18aaaSAndroid Build Coastguard Worker        self._iml_path = os.path.join(self._mod_path,
73*c2e18aaaSAndroid Build Coastguard Worker                                      mod_info[constant.KEY_IML_NAME] + '.iml')
74*c2e18aaaSAndroid Build Coastguard Worker        self._facet = ''
75*c2e18aaaSAndroid Build Coastguard Worker        self._excludes = ''
76*c2e18aaaSAndroid Build Coastguard Worker        self._srcs = ''
77*c2e18aaaSAndroid Build Coastguard Worker        self._jars = []
78*c2e18aaaSAndroid Build Coastguard Worker        self._srcjars = []
79*c2e18aaaSAndroid Build Coastguard Worker        self._deps = []
80*c2e18aaaSAndroid Build Coastguard Worker
81*c2e18aaaSAndroid Build Coastguard Worker    @classmethod
82*c2e18aaaSAndroid Build Coastguard Worker    def get_unique_iml_name(cls, abs_module_path):
83*c2e18aaaSAndroid Build Coastguard Worker        """Create a unique iml name if needed.
84*c2e18aaaSAndroid Build Coastguard Worker
85*c2e18aaaSAndroid Build Coastguard Worker        If the name of last sub folder is used already, prefixing it with prior
86*c2e18aaaSAndroid Build Coastguard Worker        sub folder names as a candidate name. If finally, it's unique, storing
87*c2e18aaaSAndroid Build Coastguard Worker        in USED_NAME_CACHE as: { abs_module_path:unique_name }. The cts case
88*c2e18aaaSAndroid Build Coastguard Worker        and UX of IDE view are the main reasons why using module path strategy
89*c2e18aaaSAndroid Build Coastguard Worker        but not name of module directly. Following is the detailed strategy:
90*c2e18aaaSAndroid Build Coastguard Worker        1. While loop composes a sensible and shorter name, by checking unique
91*c2e18aaaSAndroid Build Coastguard Worker           to finish the loop and finally add to cache.
92*c2e18aaaSAndroid Build Coastguard Worker           Take ['cts', 'tests', 'app', 'ui'] an example, if 'ui' isn't
93*c2e18aaaSAndroid Build Coastguard Worker           occupied, use it, else try 'cts_ui', then 'cts_app_ui', the worst
94*c2e18aaaSAndroid Build Coastguard Worker           case is whole three candidate names are occupied already.
95*c2e18aaaSAndroid Build Coastguard Worker        2. 'Else' for that while stands for no suitable name generated, so
96*c2e18aaaSAndroid Build Coastguard Worker           trying 'cts_tests_app_ui' directly. If it's still non unique, e.g.,
97*c2e18aaaSAndroid Build Coastguard Worker           module path cts/xxx/tests/app/ui occupied that name already,
98*c2e18aaaSAndroid Build Coastguard Worker           appending increasing sequence number to get a unique name.
99*c2e18aaaSAndroid Build Coastguard Worker
100*c2e18aaaSAndroid Build Coastguard Worker        Args:
101*c2e18aaaSAndroid Build Coastguard Worker            abs_module_path: The absolute module path string.
102*c2e18aaaSAndroid Build Coastguard Worker
103*c2e18aaaSAndroid Build Coastguard Worker        Return:
104*c2e18aaaSAndroid Build Coastguard Worker            String: A unique iml name.
105*c2e18aaaSAndroid Build Coastguard Worker        """
106*c2e18aaaSAndroid Build Coastguard Worker        if abs_module_path in cls.USED_NAME_CACHE:
107*c2e18aaaSAndroid Build Coastguard Worker            return cls.USED_NAME_CACHE[abs_module_path]
108*c2e18aaaSAndroid Build Coastguard Worker
109*c2e18aaaSAndroid Build Coastguard Worker        uniq_name = abs_module_path.strip(os.sep).split(os.sep)[-1]
110*c2e18aaaSAndroid Build Coastguard Worker
111*c2e18aaaSAndroid Build Coastguard Worker        if any(uniq_name == name for name in cls.USED_NAME_CACHE.values()):
112*c2e18aaaSAndroid Build Coastguard Worker            parent_path = os.path.relpath(abs_module_path,
113*c2e18aaaSAndroid Build Coastguard Worker                                          common_util.get_android_root_dir())
114*c2e18aaaSAndroid Build Coastguard Worker            sub_folders = parent_path.split(os.sep)
115*c2e18aaaSAndroid Build Coastguard Worker            zero_base_index = len(sub_folders) - 1
116*c2e18aaaSAndroid Build Coastguard Worker            # Start compose a sensible, shorter and unique name.
117*c2e18aaaSAndroid Build Coastguard Worker            while zero_base_index > 0:
118*c2e18aaaSAndroid Build Coastguard Worker                uniq_name = '_'.join(
119*c2e18aaaSAndroid Build Coastguard Worker                    [sub_folders[0], '_'.join(sub_folders[zero_base_index:])])
120*c2e18aaaSAndroid Build Coastguard Worker                zero_base_index = zero_base_index - 1
121*c2e18aaaSAndroid Build Coastguard Worker                if uniq_name not in cls.USED_NAME_CACHE.values():
122*c2e18aaaSAndroid Build Coastguard Worker                    break
123*c2e18aaaSAndroid Build Coastguard Worker            else:
124*c2e18aaaSAndroid Build Coastguard Worker                # For full source tree case, there are 2 path cases w/wo "/".
125*c2e18aaaSAndroid Build Coastguard Worker                # In the 2nd run, if abs_module_path is root dir,
126*c2e18aaaSAndroid Build Coastguard Worker                # it will use uniq_name directly.
127*c2e18aaaSAndroid Build Coastguard Worker                if parent_path == ".":
128*c2e18aaaSAndroid Build Coastguard Worker                    pass
129*c2e18aaaSAndroid Build Coastguard Worker                else:
130*c2e18aaaSAndroid Build Coastguard Worker                    # TODO(b/133393638): To handle several corner cases.
131*c2e18aaaSAndroid Build Coastguard Worker                    uniq_name_base = parent_path.strip(os.sep).replace(os.sep,
132*c2e18aaaSAndroid Build Coastguard Worker                                                                       '_')
133*c2e18aaaSAndroid Build Coastguard Worker                    i = 0
134*c2e18aaaSAndroid Build Coastguard Worker                    uniq_name = uniq_name_base
135*c2e18aaaSAndroid Build Coastguard Worker                    while uniq_name in cls.USED_NAME_CACHE.values():
136*c2e18aaaSAndroid Build Coastguard Worker                        i = i + 1
137*c2e18aaaSAndroid Build Coastguard Worker                        uniq_name = '_'.join([uniq_name_base, str(i)])
138*c2e18aaaSAndroid Build Coastguard Worker        cls.USED_NAME_CACHE[abs_module_path] = uniq_name
139*c2e18aaaSAndroid Build Coastguard Worker        logging.debug('Unique name for module path of %s is %s.',
140*c2e18aaaSAndroid Build Coastguard Worker                      abs_module_path, uniq_name)
141*c2e18aaaSAndroid Build Coastguard Worker        return uniq_name
142*c2e18aaaSAndroid Build Coastguard Worker
143*c2e18aaaSAndroid Build Coastguard Worker    @property
144*c2e18aaaSAndroid Build Coastguard Worker    def iml_path(self):
145*c2e18aaaSAndroid Build Coastguard Worker        """Gets the iml path."""
146*c2e18aaaSAndroid Build Coastguard Worker        return self._iml_path
147*c2e18aaaSAndroid Build Coastguard Worker
148*c2e18aaaSAndroid Build Coastguard Worker    def create(self, content_type):
149*c2e18aaaSAndroid Build Coastguard Worker        """Creates the iml file.
150*c2e18aaaSAndroid Build Coastguard Worker
151*c2e18aaaSAndroid Build Coastguard Worker        Create the iml file with specific part of sources.
152*c2e18aaaSAndroid Build Coastguard Worker        e.g.
153*c2e18aaaSAndroid Build Coastguard Worker        {
154*c2e18aaaSAndroid Build Coastguard Worker            'srcs': True,
155*c2e18aaaSAndroid Build Coastguard Worker            'dependencies': True,
156*c2e18aaaSAndroid Build Coastguard Worker        }
157*c2e18aaaSAndroid Build Coastguard Worker
158*c2e18aaaSAndroid Build Coastguard Worker        Args:
159*c2e18aaaSAndroid Build Coastguard Worker            content_type: A dict to set which part of sources will be created.
160*c2e18aaaSAndroid Build Coastguard Worker        """
161*c2e18aaaSAndroid Build Coastguard Worker        if content_type.get(constant.KEY_SRCS, None):
162*c2e18aaaSAndroid Build Coastguard Worker            self._generate_srcs()
163*c2e18aaaSAndroid Build Coastguard Worker        if content_type.get(constant.KEY_DEP_SRCS, None):
164*c2e18aaaSAndroid Build Coastguard Worker            self._generate_dep_srcs()
165*c2e18aaaSAndroid Build Coastguard Worker        if content_type.get(constant.KEY_JARS, None):
166*c2e18aaaSAndroid Build Coastguard Worker            self._generate_jars()
167*c2e18aaaSAndroid Build Coastguard Worker        if content_type.get(constant.KEY_SRCJARS, None):
168*c2e18aaaSAndroid Build Coastguard Worker            self._generate_srcjars()
169*c2e18aaaSAndroid Build Coastguard Worker        if content_type.get(constant.KEY_DEPENDENCIES, None):
170*c2e18aaaSAndroid Build Coastguard Worker            self._generate_dependencies()
171*c2e18aaaSAndroid Build Coastguard Worker
172*c2e18aaaSAndroid Build Coastguard Worker        if self._srcs or self._jars or self._srcjars or self._deps:
173*c2e18aaaSAndroid Build Coastguard Worker            self._create_iml()
174*c2e18aaaSAndroid Build Coastguard Worker
175*c2e18aaaSAndroid Build Coastguard Worker    def _generate_facet(self):
176*c2e18aaaSAndroid Build Coastguard Worker        """Generates the facet when the AndroidManifest.xml exists."""
177*c2e18aaaSAndroid Build Coastguard Worker        if os.path.exists(os.path.join(self._mod_path,
178*c2e18aaaSAndroid Build Coastguard Worker                                       constant.ANDROID_MANIFEST)):
179*c2e18aaaSAndroid Build Coastguard Worker            self._facet = templates.FACET
180*c2e18aaaSAndroid Build Coastguard Worker
181*c2e18aaaSAndroid Build Coastguard Worker    def _generate_srcs(self):
182*c2e18aaaSAndroid Build Coastguard Worker        """Generates the source urls of the project's iml file."""
183*c2e18aaaSAndroid Build Coastguard Worker        srcs = []
184*c2e18aaaSAndroid Build Coastguard Worker        framework_srcs = []
185*c2e18aaaSAndroid Build Coastguard Worker        for src in self._mod_info.get(constant.KEY_SRCS, []):
186*c2e18aaaSAndroid Build Coastguard Worker            if constant.FRAMEWORK_PATH in src:
187*c2e18aaaSAndroid Build Coastguard Worker                framework_srcs.append(templates.SOURCE.format(
188*c2e18aaaSAndroid Build Coastguard Worker                    SRC=os.path.join(self._android_root, src),
189*c2e18aaaSAndroid Build Coastguard Worker                    IS_TEST='false'))
190*c2e18aaaSAndroid Build Coastguard Worker                continue
191*c2e18aaaSAndroid Build Coastguard Worker            srcs.append(templates.SOURCE.format(
192*c2e18aaaSAndroid Build Coastguard Worker                SRC=os.path.join(self._android_root, src),
193*c2e18aaaSAndroid Build Coastguard Worker                IS_TEST='false'))
194*c2e18aaaSAndroid Build Coastguard Worker        for test in self._mod_info.get(constant.KEY_TESTS, []):
195*c2e18aaaSAndroid Build Coastguard Worker            if constant.FRAMEWORK_PATH in test:
196*c2e18aaaSAndroid Build Coastguard Worker                framework_srcs.append(templates.SOURCE.format(
197*c2e18aaaSAndroid Build Coastguard Worker                    SRC=os.path.join(self._android_root, test),
198*c2e18aaaSAndroid Build Coastguard Worker                    IS_TEST='true'))
199*c2e18aaaSAndroid Build Coastguard Worker                continue
200*c2e18aaaSAndroid Build Coastguard Worker            srcs.append(templates.SOURCE.format(
201*c2e18aaaSAndroid Build Coastguard Worker                SRC=os.path.join(self._android_root, test),
202*c2e18aaaSAndroid Build Coastguard Worker                IS_TEST='true'))
203*c2e18aaaSAndroid Build Coastguard Worker        self._excludes = self._mod_info.get(constant.KEY_EXCLUDES, '')
204*c2e18aaaSAndroid Build Coastguard Worker
205*c2e18aaaSAndroid Build Coastguard Worker        # For solving duplicate package name, frameworks/base will be higher
206*c2e18aaaSAndroid Build Coastguard Worker        # priority.
207*c2e18aaaSAndroid Build Coastguard Worker        srcs = sorted(framework_srcs) + sorted(srcs)
208*c2e18aaaSAndroid Build Coastguard Worker        self._srcs = templates.CONTENT.format(MODULE_PATH=self._mod_path,
209*c2e18aaaSAndroid Build Coastguard Worker                                              EXCLUDES=self._excludes,
210*c2e18aaaSAndroid Build Coastguard Worker                                              SOURCES=''.join(srcs))
211*c2e18aaaSAndroid Build Coastguard Worker
212*c2e18aaaSAndroid Build Coastguard Worker    def _generate_dep_srcs(self):
213*c2e18aaaSAndroid Build Coastguard Worker        """Generates the source urls of the dependencies.iml."""
214*c2e18aaaSAndroid Build Coastguard Worker        srcs = []
215*c2e18aaaSAndroid Build Coastguard Worker        for src in self._mod_info.get(constant.KEY_SRCS, []):
216*c2e18aaaSAndroid Build Coastguard Worker            srcs.append(templates.OTHER_SOURCE.format(
217*c2e18aaaSAndroid Build Coastguard Worker                SRC=os.path.join(self._android_root, src),
218*c2e18aaaSAndroid Build Coastguard Worker                IS_TEST='false'))
219*c2e18aaaSAndroid Build Coastguard Worker        for test in self._mod_info.get(constant.KEY_TESTS, []):
220*c2e18aaaSAndroid Build Coastguard Worker            srcs.append(templates.OTHER_SOURCE.format(
221*c2e18aaaSAndroid Build Coastguard Worker                SRC=os.path.join(self._android_root, test),
222*c2e18aaaSAndroid Build Coastguard Worker                IS_TEST='true'))
223*c2e18aaaSAndroid Build Coastguard Worker        self._srcs = ''.join(sorted(srcs))
224*c2e18aaaSAndroid Build Coastguard Worker
225*c2e18aaaSAndroid Build Coastguard Worker    def _generate_jars(self):
226*c2e18aaaSAndroid Build Coastguard Worker        """Generates the jar urls."""
227*c2e18aaaSAndroid Build Coastguard Worker        for jar in self._mod_info.get(constant.KEY_JARS, []):
228*c2e18aaaSAndroid Build Coastguard Worker            self._jars.append(templates.JAR.format(
229*c2e18aaaSAndroid Build Coastguard Worker                JAR=os.path.join(self._android_root, jar)))
230*c2e18aaaSAndroid Build Coastguard Worker
231*c2e18aaaSAndroid Build Coastguard Worker    def _generate_srcjars(self):
232*c2e18aaaSAndroid Build Coastguard Worker        """Generates the srcjar urls."""
233*c2e18aaaSAndroid Build Coastguard Worker        for srcjar in self._mod_info.get(constant.KEY_SRCJARS, []):
234*c2e18aaaSAndroid Build Coastguard Worker            self._srcjars.append(templates.SRCJAR.format(
235*c2e18aaaSAndroid Build Coastguard Worker                SRCJAR=os.path.join(self._android_root, srcjar)))
236*c2e18aaaSAndroid Build Coastguard Worker
237*c2e18aaaSAndroid Build Coastguard Worker    def _generate_dependencies(self):
238*c2e18aaaSAndroid Build Coastguard Worker        """Generates the dependency module urls."""
239*c2e18aaaSAndroid Build Coastguard Worker        for dep in self._mod_info.get(constant.KEY_DEPENDENCIES, []):
240*c2e18aaaSAndroid Build Coastguard Worker            self._deps.append(templates.DEPENDENCIES.format(MODULE=dep))
241*c2e18aaaSAndroid Build Coastguard Worker
242*c2e18aaaSAndroid Build Coastguard Worker    def _create_iml(self):
243*c2e18aaaSAndroid Build Coastguard Worker        """Creates the iml file."""
244*c2e18aaaSAndroid Build Coastguard Worker        content = templates.IML.format(FACET=self._facet,
245*c2e18aaaSAndroid Build Coastguard Worker                                       SOURCES=self._srcs,
246*c2e18aaaSAndroid Build Coastguard Worker                                       JARS=''.join(self._jars),
247*c2e18aaaSAndroid Build Coastguard Worker                                       SRCJARS=''.join(self._srcjars),
248*c2e18aaaSAndroid Build Coastguard Worker                                       DEPENDENCIES=''.join(self._deps))
249*c2e18aaaSAndroid Build Coastguard Worker        common_util.file_generate(self._iml_path, content)
250