xref: /aosp_15_r20/tools/asuite/aidegen/lib/ide_util.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"""It is an AIDEGen sub task : IDE operation task!
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard WorkerTakes a project file path as input, after passing the needed check(file
20*c2e18aaaSAndroid Build Coastguard Workerexistence, IDE type, etc.), launch the project in related IDE.
21*c2e18aaaSAndroid Build Coastguard Worker
22*c2e18aaaSAndroid Build Coastguard Worker    Typical usage example:
23*c2e18aaaSAndroid Build Coastguard Worker
24*c2e18aaaSAndroid Build Coastguard Worker    ide_util_obj = IdeUtil()
25*c2e18aaaSAndroid Build Coastguard Worker    if ide_util_obj.is_ide_installed():
26*c2e18aaaSAndroid Build Coastguard Worker        ide_util_obj.config_ide(project_file)
27*c2e18aaaSAndroid Build Coastguard Worker        ide_util_obj.launch_ide()
28*c2e18aaaSAndroid Build Coastguard Worker
29*c2e18aaaSAndroid Build Coastguard Worker        # Get the configuration folders of IntelliJ or Android Studio.
30*c2e18aaaSAndroid Build Coastguard Worker        ide_util_obj.get_ide_config_folders()
31*c2e18aaaSAndroid Build Coastguard Worker"""
32*c2e18aaaSAndroid Build Coastguard Worker
33*c2e18aaaSAndroid Build Coastguard Workerimport glob
34*c2e18aaaSAndroid Build Coastguard Workerimport logging
35*c2e18aaaSAndroid Build Coastguard Workerimport os
36*c2e18aaaSAndroid Build Coastguard Workerimport platform
37*c2e18aaaSAndroid Build Coastguard Workerimport re
38*c2e18aaaSAndroid Build Coastguard Workerimport subprocess
39*c2e18aaaSAndroid Build Coastguard Worker
40*c2e18aaaSAndroid Build Coastguard Workerfrom xml.etree import ElementTree
41*c2e18aaaSAndroid Build Coastguard Worker
42*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import constant
43*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import templates
44*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import aidegen_metrics
45*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import android_dev_os
46*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import common_util
47*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import config
48*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import errors
49*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import ide_common_util
50*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import project_config
51*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import project_file_gen
52*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.sdk import jdk_table
53*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import xml_util
54*c2e18aaaSAndroid Build Coastguard Worker
55*c2e18aaaSAndroid Build Coastguard Worker# Add 'nohup' to prevent IDE from being terminated when console is terminated.
56*c2e18aaaSAndroid Build Coastguard Worker_IDEA_FOLDER = '.idea'
57*c2e18aaaSAndroid Build Coastguard Worker_IML_EXTENSION = '.iml'
58*c2e18aaaSAndroid Build Coastguard Worker_JDK_PATH_TOKEN = '@JDKpath'
59*c2e18aaaSAndroid Build Coastguard Worker_COMPONENT_END_TAG = '  </component>'
60*c2e18aaaSAndroid Build Coastguard Worker_ECLIPSE_WS = '~/Documents/AIDEGen_Eclipse_workspace'
61*c2e18aaaSAndroid Build Coastguard Worker_ALERT_CREATE_WS = ('AIDEGen will create a workspace at %s for Eclipse, '
62*c2e18aaaSAndroid Build Coastguard Worker                    'Enter `y` to allow AIDEgen to automatically create the '
63*c2e18aaaSAndroid Build Coastguard Worker                    'workspace for you. Otherwise, you need to select the '
64*c2e18aaaSAndroid Build Coastguard Worker                    'workspace after Eclipse is launched.\nWould you like '
65*c2e18aaaSAndroid Build Coastguard Worker                    'AIDEgen to automatically create the workspace for you?'
66*c2e18aaaSAndroid Build Coastguard Worker                    '(y/n)' % constant.ECLIPSE_WS)
67*c2e18aaaSAndroid Build Coastguard Worker_NO_LAUNCH_IDE_CMD = """
68*c2e18aaaSAndroid Build Coastguard WorkerCan not find IDE: {}, in path: {}, you can:
69*c2e18aaaSAndroid Build Coastguard Worker    - add IDE executable to your $PATH
70*c2e18aaaSAndroid Build Coastguard Workeror  - specify the exact IDE executable path by "aidegen -p"
71*c2e18aaaSAndroid Build Coastguard Workeror  - specify "aidegen -n" to generate project file only
72*c2e18aaaSAndroid Build Coastguard Worker"""
73*c2e18aaaSAndroid Build Coastguard Worker_INFO_IMPORT_CONFIG = ('{} needs to import the application configuration for '
74*c2e18aaaSAndroid Build Coastguard Worker                       'the new version!\nAfter the import is finished, rerun '
75*c2e18aaaSAndroid Build Coastguard Worker                       'the command if your project did not launch. Please '
76*c2e18aaaSAndroid Build Coastguard Worker                       'follow the showing dialog to finish the import action.'
77*c2e18aaaSAndroid Build Coastguard Worker                       '\n\n')
78*c2e18aaaSAndroid Build Coastguard WorkerCONFIG_DIR = 'config'
79*c2e18aaaSAndroid Build Coastguard WorkerLINUX_JDK_PATH = os.path.join(common_util.get_android_root_dir(),
80*c2e18aaaSAndroid Build Coastguard Worker                              'prebuilts/jdk/jdk21/linux-x86')
81*c2e18aaaSAndroid Build Coastguard WorkerLINUX_JDK_TABLE_PATH = 'config/options/jdk.table.xml'
82*c2e18aaaSAndroid Build Coastguard WorkerLINUX_FILE_TYPE_PATH = 'config/options/filetypes.xml'
83*c2e18aaaSAndroid Build Coastguard WorkerLINUX_ANDROID_SDK_PATH = os.path.join(os.getenv('HOME'), 'Android/Sdk')
84*c2e18aaaSAndroid Build Coastguard WorkerMAC_JDK_PATH = os.path.join(common_util.get_android_root_dir(),
85*c2e18aaaSAndroid Build Coastguard Worker                            'prebuilts/jdk/jdk21/darwin-x86')
86*c2e18aaaSAndroid Build Coastguard WorkerALTERNATIVE_JDK_TABLE_PATH = 'options/jdk.table.xml'
87*c2e18aaaSAndroid Build Coastguard WorkerALTERNATIVE_FILE_TYPE_XML_PATH = 'options/filetypes.xml'
88*c2e18aaaSAndroid Build Coastguard WorkerMAC_ANDROID_SDK_PATH = os.path.join(os.getenv('HOME'), 'Library/Android/sdk')
89*c2e18aaaSAndroid Build Coastguard WorkerPATTERN_KEY = 'pattern'
90*c2e18aaaSAndroid Build Coastguard WorkerTYPE_KEY = 'type'
91*c2e18aaaSAndroid Build Coastguard Worker_TEST_MAPPING_FILE_TYPE = 'JSON'
92*c2e18aaaSAndroid Build Coastguard WorkerTEST_MAPPING_NAME = 'TEST_MAPPING'
93*c2e18aaaSAndroid Build Coastguard Worker_TEST_MAPPING_TYPE = '<mapping pattern="TEST_MAPPING" type="JSON" />'
94*c2e18aaaSAndroid Build Coastguard Worker_XPATH_EXTENSION_MAP = 'component/extensionMap'
95*c2e18aaaSAndroid Build Coastguard Worker_XPATH_MAPPING = _XPATH_EXTENSION_MAP + '/mapping'
96*c2e18aaaSAndroid Build Coastguard Worker_SPECIFIC_INTELLIJ_VERSION = 2020.1
97*c2e18aaaSAndroid Build Coastguard Worker_TEST_MAPPING_FILE_TYPE_ADDING_WARN = '\n{} {}\n'.format(
98*c2e18aaaSAndroid Build Coastguard Worker    common_util.COLORED_INFO('WARNING:'),
99*c2e18aaaSAndroid Build Coastguard Worker    ('TEST_MAPPING file type can\'t be added to filetypes.xml. The reason '
100*c2e18aaaSAndroid Build Coastguard Worker     'might be: lack of the parent tag to add TEST_MAPPING file type.'))
101*c2e18aaaSAndroid Build Coastguard Worker
102*c2e18aaaSAndroid Build Coastguard Worker
103*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=too-many-lines
104*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=invalid-name
105*c2e18aaaSAndroid Build Coastguard Workerclass IdeUtil:
106*c2e18aaaSAndroid Build Coastguard Worker    """Provide a set of IDE operations, e.g., launch and configuration.
107*c2e18aaaSAndroid Build Coastguard Worker
108*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
109*c2e18aaaSAndroid Build Coastguard Worker        _ide: IdeBase derived instance, the related IDE object.
110*c2e18aaaSAndroid Build Coastguard Worker
111*c2e18aaaSAndroid Build Coastguard Worker    For example:
112*c2e18aaaSAndroid Build Coastguard Worker        1. Check if IDE is installed.
113*c2e18aaaSAndroid Build Coastguard Worker        2. Config IDE, e.g. config code style, SDK path, and etc.
114*c2e18aaaSAndroid Build Coastguard Worker        3. Launch an IDE.
115*c2e18aaaSAndroid Build Coastguard Worker    """
116*c2e18aaaSAndroid Build Coastguard Worker
117*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self,
118*c2e18aaaSAndroid Build Coastguard Worker                 installed_path=None,
119*c2e18aaaSAndroid Build Coastguard Worker                 ide='j',
120*c2e18aaaSAndroid Build Coastguard Worker                 config_reset=False,
121*c2e18aaaSAndroid Build Coastguard Worker                 is_mac=False):
122*c2e18aaaSAndroid Build Coastguard Worker        logging.debug('IdeUtil with OS name: %s%s', platform.system(),
123*c2e18aaaSAndroid Build Coastguard Worker                      '(Mac)' if is_mac else '')
124*c2e18aaaSAndroid Build Coastguard Worker        self._ide = _get_ide(installed_path, ide, config_reset, is_mac)
125*c2e18aaaSAndroid Build Coastguard Worker
126*c2e18aaaSAndroid Build Coastguard Worker    def is_ide_installed(self):
127*c2e18aaaSAndroid Build Coastguard Worker        """Checks if the IDE is already installed.
128*c2e18aaaSAndroid Build Coastguard Worker
129*c2e18aaaSAndroid Build Coastguard Worker        Returns:
130*c2e18aaaSAndroid Build Coastguard Worker            True if IDE is installed already, otherwise False.
131*c2e18aaaSAndroid Build Coastguard Worker        """
132*c2e18aaaSAndroid Build Coastguard Worker        return self._ide.is_ide_installed()
133*c2e18aaaSAndroid Build Coastguard Worker
134*c2e18aaaSAndroid Build Coastguard Worker    def launch_ide(self):
135*c2e18aaaSAndroid Build Coastguard Worker        """Launches the relative IDE by opening the passed project file."""
136*c2e18aaaSAndroid Build Coastguard Worker        return self._ide.launch_ide()
137*c2e18aaaSAndroid Build Coastguard Worker
138*c2e18aaaSAndroid Build Coastguard Worker    def config_ide(self, project_abspath):
139*c2e18aaaSAndroid Build Coastguard Worker        """To config the IDE, e.g., setup code style, init SDK, and etc.
140*c2e18aaaSAndroid Build Coastguard Worker
141*c2e18aaaSAndroid Build Coastguard Worker        Args:
142*c2e18aaaSAndroid Build Coastguard Worker            project_abspath: An absolute path of the project.
143*c2e18aaaSAndroid Build Coastguard Worker        """
144*c2e18aaaSAndroid Build Coastguard Worker        self._ide.project_abspath = project_abspath
145*c2e18aaaSAndroid Build Coastguard Worker        if self.is_ide_installed() and self._ide:
146*c2e18aaaSAndroid Build Coastguard Worker            self._ide.apply_optional_config()
147*c2e18aaaSAndroid Build Coastguard Worker
148*c2e18aaaSAndroid Build Coastguard Worker    def get_default_path(self):
149*c2e18aaaSAndroid Build Coastguard Worker        """Gets IDE default installed path."""
150*c2e18aaaSAndroid Build Coastguard Worker        return self._ide.default_installed_path
151*c2e18aaaSAndroid Build Coastguard Worker
152*c2e18aaaSAndroid Build Coastguard Worker    def ide_name(self):
153*c2e18aaaSAndroid Build Coastguard Worker        """Gets IDE name."""
154*c2e18aaaSAndroid Build Coastguard Worker        return self._ide.ide_name
155*c2e18aaaSAndroid Build Coastguard Worker
156*c2e18aaaSAndroid Build Coastguard Worker    def get_ide_config_folders(self):
157*c2e18aaaSAndroid Build Coastguard Worker        """Gets the config folders of IDE."""
158*c2e18aaaSAndroid Build Coastguard Worker        return self._ide.config_folders
159*c2e18aaaSAndroid Build Coastguard Worker
160*c2e18aaaSAndroid Build Coastguard Worker
161*c2e18aaaSAndroid Build Coastguard Workerclass IdeBase:
162*c2e18aaaSAndroid Build Coastguard Worker    """The most base class of IDE, provides interface and partial path init.
163*c2e18aaaSAndroid Build Coastguard Worker
164*c2e18aaaSAndroid Build Coastguard Worker    Class Attributes:
165*c2e18aaaSAndroid Build Coastguard Worker        _JDK_PATH: The path of JDK in android project.
166*c2e18aaaSAndroid Build Coastguard Worker        _IDE_JDK_TABLE_PATH: The path of JDK table which record JDK info in IDE.
167*c2e18aaaSAndroid Build Coastguard Worker        _IDE_FILE_TYPE_PATH: The path of filetypes.xml.
168*c2e18aaaSAndroid Build Coastguard Worker        _JDK_CONTENT: A string, the content of the JDK configuration.
169*c2e18aaaSAndroid Build Coastguard Worker        _DEFAULT_ANDROID_SDK_PATH: A string, the path of Android SDK.
170*c2e18aaaSAndroid Build Coastguard Worker        _CONFIG_DIR: A string of the config folder name.
171*c2e18aaaSAndroid Build Coastguard Worker        _SYMBOLIC_VERSIONS: A string list of the symbolic link paths of the
172*c2e18aaaSAndroid Build Coastguard Worker                            relevant IDE.
173*c2e18aaaSAndroid Build Coastguard Worker
174*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
175*c2e18aaaSAndroid Build Coastguard Worker        _installed_path: String for the IDE binary path.
176*c2e18aaaSAndroid Build Coastguard Worker        _config_reset: Boolean, True for reset configuration, else not reset.
177*c2e18aaaSAndroid Build Coastguard Worker        _bin_file_name: String for IDE executable file name.
178*c2e18aaaSAndroid Build Coastguard Worker        _bin_paths: A list of all possible IDE executable file absolute paths.
179*c2e18aaaSAndroid Build Coastguard Worker        _ide_name: String for IDE name.
180*c2e18aaaSAndroid Build Coastguard Worker        _bin_folders: A list of all possible IDE installed paths.
181*c2e18aaaSAndroid Build Coastguard Worker        config_folders: A list of all possible paths for the IntelliJ
182*c2e18aaaSAndroid Build Coastguard Worker                        configuration folder.
183*c2e18aaaSAndroid Build Coastguard Worker        project_abspath: The absolute path of the project.
184*c2e18aaaSAndroid Build Coastguard Worker
185*c2e18aaaSAndroid Build Coastguard Worker    For example:
186*c2e18aaaSAndroid Build Coastguard Worker        1. Check if IDE is installed.
187*c2e18aaaSAndroid Build Coastguard Worker        2. Launch IDE.
188*c2e18aaaSAndroid Build Coastguard Worker        3. Config IDE.
189*c2e18aaaSAndroid Build Coastguard Worker    """
190*c2e18aaaSAndroid Build Coastguard Worker
191*c2e18aaaSAndroid Build Coastguard Worker    _JDK_PATH = ''
192*c2e18aaaSAndroid Build Coastguard Worker    _IDE_JDK_TABLE_PATH = ''
193*c2e18aaaSAndroid Build Coastguard Worker    _IDE_FILE_TYPE_PATH = ''
194*c2e18aaaSAndroid Build Coastguard Worker    _JDK_CONTENT = ''
195*c2e18aaaSAndroid Build Coastguard Worker    _DEFAULT_ANDROID_SDK_PATH = ''
196*c2e18aaaSAndroid Build Coastguard Worker    _CONFIG_DIR = ''
197*c2e18aaaSAndroid Build Coastguard Worker    _SYMBOLIC_VERSIONS = []
198*c2e18aaaSAndroid Build Coastguard Worker
199*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
200*c2e18aaaSAndroid Build Coastguard Worker        self._installed_path = installed_path
201*c2e18aaaSAndroid Build Coastguard Worker        self._config_reset = config_reset
202*c2e18aaaSAndroid Build Coastguard Worker        self._ide_name = ''
203*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = ''
204*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = []
205*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = []
206*c2e18aaaSAndroid Build Coastguard Worker        self.config_folders = []
207*c2e18aaaSAndroid Build Coastguard Worker        self.project_abspath = ''
208*c2e18aaaSAndroid Build Coastguard Worker
209*c2e18aaaSAndroid Build Coastguard Worker    def is_ide_installed(self):
210*c2e18aaaSAndroid Build Coastguard Worker        """Checks if IDE is already installed.
211*c2e18aaaSAndroid Build Coastguard Worker
212*c2e18aaaSAndroid Build Coastguard Worker        Returns:
213*c2e18aaaSAndroid Build Coastguard Worker            True if IDE is installed already, otherwise False.
214*c2e18aaaSAndroid Build Coastguard Worker        """
215*c2e18aaaSAndroid Build Coastguard Worker        return bool(self._installed_path)
216*c2e18aaaSAndroid Build Coastguard Worker
217*c2e18aaaSAndroid Build Coastguard Worker    def launch_ide(self):
218*c2e18aaaSAndroid Build Coastguard Worker        """Launches IDE by opening the passed project file."""
219*c2e18aaaSAndroid Build Coastguard Worker        ide_common_util.launch_ide(self.project_abspath, self._get_ide_cmd(),
220*c2e18aaaSAndroid Build Coastguard Worker                                   self._ide_name)
221*c2e18aaaSAndroid Build Coastguard Worker
222*c2e18aaaSAndroid Build Coastguard Worker    def apply_optional_config(self):
223*c2e18aaaSAndroid Build Coastguard Worker        """Do IDEA global config action.
224*c2e18aaaSAndroid Build Coastguard Worker
225*c2e18aaaSAndroid Build Coastguard Worker        Run code style config, SDK config.
226*c2e18aaaSAndroid Build Coastguard Worker        """
227*c2e18aaaSAndroid Build Coastguard Worker        if not self._installed_path:
228*c2e18aaaSAndroid Build Coastguard Worker            return
229*c2e18aaaSAndroid Build Coastguard Worker        # Skip config action if there's no config folder exists.
230*c2e18aaaSAndroid Build Coastguard Worker        _path_list = self._get_config_root_paths()
231*c2e18aaaSAndroid Build Coastguard Worker        if not _path_list:
232*c2e18aaaSAndroid Build Coastguard Worker            return
233*c2e18aaaSAndroid Build Coastguard Worker        self.config_folders = _path_list.copy()
234*c2e18aaaSAndroid Build Coastguard Worker
235*c2e18aaaSAndroid Build Coastguard Worker        for _config_path in _path_list:
236*c2e18aaaSAndroid Build Coastguard Worker            jdk_file = os.path.join(_config_path, self._IDE_JDK_TABLE_PATH)
237*c2e18aaaSAndroid Build Coastguard Worker            jdk_xml = jdk_table.JDKTableXML(jdk_file, self._JDK_CONTENT,
238*c2e18aaaSAndroid Build Coastguard Worker                                            self._JDK_PATH,
239*c2e18aaaSAndroid Build Coastguard Worker                                            self._DEFAULT_ANDROID_SDK_PATH)
240*c2e18aaaSAndroid Build Coastguard Worker            if jdk_xml.config_jdk_table_xml():
241*c2e18aaaSAndroid Build Coastguard Worker                project_file_gen.gen_enable_debugger_module(
242*c2e18aaaSAndroid Build Coastguard Worker                    self.project_abspath, jdk_xml.android_sdk_version)
243*c2e18aaaSAndroid Build Coastguard Worker
244*c2e18aaaSAndroid Build Coastguard Worker            # Set the max file size in the idea.properties.
245*c2e18aaaSAndroid Build Coastguard Worker            intellij_config_dir = os.path.join(_config_path, self._CONFIG_DIR)
246*c2e18aaaSAndroid Build Coastguard Worker            config.IdeaProperties(intellij_config_dir).set_max_file_size()
247*c2e18aaaSAndroid Build Coastguard Worker
248*c2e18aaaSAndroid Build Coastguard Worker            self._add_test_mapping_file_type(_config_path)
249*c2e18aaaSAndroid Build Coastguard Worker
250*c2e18aaaSAndroid Build Coastguard Worker    def _add_test_mapping_file_type(self, _config_path):
251*c2e18aaaSAndroid Build Coastguard Worker        """Adds TEST_MAPPING file type.
252*c2e18aaaSAndroid Build Coastguard Worker
253*c2e18aaaSAndroid Build Coastguard Worker        IntelliJ can't recognize TEST_MAPPING files as the json file. It needs
254*c2e18aaaSAndroid Build Coastguard Worker        adding file type mapping in filetypes.xml to recognize TEST_MAPPING
255*c2e18aaaSAndroid Build Coastguard Worker        files.
256*c2e18aaaSAndroid Build Coastguard Worker
257*c2e18aaaSAndroid Build Coastguard Worker        Args:
258*c2e18aaaSAndroid Build Coastguard Worker            _config_path: the path of IDE config.
259*c2e18aaaSAndroid Build Coastguard Worker        """
260*c2e18aaaSAndroid Build Coastguard Worker        file_type_path = os.path.join(_config_path, self._IDE_FILE_TYPE_PATH)
261*c2e18aaaSAndroid Build Coastguard Worker        if not os.path.isfile(file_type_path):
262*c2e18aaaSAndroid Build Coastguard Worker            logging.warning('The file: filetypes.xml is not found.')
263*c2e18aaaSAndroid Build Coastguard Worker            return
264*c2e18aaaSAndroid Build Coastguard Worker
265*c2e18aaaSAndroid Build Coastguard Worker        file_type_xml = xml_util.parse_xml(file_type_path)
266*c2e18aaaSAndroid Build Coastguard Worker        if not file_type_xml:
267*c2e18aaaSAndroid Build Coastguard Worker            logging.warning('Can\'t parse filetypes.xml.')
268*c2e18aaaSAndroid Build Coastguard Worker            return
269*c2e18aaaSAndroid Build Coastguard Worker
270*c2e18aaaSAndroid Build Coastguard Worker        root = file_type_xml.getroot()
271*c2e18aaaSAndroid Build Coastguard Worker        add_pattern = True
272*c2e18aaaSAndroid Build Coastguard Worker        for mapping in root.findall(_XPATH_MAPPING):
273*c2e18aaaSAndroid Build Coastguard Worker            attrib = mapping.attrib
274*c2e18aaaSAndroid Build Coastguard Worker            if PATTERN_KEY in attrib and TYPE_KEY in attrib:
275*c2e18aaaSAndroid Build Coastguard Worker                if attrib[PATTERN_KEY] == TEST_MAPPING_NAME:
276*c2e18aaaSAndroid Build Coastguard Worker                    if attrib[TYPE_KEY] != _TEST_MAPPING_FILE_TYPE:
277*c2e18aaaSAndroid Build Coastguard Worker                        attrib[TYPE_KEY] = _TEST_MAPPING_FILE_TYPE
278*c2e18aaaSAndroid Build Coastguard Worker                        file_type_xml.write(file_type_path)
279*c2e18aaaSAndroid Build Coastguard Worker                    add_pattern = False
280*c2e18aaaSAndroid Build Coastguard Worker                    break
281*c2e18aaaSAndroid Build Coastguard Worker        if add_pattern:
282*c2e18aaaSAndroid Build Coastguard Worker            ext_attrib = root.find(_XPATH_EXTENSION_MAP)
283*c2e18aaaSAndroid Build Coastguard Worker            if ext_attrib is None:
284*c2e18aaaSAndroid Build Coastguard Worker                print(_TEST_MAPPING_FILE_TYPE_ADDING_WARN)
285*c2e18aaaSAndroid Build Coastguard Worker                return
286*c2e18aaaSAndroid Build Coastguard Worker            ext_attrib.append(ElementTree.fromstring(_TEST_MAPPING_TYPE))
287*c2e18aaaSAndroid Build Coastguard Worker            pretty_xml = common_util.to_pretty_xml(root)
288*c2e18aaaSAndroid Build Coastguard Worker            common_util.file_generate(file_type_path, pretty_xml)
289*c2e18aaaSAndroid Build Coastguard Worker
290*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
291*c2e18aaaSAndroid Build Coastguard Worker        """Get the config root paths from derived class.
292*c2e18aaaSAndroid Build Coastguard Worker
293*c2e18aaaSAndroid Build Coastguard Worker        Returns:
294*c2e18aaaSAndroid Build Coastguard Worker            A string list of IDE config paths, return multiple paths if more
295*c2e18aaaSAndroid Build Coastguard Worker            than one path are found, return an empty list when none is found.
296*c2e18aaaSAndroid Build Coastguard Worker        """
297*c2e18aaaSAndroid Build Coastguard Worker        raise NotImplementedError()
298*c2e18aaaSAndroid Build Coastguard Worker
299*c2e18aaaSAndroid Build Coastguard Worker    @property
300*c2e18aaaSAndroid Build Coastguard Worker    def default_installed_path(self):
301*c2e18aaaSAndroid Build Coastguard Worker        """Gets IDE default installed path."""
302*c2e18aaaSAndroid Build Coastguard Worker        return ' '.join(self._bin_folders)
303*c2e18aaaSAndroid Build Coastguard Worker
304*c2e18aaaSAndroid Build Coastguard Worker    @property
305*c2e18aaaSAndroid Build Coastguard Worker    def ide_name(self):
306*c2e18aaaSAndroid Build Coastguard Worker        """Gets IDE name."""
307*c2e18aaaSAndroid Build Coastguard Worker        return self._ide_name
308*c2e18aaaSAndroid Build Coastguard Worker
309*c2e18aaaSAndroid Build Coastguard Worker    def _get_ide_cmd(self):
310*c2e18aaaSAndroid Build Coastguard Worker        """Compose launch IDE command to run a new process and redirect output.
311*c2e18aaaSAndroid Build Coastguard Worker
312*c2e18aaaSAndroid Build Coastguard Worker        Returns:
313*c2e18aaaSAndroid Build Coastguard Worker            A string of launch IDE command.
314*c2e18aaaSAndroid Build Coastguard Worker        """
315*c2e18aaaSAndroid Build Coastguard Worker        return ide_common_util.get_run_ide_cmd(self._installed_path,
316*c2e18aaaSAndroid Build Coastguard Worker                                               self.project_abspath)
317*c2e18aaaSAndroid Build Coastguard Worker
318*c2e18aaaSAndroid Build Coastguard Worker    def _init_installed_path(self, installed_path):
319*c2e18aaaSAndroid Build Coastguard Worker        """Initialize IDE installed path.
320*c2e18aaaSAndroid Build Coastguard Worker
321*c2e18aaaSAndroid Build Coastguard Worker        Args:
322*c2e18aaaSAndroid Build Coastguard Worker            installed_path: the installed path to be checked.
323*c2e18aaaSAndroid Build Coastguard Worker        """
324*c2e18aaaSAndroid Build Coastguard Worker        if installed_path:
325*c2e18aaaSAndroid Build Coastguard Worker            path_list = ide_common_util.get_script_from_input_path(
326*c2e18aaaSAndroid Build Coastguard Worker                installed_path, self._bin_file_name)
327*c2e18aaaSAndroid Build Coastguard Worker            self._installed_path = path_list[0] if path_list else None
328*c2e18aaaSAndroid Build Coastguard Worker        else:
329*c2e18aaaSAndroid Build Coastguard Worker            self._installed_path = self._get_script_from_system()
330*c2e18aaaSAndroid Build Coastguard Worker        if not self._installed_path:
331*c2e18aaaSAndroid Build Coastguard Worker            logging.error('No %s installed.', self._ide_name)
332*c2e18aaaSAndroid Build Coastguard Worker            return
333*c2e18aaaSAndroid Build Coastguard Worker
334*c2e18aaaSAndroid Build Coastguard Worker        self._set_installed_path()
335*c2e18aaaSAndroid Build Coastguard Worker
336*c2e18aaaSAndroid Build Coastguard Worker    def _get_script_from_system(self):
337*c2e18aaaSAndroid Build Coastguard Worker        """Get one IDE installed path from internal path.
338*c2e18aaaSAndroid Build Coastguard Worker
339*c2e18aaaSAndroid Build Coastguard Worker        Returns:
340*c2e18aaaSAndroid Build Coastguard Worker            The sh full path, or None if not found.
341*c2e18aaaSAndroid Build Coastguard Worker        """
342*c2e18aaaSAndroid Build Coastguard Worker        sh_list = self._get_existent_scripts_in_system()
343*c2e18aaaSAndroid Build Coastguard Worker        return sh_list[0] if sh_list else None
344*c2e18aaaSAndroid Build Coastguard Worker
345*c2e18aaaSAndroid Build Coastguard Worker    def _get_possible_bin_paths(self):
346*c2e18aaaSAndroid Build Coastguard Worker        """Gets all possible IDE installed paths."""
347*c2e18aaaSAndroid Build Coastguard Worker        return [os.path.join(f, self._bin_file_name) for f in self._bin_folders]
348*c2e18aaaSAndroid Build Coastguard Worker
349*c2e18aaaSAndroid Build Coastguard Worker    def _get_ide_from_environment_paths(self):
350*c2e18aaaSAndroid Build Coastguard Worker        """Get IDE executable binary file from environment paths.
351*c2e18aaaSAndroid Build Coastguard Worker
352*c2e18aaaSAndroid Build Coastguard Worker        Returns:
353*c2e18aaaSAndroid Build Coastguard Worker            A string of IDE executable binary path if found, otherwise return
354*c2e18aaaSAndroid Build Coastguard Worker            None.
355*c2e18aaaSAndroid Build Coastguard Worker        """
356*c2e18aaaSAndroid Build Coastguard Worker        env_paths = os.environ['PATH'].split(':')
357*c2e18aaaSAndroid Build Coastguard Worker        for env_path in env_paths:
358*c2e18aaaSAndroid Build Coastguard Worker            path = ide_common_util.get_scripts_from_dir_path(
359*c2e18aaaSAndroid Build Coastguard Worker                env_path, self._bin_file_name)
360*c2e18aaaSAndroid Build Coastguard Worker            if path:
361*c2e18aaaSAndroid Build Coastguard Worker                return path
362*c2e18aaaSAndroid Build Coastguard Worker        return None
363*c2e18aaaSAndroid Build Coastguard Worker
364*c2e18aaaSAndroid Build Coastguard Worker    def _setup_ide(self):
365*c2e18aaaSAndroid Build Coastguard Worker        """The callback used to run the necessary setup work of the IDE.
366*c2e18aaaSAndroid Build Coastguard Worker
367*c2e18aaaSAndroid Build Coastguard Worker        When ide_util.config_ide is called to set up the JDK, SDK and some
368*c2e18aaaSAndroid Build Coastguard Worker        features, the main thread will callback the Idexxx._setup_ide
369*c2e18aaaSAndroid Build Coastguard Worker        to provide the chance for running the necessary setup of the specific
370*c2e18aaaSAndroid Build Coastguard Worker        IDE. Default is to do nothing.
371*c2e18aaaSAndroid Build Coastguard Worker        """
372*c2e18aaaSAndroid Build Coastguard Worker
373*c2e18aaaSAndroid Build Coastguard Worker    def _get_existent_scripts_in_system(self):
374*c2e18aaaSAndroid Build Coastguard Worker        """Gets the relevant IDE run script path from system.
375*c2e18aaaSAndroid Build Coastguard Worker
376*c2e18aaaSAndroid Build Coastguard Worker        First get correct IDE installed path from internal paths, if not found
377*c2e18aaaSAndroid Build Coastguard Worker        search it from environment paths.
378*c2e18aaaSAndroid Build Coastguard Worker
379*c2e18aaaSAndroid Build Coastguard Worker        Returns:
380*c2e18aaaSAndroid Build Coastguard Worker            The list of script full path, or None if not found.
381*c2e18aaaSAndroid Build Coastguard Worker        """
382*c2e18aaaSAndroid Build Coastguard Worker        return (ide_common_util.get_script_from_internal_path(self._bin_paths,
383*c2e18aaaSAndroid Build Coastguard Worker                                                              self._ide_name) or
384*c2e18aaaSAndroid Build Coastguard Worker                self._get_ide_from_environment_paths())
385*c2e18aaaSAndroid Build Coastguard Worker
386*c2e18aaaSAndroid Build Coastguard Worker    def _get_user_preference(self, versions):
387*c2e18aaaSAndroid Build Coastguard Worker        """Make sure the version is valid and update preference if needed.
388*c2e18aaaSAndroid Build Coastguard Worker
389*c2e18aaaSAndroid Build Coastguard Worker        Args:
390*c2e18aaaSAndroid Build Coastguard Worker            versions: A list of the IDE script path, contains the symbolic path.
391*c2e18aaaSAndroid Build Coastguard Worker
392*c2e18aaaSAndroid Build Coastguard Worker        Returns: An IDE script path, or None is not found.
393*c2e18aaaSAndroid Build Coastguard Worker        """
394*c2e18aaaSAndroid Build Coastguard Worker        if not versions:
395*c2e18aaaSAndroid Build Coastguard Worker            return None
396*c2e18aaaSAndroid Build Coastguard Worker        if len(versions) == 1:
397*c2e18aaaSAndroid Build Coastguard Worker            return versions[0]
398*c2e18aaaSAndroid Build Coastguard Worker        with config.AidegenConfig() as conf:
399*c2e18aaaSAndroid Build Coastguard Worker            if not self._config_reset and (conf.preferred_version(self.ide_name)
400*c2e18aaaSAndroid Build Coastguard Worker                                           in versions):
401*c2e18aaaSAndroid Build Coastguard Worker                return conf.preferred_version(self.ide_name)
402*c2e18aaaSAndroid Build Coastguard Worker            display_versions = self._merge_symbolic_version(versions)
403*c2e18aaaSAndroid Build Coastguard Worker            preferred = ide_common_util.ask_preference(display_versions,
404*c2e18aaaSAndroid Build Coastguard Worker                                                       self.ide_name)
405*c2e18aaaSAndroid Build Coastguard Worker            if preferred:
406*c2e18aaaSAndroid Build Coastguard Worker                conf.set_preferred_version(self._get_real_path(preferred),
407*c2e18aaaSAndroid Build Coastguard Worker                                           self.ide_name)
408*c2e18aaaSAndroid Build Coastguard Worker
409*c2e18aaaSAndroid Build Coastguard Worker            return conf.preferred_version(self.ide_name)
410*c2e18aaaSAndroid Build Coastguard Worker
411*c2e18aaaSAndroid Build Coastguard Worker    def _set_installed_path(self):
412*c2e18aaaSAndroid Build Coastguard Worker        """Write the user's input installed path into the config file.
413*c2e18aaaSAndroid Build Coastguard Worker
414*c2e18aaaSAndroid Build Coastguard Worker        If users input an existent IDE installed path, we should keep it in
415*c2e18aaaSAndroid Build Coastguard Worker        the configuration.
416*c2e18aaaSAndroid Build Coastguard Worker        """
417*c2e18aaaSAndroid Build Coastguard Worker        if self._installed_path:
418*c2e18aaaSAndroid Build Coastguard Worker            with config.AidegenConfig() as aconf:
419*c2e18aaaSAndroid Build Coastguard Worker                aconf.set_preferred_version(self._installed_path, self.ide_name)
420*c2e18aaaSAndroid Build Coastguard Worker
421*c2e18aaaSAndroid Build Coastguard Worker    def _merge_symbolic_version(self, versions):
422*c2e18aaaSAndroid Build Coastguard Worker        """Merge the duplicate version of symbolic links.
423*c2e18aaaSAndroid Build Coastguard Worker
424*c2e18aaaSAndroid Build Coastguard Worker        Stable and beta versions are a symbolic link to an existing version.
425*c2e18aaaSAndroid Build Coastguard Worker        This function assemble symbolic and real to make it more clear to read.
426*c2e18aaaSAndroid Build Coastguard Worker        Ex:
427*c2e18aaaSAndroid Build Coastguard Worker        ['/opt/intellij-ce-stable/bin/idea.sh',
428*c2e18aaaSAndroid Build Coastguard Worker        '/opt/intellij-ce-2019.1/bin/idea.sh'] to
429*c2e18aaaSAndroid Build Coastguard Worker        ['/opt/intellij-ce-stable/bin/idea.sh ->
430*c2e18aaaSAndroid Build Coastguard Worker        /opt/intellij-ce-2019.1/bin/idea.sh',
431*c2e18aaaSAndroid Build Coastguard Worker        '/opt/intellij-ce-2019.1/bin/idea.sh']
432*c2e18aaaSAndroid Build Coastguard Worker
433*c2e18aaaSAndroid Build Coastguard Worker        Args:
434*c2e18aaaSAndroid Build Coastguard Worker            versions: A list of all installed versions.
435*c2e18aaaSAndroid Build Coastguard Worker
436*c2e18aaaSAndroid Build Coastguard Worker        Returns:
437*c2e18aaaSAndroid Build Coastguard Worker            A list of versions to show for user to select. It may contain
438*c2e18aaaSAndroid Build Coastguard Worker            'symbolic_path/idea.sh -> original_path/idea.sh'.
439*c2e18aaaSAndroid Build Coastguard Worker        """
440*c2e18aaaSAndroid Build Coastguard Worker        display_versions = versions[:]
441*c2e18aaaSAndroid Build Coastguard Worker        for symbolic in self._SYMBOLIC_VERSIONS:
442*c2e18aaaSAndroid Build Coastguard Worker            if symbolic in display_versions and (os.path.isfile(symbolic)):
443*c2e18aaaSAndroid Build Coastguard Worker                real_path = os.path.realpath(symbolic)
444*c2e18aaaSAndroid Build Coastguard Worker                for index, path in enumerate(display_versions):
445*c2e18aaaSAndroid Build Coastguard Worker                    if path == symbolic:
446*c2e18aaaSAndroid Build Coastguard Worker                        display_versions[index] = ' -> '.join(
447*c2e18aaaSAndroid Build Coastguard Worker                            [display_versions[index], real_path])
448*c2e18aaaSAndroid Build Coastguard Worker                        break
449*c2e18aaaSAndroid Build Coastguard Worker        return display_versions
450*c2e18aaaSAndroid Build Coastguard Worker
451*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
452*c2e18aaaSAndroid Build Coastguard Worker    def _get_real_path(path):
453*c2e18aaaSAndroid Build Coastguard Worker        """ Get real path from merged path.
454*c2e18aaaSAndroid Build Coastguard Worker
455*c2e18aaaSAndroid Build Coastguard Worker        Turn the path string "/opt/intellij-ce-stable/bin/idea.sh -> /opt/
456*c2e18aaaSAndroid Build Coastguard Worker        intellij-ce-2019.2/bin/idea.sh" into
457*c2e18aaaSAndroid Build Coastguard Worker        "/opt/intellij-ce-stable/bin/idea.sh"
458*c2e18aaaSAndroid Build Coastguard Worker
459*c2e18aaaSAndroid Build Coastguard Worker        Args:
460*c2e18aaaSAndroid Build Coastguard Worker            path: A path string may be merged with symbolic path.
461*c2e18aaaSAndroid Build Coastguard Worker        Returns:
462*c2e18aaaSAndroid Build Coastguard Worker            The real IntelliJ installed path.
463*c2e18aaaSAndroid Build Coastguard Worker        """
464*c2e18aaaSAndroid Build Coastguard Worker        return path.split()[0]
465*c2e18aaaSAndroid Build Coastguard Worker
466*c2e18aaaSAndroid Build Coastguard Worker
467*c2e18aaaSAndroid Build Coastguard Workerclass IdeIntelliJ(IdeBase):
468*c2e18aaaSAndroid Build Coastguard Worker    """Provide basic IntelliJ ops, e.g., launch IDEA, and config IntelliJ.
469*c2e18aaaSAndroid Build Coastguard Worker
470*c2e18aaaSAndroid Build Coastguard Worker    For example:
471*c2e18aaaSAndroid Build Coastguard Worker        1. Check if IntelliJ is installed.
472*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an IntelliJ.
473*c2e18aaaSAndroid Build Coastguard Worker        3. Config IntelliJ.
474*c2e18aaaSAndroid Build Coastguard Worker    """
475*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
476*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
477*c2e18aaaSAndroid Build Coastguard Worker        self._ide_name = constant.IDE_INTELLIJ
478*c2e18aaaSAndroid Build Coastguard Worker        self._ls_ce_path = ''
479*c2e18aaaSAndroid Build Coastguard Worker        self._ls_ue_path = ''
480*c2e18aaaSAndroid Build Coastguard Worker
481*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
482*c2e18aaaSAndroid Build Coastguard Worker        """Get the config root paths from derived class.
483*c2e18aaaSAndroid Build Coastguard Worker
484*c2e18aaaSAndroid Build Coastguard Worker        Returns:
485*c2e18aaaSAndroid Build Coastguard Worker            A string list of IDE config paths, return multiple paths if more
486*c2e18aaaSAndroid Build Coastguard Worker            than one path are found, return an empty list when none is found.
487*c2e18aaaSAndroid Build Coastguard Worker        """
488*c2e18aaaSAndroid Build Coastguard Worker        raise NotImplementedError()
489*c2e18aaaSAndroid Build Coastguard Worker
490*c2e18aaaSAndroid Build Coastguard Worker    def _get_preferred_version(self):
491*c2e18aaaSAndroid Build Coastguard Worker        """Get the user's preferred IntelliJ version.
492*c2e18aaaSAndroid Build Coastguard Worker
493*c2e18aaaSAndroid Build Coastguard Worker        Locates the IntelliJ IDEA launch script path by following rule.
494*c2e18aaaSAndroid Build Coastguard Worker
495*c2e18aaaSAndroid Build Coastguard Worker        1. If config file recorded user's preference version, load it.
496*c2e18aaaSAndroid Build Coastguard Worker        2. If config file didn't record, search them form default path if there
497*c2e18aaaSAndroid Build Coastguard Worker           are more than one version, ask user and record it.
498*c2e18aaaSAndroid Build Coastguard Worker
499*c2e18aaaSAndroid Build Coastguard Worker        Returns:
500*c2e18aaaSAndroid Build Coastguard Worker            The sh full path, or None if no IntelliJ version is installed.
501*c2e18aaaSAndroid Build Coastguard Worker        """
502*c2e18aaaSAndroid Build Coastguard Worker        ce_paths = ide_common_util.get_intellij_version_path(self._ls_ce_path)
503*c2e18aaaSAndroid Build Coastguard Worker        ue_paths = ide_common_util.get_intellij_version_path(self._ls_ue_path)
504*c2e18aaaSAndroid Build Coastguard Worker        all_versions = self._get_all_versions(ce_paths, ue_paths)
505*c2e18aaaSAndroid Build Coastguard Worker        for version in list(all_versions):
506*c2e18aaaSAndroid Build Coastguard Worker            real_version = os.path.realpath(version)
507*c2e18aaaSAndroid Build Coastguard Worker            if (os.path.islink(version.split('/bin')[0]) and
508*c2e18aaaSAndroid Build Coastguard Worker                (real_version in all_versions)):
509*c2e18aaaSAndroid Build Coastguard Worker                all_versions.remove(real_version)
510*c2e18aaaSAndroid Build Coastguard Worker            if config.AidegenConfig.deprecated_intellij_version(real_version):
511*c2e18aaaSAndroid Build Coastguard Worker                all_versions.remove(version)
512*c2e18aaaSAndroid Build Coastguard Worker        return self._get_user_preference(all_versions)
513*c2e18aaaSAndroid Build Coastguard Worker
514*c2e18aaaSAndroid Build Coastguard Worker    def _setup_ide(self):
515*c2e18aaaSAndroid Build Coastguard Worker        """The callback used to run the necessary setup work for the IDE.
516*c2e18aaaSAndroid Build Coastguard Worker
517*c2e18aaaSAndroid Build Coastguard Worker        IntelliJ has a default flow to let the user import the configuration
518*c2e18aaaSAndroid Build Coastguard Worker        from the previous version, aidegen makes sure not to break the behavior
519*c2e18aaaSAndroid Build Coastguard Worker        by checking in this callback implementation.
520*c2e18aaaSAndroid Build Coastguard Worker        """
521*c2e18aaaSAndroid Build Coastguard Worker        run_script_path = os.path.realpath(self._installed_path)
522*c2e18aaaSAndroid Build Coastguard Worker        app_folder = self._get_application_path(run_script_path)
523*c2e18aaaSAndroid Build Coastguard Worker        if not app_folder:
524*c2e18aaaSAndroid Build Coastguard Worker            logging.warning('\nInvalid IDE installed path.')
525*c2e18aaaSAndroid Build Coastguard Worker            return
526*c2e18aaaSAndroid Build Coastguard Worker
527*c2e18aaaSAndroid Build Coastguard Worker        show_hint = False
528*c2e18aaaSAndroid Build Coastguard Worker        ide_version = self._get_ide_version(app_folder)
529*c2e18aaaSAndroid Build Coastguard Worker        folder_path = self._get_config_dir(ide_version, app_folder)
530*c2e18aaaSAndroid Build Coastguard Worker        import_process = None
531*c2e18aaaSAndroid Build Coastguard Worker        while not os.path.isdir(folder_path):
532*c2e18aaaSAndroid Build Coastguard Worker            # Guide the user to go through the IDE flow.
533*c2e18aaaSAndroid Build Coastguard Worker            if not show_hint:
534*c2e18aaaSAndroid Build Coastguard Worker                print('\n{} {}'.format(common_util.COLORED_INFO('INFO:'),
535*c2e18aaaSAndroid Build Coastguard Worker                                       _INFO_IMPORT_CONFIG.format(
536*c2e18aaaSAndroid Build Coastguard Worker                                           self.ide_name)))
537*c2e18aaaSAndroid Build Coastguard Worker                try:
538*c2e18aaaSAndroid Build Coastguard Worker                    import_process = subprocess.Popen(
539*c2e18aaaSAndroid Build Coastguard Worker                        ide_common_util.get_run_ide_cmd(run_script_path, '',
540*c2e18aaaSAndroid Build Coastguard Worker                                                        False), shell=True)
541*c2e18aaaSAndroid Build Coastguard Worker                except (subprocess.SubprocessError, ValueError):
542*c2e18aaaSAndroid Build Coastguard Worker                    logging.warning('\nSubprocess call gets the invalid input.')
543*c2e18aaaSAndroid Build Coastguard Worker                finally:
544*c2e18aaaSAndroid Build Coastguard Worker                    show_hint = True
545*c2e18aaaSAndroid Build Coastguard Worker        if import_process:
546*c2e18aaaSAndroid Build Coastguard Worker            try:
547*c2e18aaaSAndroid Build Coastguard Worker                import_process.wait(1)
548*c2e18aaaSAndroid Build Coastguard Worker            except subprocess.TimeoutExpired:
549*c2e18aaaSAndroid Build Coastguard Worker                import_process.terminate()
550*c2e18aaaSAndroid Build Coastguard Worker        return
551*c2e18aaaSAndroid Build Coastguard Worker
552*c2e18aaaSAndroid Build Coastguard Worker    def _get_script_from_system(self):
553*c2e18aaaSAndroid Build Coastguard Worker        """Get correct IntelliJ installed path from internal path.
554*c2e18aaaSAndroid Build Coastguard Worker
555*c2e18aaaSAndroid Build Coastguard Worker        Returns:
556*c2e18aaaSAndroid Build Coastguard Worker            The sh full path, or None if no IntelliJ version is installed.
557*c2e18aaaSAndroid Build Coastguard Worker        """
558*c2e18aaaSAndroid Build Coastguard Worker        found = self._get_preferred_version()
559*c2e18aaaSAndroid Build Coastguard Worker        if found:
560*c2e18aaaSAndroid Build Coastguard Worker            logging.debug('IDE internal installed path: %s.', found)
561*c2e18aaaSAndroid Build Coastguard Worker        return found
562*c2e18aaaSAndroid Build Coastguard Worker
563*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
564*c2e18aaaSAndroid Build Coastguard Worker    def _get_all_versions(cefiles, uefiles):
565*c2e18aaaSAndroid Build Coastguard Worker        """Get all versions of launch script files.
566*c2e18aaaSAndroid Build Coastguard Worker
567*c2e18aaaSAndroid Build Coastguard Worker        Args:
568*c2e18aaaSAndroid Build Coastguard Worker            cefiles: CE version launch script paths.
569*c2e18aaaSAndroid Build Coastguard Worker            uefiles: UE version launch script paths.
570*c2e18aaaSAndroid Build Coastguard Worker
571*c2e18aaaSAndroid Build Coastguard Worker        Returns:
572*c2e18aaaSAndroid Build Coastguard Worker            A list contains all versions of launch script files.
573*c2e18aaaSAndroid Build Coastguard Worker        """
574*c2e18aaaSAndroid Build Coastguard Worker        all_versions = []
575*c2e18aaaSAndroid Build Coastguard Worker        if cefiles:
576*c2e18aaaSAndroid Build Coastguard Worker            all_versions.extend(cefiles)
577*c2e18aaaSAndroid Build Coastguard Worker        if uefiles:
578*c2e18aaaSAndroid Build Coastguard Worker            all_versions.extend(uefiles)
579*c2e18aaaSAndroid Build Coastguard Worker        return all_versions
580*c2e18aaaSAndroid Build Coastguard Worker
581*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
582*c2e18aaaSAndroid Build Coastguard Worker    def _get_application_path(run_script_path):
583*c2e18aaaSAndroid Build Coastguard Worker        """Get the relevant configuration folder based on the run script path.
584*c2e18aaaSAndroid Build Coastguard Worker
585*c2e18aaaSAndroid Build Coastguard Worker        Args:
586*c2e18aaaSAndroid Build Coastguard Worker            run_script_path: The string of the run script path for the IntelliJ.
587*c2e18aaaSAndroid Build Coastguard Worker
588*c2e18aaaSAndroid Build Coastguard Worker        Returns:
589*c2e18aaaSAndroid Build Coastguard Worker            The string of the IntelliJ application folder name or None if the
590*c2e18aaaSAndroid Build Coastguard Worker            run_script_path is invalid. The returned folder format is as
591*c2e18aaaSAndroid Build Coastguard Worker            follows,
592*c2e18aaaSAndroid Build Coastguard Worker                1. .IdeaIC2019.3
593*c2e18aaaSAndroid Build Coastguard Worker                2. .IntelliJIdea2019.3
594*c2e18aaaSAndroid Build Coastguard Worker                3. IntelliJIdea2020.1
595*c2e18aaaSAndroid Build Coastguard Worker        """
596*c2e18aaaSAndroid Build Coastguard Worker        if not run_script_path or not os.path.isfile(run_script_path):
597*c2e18aaaSAndroid Build Coastguard Worker            return None
598*c2e18aaaSAndroid Build Coastguard Worker        index = str.find(run_script_path, 'intellij-')
599*c2e18aaaSAndroid Build Coastguard Worker        target_path = None if index == -1 else run_script_path[index:]
600*c2e18aaaSAndroid Build Coastguard Worker        if not target_path or '-' not in run_script_path:
601*c2e18aaaSAndroid Build Coastguard Worker            return None
602*c2e18aaaSAndroid Build Coastguard Worker        return IdeIntelliJ._get_config_folder_name(target_path)
603*c2e18aaaSAndroid Build Coastguard Worker
604*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
605*c2e18aaaSAndroid Build Coastguard Worker    def _get_ide_version(config_folder_name):
606*c2e18aaaSAndroid Build Coastguard Worker        """Gets IntelliJ version from the input app folder name.
607*c2e18aaaSAndroid Build Coastguard Worker
608*c2e18aaaSAndroid Build Coastguard Worker        Args:
609*c2e18aaaSAndroid Build Coastguard Worker            config_folder_name: A string of the app folder name.
610*c2e18aaaSAndroid Build Coastguard Worker
611*c2e18aaaSAndroid Build Coastguard Worker        Returns:
612*c2e18aaaSAndroid Build Coastguard Worker            A string of the IntelliJ version.
613*c2e18aaaSAndroid Build Coastguard Worker        """
614*c2e18aaaSAndroid Build Coastguard Worker        versions = re.findall(r'\d+', config_folder_name)
615*c2e18aaaSAndroid Build Coastguard Worker        if not versions:
616*c2e18aaaSAndroid Build Coastguard Worker            logging.warning('\nInvalid IntelliJ config folder name: %s.',
617*c2e18aaaSAndroid Build Coastguard Worker                            config_folder_name)
618*c2e18aaaSAndroid Build Coastguard Worker            return None
619*c2e18aaaSAndroid Build Coastguard Worker        return '.'.join(versions)
620*c2e18aaaSAndroid Build Coastguard Worker
621*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
622*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_folder_name(script_folder_name):
623*c2e18aaaSAndroid Build Coastguard Worker        """Gets IntelliJ config folder name from the IDE version.
624*c2e18aaaSAndroid Build Coastguard Worker
625*c2e18aaaSAndroid Build Coastguard Worker        The config folder name has been changed since 2020.1.
626*c2e18aaaSAndroid Build Coastguard Worker
627*c2e18aaaSAndroid Build Coastguard Worker        Args:
628*c2e18aaaSAndroid Build Coastguard Worker            script_folder_name: A string of the script folder name of IntelliJ.
629*c2e18aaaSAndroid Build Coastguard Worker
630*c2e18aaaSAndroid Build Coastguard Worker        Returns:
631*c2e18aaaSAndroid Build Coastguard Worker            A string of the IntelliJ config folder name.
632*c2e18aaaSAndroid Build Coastguard Worker        """
633*c2e18aaaSAndroid Build Coastguard Worker        path_data = script_folder_name.split('-')
634*c2e18aaaSAndroid Build Coastguard Worker        if not path_data or len(path_data) < 3:
635*c2e18aaaSAndroid Build Coastguard Worker            return None
636*c2e18aaaSAndroid Build Coastguard Worker        ide_version = path_data[2].split(os.sep)[0]
637*c2e18aaaSAndroid Build Coastguard Worker        numbers = ide_version.split('.')
638*c2e18aaaSAndroid Build Coastguard Worker        if len(numbers) > 2:
639*c2e18aaaSAndroid Build Coastguard Worker            ide_version = '.'.join([numbers[0], numbers[1]])
640*c2e18aaaSAndroid Build Coastguard Worker        try:
641*c2e18aaaSAndroid Build Coastguard Worker            version = float(ide_version)
642*c2e18aaaSAndroid Build Coastguard Worker        except ValueError:
643*c2e18aaaSAndroid Build Coastguard Worker            return None
644*c2e18aaaSAndroid Build Coastguard Worker        pre_folder = '.IdeaIC'
645*c2e18aaaSAndroid Build Coastguard Worker        if version < _SPECIFIC_INTELLIJ_VERSION:
646*c2e18aaaSAndroid Build Coastguard Worker            if path_data[1] == 'ue':
647*c2e18aaaSAndroid Build Coastguard Worker                pre_folder = '.IntelliJIdea'
648*c2e18aaaSAndroid Build Coastguard Worker        else:
649*c2e18aaaSAndroid Build Coastguard Worker            if path_data[1] == 'ce':
650*c2e18aaaSAndroid Build Coastguard Worker                pre_folder = 'IdeaIC'
651*c2e18aaaSAndroid Build Coastguard Worker            elif path_data[1] == 'ue':
652*c2e18aaaSAndroid Build Coastguard Worker                pre_folder = 'IntelliJIdea'
653*c2e18aaaSAndroid Build Coastguard Worker        return ''.join([pre_folder, ide_version])
654*c2e18aaaSAndroid Build Coastguard Worker
655*c2e18aaaSAndroid Build Coastguard Worker    @staticmethod
656*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_dir(ide_version, config_folder_name):
657*c2e18aaaSAndroid Build Coastguard Worker        """Gets IntelliJ config directory by the config folder name.
658*c2e18aaaSAndroid Build Coastguard Worker
659*c2e18aaaSAndroid Build Coastguard Worker        The IntelliJ config directory is changed from version 2020.1. Get the
660*c2e18aaaSAndroid Build Coastguard Worker        version from app folder name and determine the config directory.
661*c2e18aaaSAndroid Build Coastguard Worker        URL: https://intellij-support.jetbrains.com/hc/en-us/articles/206544519
662*c2e18aaaSAndroid Build Coastguard Worker
663*c2e18aaaSAndroid Build Coastguard Worker        Args:
664*c2e18aaaSAndroid Build Coastguard Worker            ide_version: A string of the IntelliJ's version.
665*c2e18aaaSAndroid Build Coastguard Worker            config_folder_name: A string of the IntelliJ's config folder name.
666*c2e18aaaSAndroid Build Coastguard Worker
667*c2e18aaaSAndroid Build Coastguard Worker        Returns:
668*c2e18aaaSAndroid Build Coastguard Worker            A string of the IntelliJ config directory.
669*c2e18aaaSAndroid Build Coastguard Worker        """
670*c2e18aaaSAndroid Build Coastguard Worker        try:
671*c2e18aaaSAndroid Build Coastguard Worker            version = float(ide_version)
672*c2e18aaaSAndroid Build Coastguard Worker        except ValueError:
673*c2e18aaaSAndroid Build Coastguard Worker            return None
674*c2e18aaaSAndroid Build Coastguard Worker        if version < _SPECIFIC_INTELLIJ_VERSION:
675*c2e18aaaSAndroid Build Coastguard Worker            return os.path.join(
676*c2e18aaaSAndroid Build Coastguard Worker                os.getenv('HOME'), config_folder_name)
677*c2e18aaaSAndroid Build Coastguard Worker        return os.path.join(
678*c2e18aaaSAndroid Build Coastguard Worker            os.getenv('HOME'), '.config', 'JetBrains', config_folder_name)
679*c2e18aaaSAndroid Build Coastguard Worker
680*c2e18aaaSAndroid Build Coastguard Worker
681*c2e18aaaSAndroid Build Coastguard Workerclass IdeLinuxIntelliJ(IdeIntelliJ):
682*c2e18aaaSAndroid Build Coastguard Worker    """Provide the IDEA behavior implementation for OS Linux.
683*c2e18aaaSAndroid Build Coastguard Worker
684*c2e18aaaSAndroid Build Coastguard Worker    Class Attributes:
685*c2e18aaaSAndroid Build Coastguard Worker        _INTELLIJ_RE: Regular expression of IntelliJ installed name in GLinux.
686*c2e18aaaSAndroid Build Coastguard Worker
687*c2e18aaaSAndroid Build Coastguard Worker    For example:
688*c2e18aaaSAndroid Build Coastguard Worker        1. Check if IntelliJ is installed.
689*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an IntelliJ.
690*c2e18aaaSAndroid Build Coastguard Worker        3. Config IntelliJ.
691*c2e18aaaSAndroid Build Coastguard Worker    """
692*c2e18aaaSAndroid Build Coastguard Worker
693*c2e18aaaSAndroid Build Coastguard Worker    _JDK_PATH = LINUX_JDK_PATH
694*c2e18aaaSAndroid Build Coastguard Worker    # TODO(b/127899277): Preserve a config for jdk version option case.
695*c2e18aaaSAndroid Build Coastguard Worker    _CONFIG_DIR = CONFIG_DIR
696*c2e18aaaSAndroid Build Coastguard Worker    _IDE_JDK_TABLE_PATH = LINUX_JDK_TABLE_PATH
697*c2e18aaaSAndroid Build Coastguard Worker    _IDE_FILE_TYPE_PATH = LINUX_FILE_TYPE_PATH
698*c2e18aaaSAndroid Build Coastguard Worker    _JDK_CONTENT = templates.LINUX_JDK_XML
699*c2e18aaaSAndroid Build Coastguard Worker    _DEFAULT_ANDROID_SDK_PATH = LINUX_ANDROID_SDK_PATH
700*c2e18aaaSAndroid Build Coastguard Worker    _SYMBOLIC_VERSIONS = ['/opt/intellij-ce-stable/bin/idea.sh',
701*c2e18aaaSAndroid Build Coastguard Worker                          '/opt/intellij-ue-stable/bin/idea.sh',
702*c2e18aaaSAndroid Build Coastguard Worker                          '/opt/intellij-ce-beta/bin/idea.sh',
703*c2e18aaaSAndroid Build Coastguard Worker                          '/opt/intellij-ue-beta/bin/idea.sh']
704*c2e18aaaSAndroid Build Coastguard Worker    _INTELLIJ_RE = re.compile(r'intellij-(ce|ue)-')
705*c2e18aaaSAndroid Build Coastguard Worker
706*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
707*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
708*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'idea.sh'
709*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/opt/intellij-*/bin']
710*c2e18aaaSAndroid Build Coastguard Worker        self._ls_ce_path = os.path.join('/opt/intellij-ce-*/bin',
711*c2e18aaaSAndroid Build Coastguard Worker                                        self._bin_file_name)
712*c2e18aaaSAndroid Build Coastguard Worker        self._ls_ue_path = os.path.join('/opt/intellij-ue-*/bin',
713*c2e18aaaSAndroid Build Coastguard Worker                                        self._bin_file_name)
714*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
715*c2e18aaaSAndroid Build Coastguard Worker
716*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
717*c2e18aaaSAndroid Build Coastguard Worker        """To collect the global config folder paths of IDEA as a string list.
718*c2e18aaaSAndroid Build Coastguard Worker
719*c2e18aaaSAndroid Build Coastguard Worker        The config folder of IntelliJ IDEA is under the user's home directory,
720*c2e18aaaSAndroid Build Coastguard Worker        .IdeaIC20xx.x and .IntelliJIdea20xx.x are folder names for different
721*c2e18aaaSAndroid Build Coastguard Worker        versions.
722*c2e18aaaSAndroid Build Coastguard Worker
723*c2e18aaaSAndroid Build Coastguard Worker        Returns:
724*c2e18aaaSAndroid Build Coastguard Worker            A string list for IDE config root paths, and return an empty list
725*c2e18aaaSAndroid Build Coastguard Worker            when none is found.
726*c2e18aaaSAndroid Build Coastguard Worker        """
727*c2e18aaaSAndroid Build Coastguard Worker        if not self._installed_path:
728*c2e18aaaSAndroid Build Coastguard Worker            return None
729*c2e18aaaSAndroid Build Coastguard Worker
730*c2e18aaaSAndroid Build Coastguard Worker        _config_folders = []
731*c2e18aaaSAndroid Build Coastguard Worker        _config_folder = ''
732*c2e18aaaSAndroid Build Coastguard Worker        if IdeLinuxIntelliJ._INTELLIJ_RE.search(self._installed_path):
733*c2e18aaaSAndroid Build Coastguard Worker            _path_data = os.path.realpath(self._installed_path)
734*c2e18aaaSAndroid Build Coastguard Worker            _config_folder = self._get_application_path(_path_data)
735*c2e18aaaSAndroid Build Coastguard Worker            if not _config_folder:
736*c2e18aaaSAndroid Build Coastguard Worker                return None
737*c2e18aaaSAndroid Build Coastguard Worker            ide_version = self._get_ide_version(_config_folder)
738*c2e18aaaSAndroid Build Coastguard Worker            if not ide_version:
739*c2e18aaaSAndroid Build Coastguard Worker                return None
740*c2e18aaaSAndroid Build Coastguard Worker            try:
741*c2e18aaaSAndroid Build Coastguard Worker                version = float(ide_version)
742*c2e18aaaSAndroid Build Coastguard Worker            except ValueError:
743*c2e18aaaSAndroid Build Coastguard Worker                return None
744*c2e18aaaSAndroid Build Coastguard Worker            folder_path = self._get_config_dir(ide_version, _config_folder)
745*c2e18aaaSAndroid Build Coastguard Worker            if version >= _SPECIFIC_INTELLIJ_VERSION:
746*c2e18aaaSAndroid Build Coastguard Worker                self._IDE_JDK_TABLE_PATH = ALTERNATIVE_JDK_TABLE_PATH
747*c2e18aaaSAndroid Build Coastguard Worker                self._IDE_FILE_TYPE_PATH = ALTERNATIVE_FILE_TYPE_XML_PATH
748*c2e18aaaSAndroid Build Coastguard Worker
749*c2e18aaaSAndroid Build Coastguard Worker            if not os.path.isdir(folder_path):
750*c2e18aaaSAndroid Build Coastguard Worker                logging.debug("\nThe config folder: %s doesn't exist",
751*c2e18aaaSAndroid Build Coastguard Worker                              _config_folder)
752*c2e18aaaSAndroid Build Coastguard Worker                self._setup_ide()
753*c2e18aaaSAndroid Build Coastguard Worker
754*c2e18aaaSAndroid Build Coastguard Worker            _config_folders.append(folder_path)
755*c2e18aaaSAndroid Build Coastguard Worker        else:
756*c2e18aaaSAndroid Build Coastguard Worker            # TODO(b/123459239): For the case that the user provides the IDEA
757*c2e18aaaSAndroid Build Coastguard Worker            # binary path, we now collect all possible IDEA config root paths.
758*c2e18aaaSAndroid Build Coastguard Worker            _config_folders = glob.glob(
759*c2e18aaaSAndroid Build Coastguard Worker                os.path.join(os.getenv('HOME'), '.IdeaI?20*'))
760*c2e18aaaSAndroid Build Coastguard Worker            _config_folders.extend(
761*c2e18aaaSAndroid Build Coastguard Worker                glob.glob(os.path.join(os.getenv('HOME'), '.IntelliJIdea20*')))
762*c2e18aaaSAndroid Build Coastguard Worker            _config_folders.extend(
763*c2e18aaaSAndroid Build Coastguard Worker                glob.glob(os.path.join(os.getenv('HOME'), '.config',
764*c2e18aaaSAndroid Build Coastguard Worker                                       'IntelliJIdea202*')))
765*c2e18aaaSAndroid Build Coastguard Worker            logging.debug('The config path list: %s.', _config_folders)
766*c2e18aaaSAndroid Build Coastguard Worker
767*c2e18aaaSAndroid Build Coastguard Worker        return _config_folders
768*c2e18aaaSAndroid Build Coastguard Worker
769*c2e18aaaSAndroid Build Coastguard Worker
770*c2e18aaaSAndroid Build Coastguard Workerclass IdeMacIntelliJ(IdeIntelliJ):
771*c2e18aaaSAndroid Build Coastguard Worker    """Provide the IDEA behavior implementation for OS Mac.
772*c2e18aaaSAndroid Build Coastguard Worker
773*c2e18aaaSAndroid Build Coastguard Worker    For example:
774*c2e18aaaSAndroid Build Coastguard Worker        1. Check if IntelliJ is installed.
775*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an IntelliJ.
776*c2e18aaaSAndroid Build Coastguard Worker        3. Config IntelliJ.
777*c2e18aaaSAndroid Build Coastguard Worker    """
778*c2e18aaaSAndroid Build Coastguard Worker
779*c2e18aaaSAndroid Build Coastguard Worker    _JDK_PATH = MAC_JDK_PATH
780*c2e18aaaSAndroid Build Coastguard Worker    _IDE_JDK_TABLE_PATH = ALTERNATIVE_JDK_TABLE_PATH
781*c2e18aaaSAndroid Build Coastguard Worker    _IDE_FILE_TYPE_PATH = ALTERNATIVE_FILE_TYPE_XML_PATH
782*c2e18aaaSAndroid Build Coastguard Worker    _JDK_CONTENT = templates.MAC_JDK_XML
783*c2e18aaaSAndroid Build Coastguard Worker    _DEFAULT_ANDROID_SDK_PATH = MAC_ANDROID_SDK_PATH
784*c2e18aaaSAndroid Build Coastguard Worker
785*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
786*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
787*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'idea'
788*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/Applications/IntelliJ IDEA.app/Contents/MacOS']
789*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
790*c2e18aaaSAndroid Build Coastguard Worker        self._ls_ce_path = os.path.join(
791*c2e18aaaSAndroid Build Coastguard Worker            '/Applications/IntelliJ IDEA CE.app/Contents/MacOS',
792*c2e18aaaSAndroid Build Coastguard Worker            self._bin_file_name)
793*c2e18aaaSAndroid Build Coastguard Worker        self._ls_ue_path = os.path.join(
794*c2e18aaaSAndroid Build Coastguard Worker            '/Applications/IntelliJ IDEA.app/Contents/MacOS',
795*c2e18aaaSAndroid Build Coastguard Worker            self._bin_file_name)
796*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
797*c2e18aaaSAndroid Build Coastguard Worker
798*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
799*c2e18aaaSAndroid Build Coastguard Worker        """To collect the global config folder paths of IDEA as a string list.
800*c2e18aaaSAndroid Build Coastguard Worker
801*c2e18aaaSAndroid Build Coastguard Worker        Returns:
802*c2e18aaaSAndroid Build Coastguard Worker            A string list for IDE config root paths, and return an empty list
803*c2e18aaaSAndroid Build Coastguard Worker            when none is found.
804*c2e18aaaSAndroid Build Coastguard Worker        """
805*c2e18aaaSAndroid Build Coastguard Worker        if not self._installed_path:
806*c2e18aaaSAndroid Build Coastguard Worker            return None
807*c2e18aaaSAndroid Build Coastguard Worker
808*c2e18aaaSAndroid Build Coastguard Worker        _config_folders = []
809*c2e18aaaSAndroid Build Coastguard Worker        if 'IntelliJ' in self._installed_path:
810*c2e18aaaSAndroid Build Coastguard Worker            _config_folders = glob.glob(
811*c2e18aaaSAndroid Build Coastguard Worker                os.path.join(
812*c2e18aaaSAndroid Build Coastguard Worker                    os.getenv('HOME'), 'Library/Preferences/IdeaI?20*'))
813*c2e18aaaSAndroid Build Coastguard Worker            _config_folders.extend(
814*c2e18aaaSAndroid Build Coastguard Worker                glob.glob(
815*c2e18aaaSAndroid Build Coastguard Worker                    os.path.join(
816*c2e18aaaSAndroid Build Coastguard Worker                        os.getenv('HOME'),
817*c2e18aaaSAndroid Build Coastguard Worker                        'Library/Preferences/IntelliJIdea20*')))
818*c2e18aaaSAndroid Build Coastguard Worker        return _config_folders
819*c2e18aaaSAndroid Build Coastguard Worker
820*c2e18aaaSAndroid Build Coastguard Worker
821*c2e18aaaSAndroid Build Coastguard Workerclass IdeStudio(IdeBase):
822*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Android Studio launching utilities.
823*c2e18aaaSAndroid Build Coastguard Worker
824*c2e18aaaSAndroid Build Coastguard Worker    For example:
825*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Android Studio is installed.
826*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Android Studio.
827*c2e18aaaSAndroid Build Coastguard Worker        3. Config Android Studio.
828*c2e18aaaSAndroid Build Coastguard Worker    """
829*c2e18aaaSAndroid Build Coastguard Worker
830*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
831*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
832*c2e18aaaSAndroid Build Coastguard Worker        self._ide_name = constant.IDE_ANDROID_STUDIO
833*c2e18aaaSAndroid Build Coastguard Worker
834*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
835*c2e18aaaSAndroid Build Coastguard Worker        """Get the config root paths from derived class.
836*c2e18aaaSAndroid Build Coastguard Worker
837*c2e18aaaSAndroid Build Coastguard Worker        Returns:
838*c2e18aaaSAndroid Build Coastguard Worker            A string list of IDE config paths, return multiple paths if more
839*c2e18aaaSAndroid Build Coastguard Worker            than one path are found, return an empty list when none is found.
840*c2e18aaaSAndroid Build Coastguard Worker        """
841*c2e18aaaSAndroid Build Coastguard Worker        raise NotImplementedError()
842*c2e18aaaSAndroid Build Coastguard Worker
843*c2e18aaaSAndroid Build Coastguard Worker    def _get_script_from_system(self):
844*c2e18aaaSAndroid Build Coastguard Worker        """Get correct Studio installed path from internal path.
845*c2e18aaaSAndroid Build Coastguard Worker
846*c2e18aaaSAndroid Build Coastguard Worker        Returns:
847*c2e18aaaSAndroid Build Coastguard Worker            The sh full path, or None if no Studio version is installed.
848*c2e18aaaSAndroid Build Coastguard Worker        """
849*c2e18aaaSAndroid Build Coastguard Worker        found = self._get_preferred_version()
850*c2e18aaaSAndroid Build Coastguard Worker        if found:
851*c2e18aaaSAndroid Build Coastguard Worker            logging.debug('IDE internal installed path: %s.', found)
852*c2e18aaaSAndroid Build Coastguard Worker        return found
853*c2e18aaaSAndroid Build Coastguard Worker
854*c2e18aaaSAndroid Build Coastguard Worker    def _get_preferred_version(self):
855*c2e18aaaSAndroid Build Coastguard Worker        """Get the user's preferred Studio version.
856*c2e18aaaSAndroid Build Coastguard Worker
857*c2e18aaaSAndroid Build Coastguard Worker        Locates the Studio launch script path by following rule.
858*c2e18aaaSAndroid Build Coastguard Worker
859*c2e18aaaSAndroid Build Coastguard Worker        1. If config file recorded user's preference version, load it.
860*c2e18aaaSAndroid Build Coastguard Worker        2. If config file didn't record, search them form default path if there
861*c2e18aaaSAndroid Build Coastguard Worker           are more than one version, ask user and record it.
862*c2e18aaaSAndroid Build Coastguard Worker
863*c2e18aaaSAndroid Build Coastguard Worker        Returns:
864*c2e18aaaSAndroid Build Coastguard Worker            The sh full path, or None if no Studio version is installed.
865*c2e18aaaSAndroid Build Coastguard Worker        """
866*c2e18aaaSAndroid Build Coastguard Worker        all_versions = self._get_existent_scripts_in_system()
867*c2e18aaaSAndroid Build Coastguard Worker        if not all_versions:
868*c2e18aaaSAndroid Build Coastguard Worker            return None
869*c2e18aaaSAndroid Build Coastguard Worker        for version in list(all_versions):
870*c2e18aaaSAndroid Build Coastguard Worker            real_version = os.path.realpath(version)
871*c2e18aaaSAndroid Build Coastguard Worker            if (os.path.islink(version.split('/bin')[0]) and
872*c2e18aaaSAndroid Build Coastguard Worker                (real_version in all_versions)):
873*c2e18aaaSAndroid Build Coastguard Worker                all_versions.remove(real_version)
874*c2e18aaaSAndroid Build Coastguard Worker            if config.AidegenConfig.deprecated_studio_version(real_version):
875*c2e18aaaSAndroid Build Coastguard Worker                all_versions.remove(version)
876*c2e18aaaSAndroid Build Coastguard Worker        return self._get_user_preference(all_versions)
877*c2e18aaaSAndroid Build Coastguard Worker
878*c2e18aaaSAndroid Build Coastguard Worker    def apply_optional_config(self):
879*c2e18aaaSAndroid Build Coastguard Worker        """Do the configuration of Android Studio.
880*c2e18aaaSAndroid Build Coastguard Worker
881*c2e18aaaSAndroid Build Coastguard Worker        Configures code style and SDK for Java project and do nothing for
882*c2e18aaaSAndroid Build Coastguard Worker        others.
883*c2e18aaaSAndroid Build Coastguard Worker        """
884*c2e18aaaSAndroid Build Coastguard Worker        if not self.project_abspath:
885*c2e18aaaSAndroid Build Coastguard Worker            return
886*c2e18aaaSAndroid Build Coastguard Worker        # TODO(b/150662865): The following workaround should be replaced.
887*c2e18aaaSAndroid Build Coastguard Worker        # Since the path of the artifact for Java is the .idea directory but
888*c2e18aaaSAndroid Build Coastguard Worker        # native is a CMakeLists.txt file using this to workaround first.
889*c2e18aaaSAndroid Build Coastguard Worker        if os.path.isfile(self.project_abspath):
890*c2e18aaaSAndroid Build Coastguard Worker            return
891*c2e18aaaSAndroid Build Coastguard Worker        if os.path.isdir(self.project_abspath):
892*c2e18aaaSAndroid Build Coastguard Worker            IdeBase.apply_optional_config(self)
893*c2e18aaaSAndroid Build Coastguard Worker
894*c2e18aaaSAndroid Build Coastguard Worker
895*c2e18aaaSAndroid Build Coastguard Workerclass IdeLinuxStudio(IdeStudio):
896*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Android Studio launching utilities for OS Linux.
897*c2e18aaaSAndroid Build Coastguard Worker
898*c2e18aaaSAndroid Build Coastguard Worker    For example:
899*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Android Studio is installed.
900*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Android Studio.
901*c2e18aaaSAndroid Build Coastguard Worker        3. Config Android Studio.
902*c2e18aaaSAndroid Build Coastguard Worker    """
903*c2e18aaaSAndroid Build Coastguard Worker
904*c2e18aaaSAndroid Build Coastguard Worker    _JDK_PATH = LINUX_JDK_PATH
905*c2e18aaaSAndroid Build Coastguard Worker    _CONFIG_DIR = CONFIG_DIR
906*c2e18aaaSAndroid Build Coastguard Worker    _IDE_JDK_TABLE_PATH = LINUX_JDK_TABLE_PATH
907*c2e18aaaSAndroid Build Coastguard Worker    _JDK_CONTENT = templates.LINUX_JDK_XML
908*c2e18aaaSAndroid Build Coastguard Worker    _DEFAULT_ANDROID_SDK_PATH = LINUX_ANDROID_SDK_PATH
909*c2e18aaaSAndroid Build Coastguard Worker    _SYMBOLIC_VERSIONS = [
910*c2e18aaaSAndroid Build Coastguard Worker        '/opt/android-studio-with-blaze-stable/bin/studio.sh',
911*c2e18aaaSAndroid Build Coastguard Worker        '/opt/android-studio-stable/bin/studio.sh',
912*c2e18aaaSAndroid Build Coastguard Worker        '/opt/android-studio-with-blaze-beta/bin/studio.sh',
913*c2e18aaaSAndroid Build Coastguard Worker        '/opt/android-studio-beta/bin/studio.sh']
914*c2e18aaaSAndroid Build Coastguard Worker
915*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
916*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
917*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'studio.sh'
918*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/opt/android-studio*/bin']
919*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
920*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
921*c2e18aaaSAndroid Build Coastguard Worker
922*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
923*c2e18aaaSAndroid Build Coastguard Worker        """Collect the global config folder paths as a string list.
924*c2e18aaaSAndroid Build Coastguard Worker
925*c2e18aaaSAndroid Build Coastguard Worker        Returns:
926*c2e18aaaSAndroid Build Coastguard Worker            A string list for IDE config root paths, and return an empty list
927*c2e18aaaSAndroid Build Coastguard Worker            when none is found.
928*c2e18aaaSAndroid Build Coastguard Worker        """
929*c2e18aaaSAndroid Build Coastguard Worker        return glob.glob(os.path.join(os.getenv('HOME'), '.AndroidStudio*'))
930*c2e18aaaSAndroid Build Coastguard Worker
931*c2e18aaaSAndroid Build Coastguard Worker
932*c2e18aaaSAndroid Build Coastguard Workerclass IdeMacStudio(IdeStudio):
933*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Android Studio launching utilities for OS Mac.
934*c2e18aaaSAndroid Build Coastguard Worker
935*c2e18aaaSAndroid Build Coastguard Worker    For example:
936*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Android Studio is installed.
937*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Android Studio.
938*c2e18aaaSAndroid Build Coastguard Worker        3. Config Android Studio.
939*c2e18aaaSAndroid Build Coastguard Worker    """
940*c2e18aaaSAndroid Build Coastguard Worker
941*c2e18aaaSAndroid Build Coastguard Worker    _JDK_PATH = MAC_JDK_PATH
942*c2e18aaaSAndroid Build Coastguard Worker    _IDE_JDK_TABLE_PATH = ALTERNATIVE_JDK_TABLE_PATH
943*c2e18aaaSAndroid Build Coastguard Worker    _JDK_CONTENT = templates.MAC_JDK_XML
944*c2e18aaaSAndroid Build Coastguard Worker    _DEFAULT_ANDROID_SDK_PATH = MAC_ANDROID_SDK_PATH
945*c2e18aaaSAndroid Build Coastguard Worker
946*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
947*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
948*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'studio'
949*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/Applications/Android Studio.app/Contents/MacOS']
950*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
951*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
952*c2e18aaaSAndroid Build Coastguard Worker
953*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
954*c2e18aaaSAndroid Build Coastguard Worker        """Collect the global config folder paths as a string list.
955*c2e18aaaSAndroid Build Coastguard Worker
956*c2e18aaaSAndroid Build Coastguard Worker        Returns:
957*c2e18aaaSAndroid Build Coastguard Worker            A string list for IDE config root paths, and return an empty list
958*c2e18aaaSAndroid Build Coastguard Worker            when none is found.
959*c2e18aaaSAndroid Build Coastguard Worker        """
960*c2e18aaaSAndroid Build Coastguard Worker        return glob.glob(
961*c2e18aaaSAndroid Build Coastguard Worker            os.path.join(
962*c2e18aaaSAndroid Build Coastguard Worker                os.getenv('HOME'), 'Library/Preferences/AndroidStudio*'))
963*c2e18aaaSAndroid Build Coastguard Worker
964*c2e18aaaSAndroid Build Coastguard Worker
965*c2e18aaaSAndroid Build Coastguard Workerclass IdeEclipse(IdeBase):
966*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Eclipse launching utilities.
967*c2e18aaaSAndroid Build Coastguard Worker
968*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
969*c2e18aaaSAndroid Build Coastguard Worker        cmd: A list of the build command.
970*c2e18aaaSAndroid Build Coastguard Worker
971*c2e18aaaSAndroid Build Coastguard Worker    For example:
972*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Eclipse is installed.
973*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Eclipse.
974*c2e18aaaSAndroid Build Coastguard Worker    """
975*c2e18aaaSAndroid Build Coastguard Worker
976*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
977*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
978*c2e18aaaSAndroid Build Coastguard Worker        self._ide_name = constant.IDE_ECLIPSE
979*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'eclipse'
980*c2e18aaaSAndroid Build Coastguard Worker        self.cmd = []
981*c2e18aaaSAndroid Build Coastguard Worker
982*c2e18aaaSAndroid Build Coastguard Worker    def _get_script_from_system(self):
983*c2e18aaaSAndroid Build Coastguard Worker        """Get correct IDE installed path from internal path.
984*c2e18aaaSAndroid Build Coastguard Worker
985*c2e18aaaSAndroid Build Coastguard Worker        Remove any file with extension, the filename should be like, 'eclipse',
986*c2e18aaaSAndroid Build Coastguard Worker        'eclipse47' and so on, check if the file is executable and filter out
987*c2e18aaaSAndroid Build Coastguard Worker        file such as 'eclipse.ini'.
988*c2e18aaaSAndroid Build Coastguard Worker
989*c2e18aaaSAndroid Build Coastguard Worker        Returns:
990*c2e18aaaSAndroid Build Coastguard Worker            The sh full path, or None if no IntelliJ version is installed.
991*c2e18aaaSAndroid Build Coastguard Worker        """
992*c2e18aaaSAndroid Build Coastguard Worker        for ide_path in self._bin_paths:
993*c2e18aaaSAndroid Build Coastguard Worker            # The binary name of Eclipse could be eclipse47, eclipse49,
994*c2e18aaaSAndroid Build Coastguard Worker            # eclipse47_testing or eclipse49_testing. So finding the matched
995*c2e18aaaSAndroid Build Coastguard Worker            # binary by /path/to/ide/eclipse*.
996*c2e18aaaSAndroid Build Coastguard Worker            ls_output = glob.glob(ide_path + '*', recursive=True)
997*c2e18aaaSAndroid Build Coastguard Worker            if ls_output:
998*c2e18aaaSAndroid Build Coastguard Worker                ls_output = sorted(ls_output)
999*c2e18aaaSAndroid Build Coastguard Worker                match_eclipses = []
1000*c2e18aaaSAndroid Build Coastguard Worker                for path in ls_output:
1001*c2e18aaaSAndroid Build Coastguard Worker                    if os.access(path, os.X_OK):
1002*c2e18aaaSAndroid Build Coastguard Worker                        match_eclipses.append(path)
1003*c2e18aaaSAndroid Build Coastguard Worker                if match_eclipses:
1004*c2e18aaaSAndroid Build Coastguard Worker                    match_eclipses = sorted(match_eclipses)
1005*c2e18aaaSAndroid Build Coastguard Worker                    logging.debug('Result for checking %s after sort: %s.',
1006*c2e18aaaSAndroid Build Coastguard Worker                                  self._ide_name, match_eclipses[0])
1007*c2e18aaaSAndroid Build Coastguard Worker                    return match_eclipses[0]
1008*c2e18aaaSAndroid Build Coastguard Worker        return None
1009*c2e18aaaSAndroid Build Coastguard Worker
1010*c2e18aaaSAndroid Build Coastguard Worker    def _get_ide_cmd(self):
1011*c2e18aaaSAndroid Build Coastguard Worker        """Compose launch IDE command to run a new process and redirect output.
1012*c2e18aaaSAndroid Build Coastguard Worker
1013*c2e18aaaSAndroid Build Coastguard Worker        AIDEGen will create a default workspace
1014*c2e18aaaSAndroid Build Coastguard Worker        ~/Documents/AIDEGen_Eclipse_workspace for users if they agree to do
1015*c2e18aaaSAndroid Build Coastguard Worker        that. Also, we could not import the default project through the command
1016*c2e18aaaSAndroid Build Coastguard Worker        line so remove the project path argument.
1017*c2e18aaaSAndroid Build Coastguard Worker
1018*c2e18aaaSAndroid Build Coastguard Worker        Returns:
1019*c2e18aaaSAndroid Build Coastguard Worker            A string of launch IDE command.
1020*c2e18aaaSAndroid Build Coastguard Worker        """
1021*c2e18aaaSAndroid Build Coastguard Worker        if (os.path.exists(os.path.expanduser(constant.ECLIPSE_WS))
1022*c2e18aaaSAndroid Build Coastguard Worker                or str(input(_ALERT_CREATE_WS)).lower() == 'y'):
1023*c2e18aaaSAndroid Build Coastguard Worker            self.cmd.extend(['-data', constant.ECLIPSE_WS])
1024*c2e18aaaSAndroid Build Coastguard Worker        self.cmd.extend([constant.IGNORE_STD_OUT_ERR_CMD, '&'])
1025*c2e18aaaSAndroid Build Coastguard Worker        return ' '.join(self.cmd)
1026*c2e18aaaSAndroid Build Coastguard Worker
1027*c2e18aaaSAndroid Build Coastguard Worker    def apply_optional_config(self):
1028*c2e18aaaSAndroid Build Coastguard Worker        """Override to do nothing."""
1029*c2e18aaaSAndroid Build Coastguard Worker
1030*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
1031*c2e18aaaSAndroid Build Coastguard Worker        """Override to do nothing."""
1032*c2e18aaaSAndroid Build Coastguard Worker
1033*c2e18aaaSAndroid Build Coastguard Worker
1034*c2e18aaaSAndroid Build Coastguard Workerclass IdeLinuxEclipse(IdeEclipse):
1035*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Eclipse launching utilities for OS Linux.
1036*c2e18aaaSAndroid Build Coastguard Worker
1037*c2e18aaaSAndroid Build Coastguard Worker    For example:
1038*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Eclipse is installed.
1039*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Eclipse.
1040*c2e18aaaSAndroid Build Coastguard Worker    """
1041*c2e18aaaSAndroid Build Coastguard Worker
1042*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1043*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1044*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/opt/eclipse*', '/usr/bin/']
1045*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
1046*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
1047*c2e18aaaSAndroid Build Coastguard Worker        self.cmd = [constant.NOHUP, self._installed_path.replace(' ', r'\ ')]
1048*c2e18aaaSAndroid Build Coastguard Worker
1049*c2e18aaaSAndroid Build Coastguard Worker
1050*c2e18aaaSAndroid Build Coastguard Workerclass IdeMacEclipse(IdeEclipse):
1051*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Eclipse launching utilities for OS Mac.
1052*c2e18aaaSAndroid Build Coastguard Worker
1053*c2e18aaaSAndroid Build Coastguard Worker    For example:
1054*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Eclipse is installed.
1055*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Eclipse.
1056*c2e18aaaSAndroid Build Coastguard Worker    """
1057*c2e18aaaSAndroid Build Coastguard Worker
1058*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1059*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1060*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'eclipse'
1061*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = [os.path.expanduser('~/eclipse/**')]
1062*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
1063*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
1064*c2e18aaaSAndroid Build Coastguard Worker        self.cmd = [self._installed_path.replace(' ', r'\ ')]
1065*c2e18aaaSAndroid Build Coastguard Worker
1066*c2e18aaaSAndroid Build Coastguard Worker
1067*c2e18aaaSAndroid Build Coastguard Workerclass IdeCLion(IdeBase):
1068*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of CLion launching utilities.
1069*c2e18aaaSAndroid Build Coastguard Worker
1070*c2e18aaaSAndroid Build Coastguard Worker    For example:
1071*c2e18aaaSAndroid Build Coastguard Worker        1. Check if CLion is installed.
1072*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an CLion.
1073*c2e18aaaSAndroid Build Coastguard Worker    """
1074*c2e18aaaSAndroid Build Coastguard Worker
1075*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1076*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1077*c2e18aaaSAndroid Build Coastguard Worker        self._ide_name = constant.IDE_CLION
1078*c2e18aaaSAndroid Build Coastguard Worker
1079*c2e18aaaSAndroid Build Coastguard Worker    def apply_optional_config(self):
1080*c2e18aaaSAndroid Build Coastguard Worker        """Override to do nothing."""
1081*c2e18aaaSAndroid Build Coastguard Worker
1082*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
1083*c2e18aaaSAndroid Build Coastguard Worker        """Override to do nothing."""
1084*c2e18aaaSAndroid Build Coastguard Worker
1085*c2e18aaaSAndroid Build Coastguard Worker
1086*c2e18aaaSAndroid Build Coastguard Workerclass IdeLinuxCLion(IdeCLion):
1087*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of CLion launching utilities for OS Linux.
1088*c2e18aaaSAndroid Build Coastguard Worker
1089*c2e18aaaSAndroid Build Coastguard Worker    For example:
1090*c2e18aaaSAndroid Build Coastguard Worker        1. Check if CLion is installed.
1091*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an CLion.
1092*c2e18aaaSAndroid Build Coastguard Worker    """
1093*c2e18aaaSAndroid Build Coastguard Worker
1094*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1095*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1096*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'clion.sh'
1097*c2e18aaaSAndroid Build Coastguard Worker        # TODO(b/141288011): Handle /opt/clion-*/bin to let users choose a
1098*c2e18aaaSAndroid Build Coastguard Worker        # preferred version of CLion in the future.
1099*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/opt/clion-stable/bin']
1100*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
1101*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
1102*c2e18aaaSAndroid Build Coastguard Worker
1103*c2e18aaaSAndroid Build Coastguard Worker
1104*c2e18aaaSAndroid Build Coastguard Workerclass IdeMacCLion(IdeCLion):
1105*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of Android Studio launching utilities for OS Mac.
1106*c2e18aaaSAndroid Build Coastguard Worker
1107*c2e18aaaSAndroid Build Coastguard Worker    For example:
1108*c2e18aaaSAndroid Build Coastguard Worker        1. Check if Android Studio is installed.
1109*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an Android Studio.
1110*c2e18aaaSAndroid Build Coastguard Worker    """
1111*c2e18aaaSAndroid Build Coastguard Worker
1112*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1113*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1114*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'clion'
1115*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/Applications/CLion.app/Contents/MacOS/CLion']
1116*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
1117*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
1118*c2e18aaaSAndroid Build Coastguard Worker
1119*c2e18aaaSAndroid Build Coastguard Worker
1120*c2e18aaaSAndroid Build Coastguard Workerclass IdeVSCode(IdeBase):
1121*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of VSCode launching utilities.
1122*c2e18aaaSAndroid Build Coastguard Worker
1123*c2e18aaaSAndroid Build Coastguard Worker    For example:
1124*c2e18aaaSAndroid Build Coastguard Worker        1. Check if VSCode is installed.
1125*c2e18aaaSAndroid Build Coastguard Worker        2. Launch an VSCode.
1126*c2e18aaaSAndroid Build Coastguard Worker    """
1127*c2e18aaaSAndroid Build Coastguard Worker
1128*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1129*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1130*c2e18aaaSAndroid Build Coastguard Worker        self._ide_name = constant.IDE_VSCODE
1131*c2e18aaaSAndroid Build Coastguard Worker
1132*c2e18aaaSAndroid Build Coastguard Worker    def apply_optional_config(self):
1133*c2e18aaaSAndroid Build Coastguard Worker        """Override to do nothing."""
1134*c2e18aaaSAndroid Build Coastguard Worker
1135*c2e18aaaSAndroid Build Coastguard Worker    def _get_config_root_paths(self):
1136*c2e18aaaSAndroid Build Coastguard Worker        """Override to do nothing."""
1137*c2e18aaaSAndroid Build Coastguard Worker
1138*c2e18aaaSAndroid Build Coastguard Worker
1139*c2e18aaaSAndroid Build Coastguard Workerclass IdeLinuxVSCode(IdeVSCode):
1140*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of VSCode launching utilities for OS Linux."""
1141*c2e18aaaSAndroid Build Coastguard Worker
1142*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1143*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1144*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'code'
1145*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/usr/bin']
1146*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
1147*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
1148*c2e18aaaSAndroid Build Coastguard Worker
1149*c2e18aaaSAndroid Build Coastguard Worker
1150*c2e18aaaSAndroid Build Coastguard Workerclass IdeMacVSCode(IdeVSCode):
1151*c2e18aaaSAndroid Build Coastguard Worker    """Class offers a set of VSCode launching utilities for OS Mac."""
1152*c2e18aaaSAndroid Build Coastguard Worker
1153*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self, installed_path=None, config_reset=False):
1154*c2e18aaaSAndroid Build Coastguard Worker        super().__init__(installed_path, config_reset)
1155*c2e18aaaSAndroid Build Coastguard Worker        self._bin_file_name = 'code'
1156*c2e18aaaSAndroid Build Coastguard Worker        self._bin_folders = ['/usr/local/bin']
1157*c2e18aaaSAndroid Build Coastguard Worker        self._bin_paths = self._get_possible_bin_paths()
1158*c2e18aaaSAndroid Build Coastguard Worker        self._init_installed_path(installed_path)
1159*c2e18aaaSAndroid Build Coastguard Worker
1160*c2e18aaaSAndroid Build Coastguard Worker
1161*c2e18aaaSAndroid Build Coastguard Workerdef get_ide_util_instance(ide='j'):
1162*c2e18aaaSAndroid Build Coastguard Worker    """Get an IdeUtil class instance for launching IDE.
1163*c2e18aaaSAndroid Build Coastguard Worker
1164*c2e18aaaSAndroid Build Coastguard Worker    Args:
1165*c2e18aaaSAndroid Build Coastguard Worker        ide: A key character of IDE to be launched. Default ide='j' is to
1166*c2e18aaaSAndroid Build Coastguard Worker            launch IntelliJ.
1167*c2e18aaaSAndroid Build Coastguard Worker
1168*c2e18aaaSAndroid Build Coastguard Worker    Returns:
1169*c2e18aaaSAndroid Build Coastguard Worker        An IdeUtil class instance.
1170*c2e18aaaSAndroid Build Coastguard Worker    """
1171*c2e18aaaSAndroid Build Coastguard Worker    conf = project_config.ProjectConfig.get_instance()
1172*c2e18aaaSAndroid Build Coastguard Worker    if not conf.is_launch_ide:
1173*c2e18aaaSAndroid Build Coastguard Worker        return None
1174*c2e18aaaSAndroid Build Coastguard Worker    is_mac = (android_dev_os.AndroidDevOS.MAC == android_dev_os.AndroidDevOS.
1175*c2e18aaaSAndroid Build Coastguard Worker              get_os_type())
1176*c2e18aaaSAndroid Build Coastguard Worker    tool = IdeUtil(conf.ide_installed_path, ide, conf.config_reset, is_mac)
1177*c2e18aaaSAndroid Build Coastguard Worker    if not tool.is_ide_installed():
1178*c2e18aaaSAndroid Build Coastguard Worker        ipath = conf.ide_installed_path or tool.get_default_path()
1179*c2e18aaaSAndroid Build Coastguard Worker        err = _NO_LAUNCH_IDE_CMD.format(constant.IDE_NAME_DICT[ide], ipath)
1180*c2e18aaaSAndroid Build Coastguard Worker        logging.error(err)
1181*c2e18aaaSAndroid Build Coastguard Worker        stack_trace = common_util.remove_user_home_path(err)
1182*c2e18aaaSAndroid Build Coastguard Worker        logs = '%s exists? %s' % (common_util.remove_user_home_path(ipath),
1183*c2e18aaaSAndroid Build Coastguard Worker                                  os.path.exists(ipath))
1184*c2e18aaaSAndroid Build Coastguard Worker        aidegen_metrics.ends_asuite_metrics(constant.IDE_LAUNCH_FAILURE,
1185*c2e18aaaSAndroid Build Coastguard Worker                                            stack_trace,
1186*c2e18aaaSAndroid Build Coastguard Worker                                            logs)
1187*c2e18aaaSAndroid Build Coastguard Worker        raise errors.IDENotExistError(err)
1188*c2e18aaaSAndroid Build Coastguard Worker    return tool
1189*c2e18aaaSAndroid Build Coastguard Worker
1190*c2e18aaaSAndroid Build Coastguard Worker
1191*c2e18aaaSAndroid Build Coastguard Workerdef _get_ide(installed_path=None, ide='j', config_reset=False, is_mac=False):
1192*c2e18aaaSAndroid Build Coastguard Worker    """Get IDE to be launched according to the ide input and OS type.
1193*c2e18aaaSAndroid Build Coastguard Worker
1194*c2e18aaaSAndroid Build Coastguard Worker    Args:
1195*c2e18aaaSAndroid Build Coastguard Worker        installed_path: The IDE installed path to be checked.
1196*c2e18aaaSAndroid Build Coastguard Worker        ide: A key character of IDE to be launched. Default ide='j' is to
1197*c2e18aaaSAndroid Build Coastguard Worker            launch IntelliJ.
1198*c2e18aaaSAndroid Build Coastguard Worker        config_reset: A boolean, if true reset configuration data.
1199*c2e18aaaSAndroid Build Coastguard Worker
1200*c2e18aaaSAndroid Build Coastguard Worker    Returns:
1201*c2e18aaaSAndroid Build Coastguard Worker        A corresponding IDE instance.
1202*c2e18aaaSAndroid Build Coastguard Worker    """
1203*c2e18aaaSAndroid Build Coastguard Worker    if is_mac:
1204*c2e18aaaSAndroid Build Coastguard Worker        return _get_mac_ide(installed_path, ide, config_reset)
1205*c2e18aaaSAndroid Build Coastguard Worker    return _get_linux_ide(installed_path, ide, config_reset)
1206*c2e18aaaSAndroid Build Coastguard Worker
1207*c2e18aaaSAndroid Build Coastguard Worker
1208*c2e18aaaSAndroid Build Coastguard Workerdef _get_mac_ide(installed_path=None, ide='j', config_reset=False):
1209*c2e18aaaSAndroid Build Coastguard Worker    """Get IDE to be launched according to the ide input for OS Mac.
1210*c2e18aaaSAndroid Build Coastguard Worker
1211*c2e18aaaSAndroid Build Coastguard Worker    Args:
1212*c2e18aaaSAndroid Build Coastguard Worker        installed_path: The IDE installed path to be checked.
1213*c2e18aaaSAndroid Build Coastguard Worker        ide: A key character of IDE to be launched. Default ide='j' is to
1214*c2e18aaaSAndroid Build Coastguard Worker            launch IntelliJ.
1215*c2e18aaaSAndroid Build Coastguard Worker        config_reset: A boolean, if true reset configuration data.
1216*c2e18aaaSAndroid Build Coastguard Worker
1217*c2e18aaaSAndroid Build Coastguard Worker    Returns:
1218*c2e18aaaSAndroid Build Coastguard Worker        A corresponding IDE instance.
1219*c2e18aaaSAndroid Build Coastguard Worker    """
1220*c2e18aaaSAndroid Build Coastguard Worker    if ide == 'e':
1221*c2e18aaaSAndroid Build Coastguard Worker        return IdeMacEclipse(installed_path, config_reset)
1222*c2e18aaaSAndroid Build Coastguard Worker    if ide == 's':
1223*c2e18aaaSAndroid Build Coastguard Worker        return IdeMacStudio(installed_path, config_reset)
1224*c2e18aaaSAndroid Build Coastguard Worker    if ide == 'c':
1225*c2e18aaaSAndroid Build Coastguard Worker        return IdeMacCLion(installed_path, config_reset)
1226*c2e18aaaSAndroid Build Coastguard Worker    if ide == 'v':
1227*c2e18aaaSAndroid Build Coastguard Worker        return IdeMacVSCode(installed_path, config_reset)
1228*c2e18aaaSAndroid Build Coastguard Worker    return IdeMacIntelliJ(installed_path, config_reset)
1229*c2e18aaaSAndroid Build Coastguard Worker
1230*c2e18aaaSAndroid Build Coastguard Worker
1231*c2e18aaaSAndroid Build Coastguard Workerdef _get_linux_ide(installed_path=None, ide='j', config_reset=False):
1232*c2e18aaaSAndroid Build Coastguard Worker    """Get IDE to be launched according to the ide input for OS Linux.
1233*c2e18aaaSAndroid Build Coastguard Worker
1234*c2e18aaaSAndroid Build Coastguard Worker    Args:
1235*c2e18aaaSAndroid Build Coastguard Worker        installed_path: The IDE installed path to be checked.
1236*c2e18aaaSAndroid Build Coastguard Worker        ide: A key character of IDE to be launched. Default ide='j' is to
1237*c2e18aaaSAndroid Build Coastguard Worker            launch IntelliJ.
1238*c2e18aaaSAndroid Build Coastguard Worker        config_reset: A boolean, if true reset configuration data.
1239*c2e18aaaSAndroid Build Coastguard Worker
1240*c2e18aaaSAndroid Build Coastguard Worker    Returns:
1241*c2e18aaaSAndroid Build Coastguard Worker        A corresponding IDE instance.
1242*c2e18aaaSAndroid Build Coastguard Worker    """
1243*c2e18aaaSAndroid Build Coastguard Worker    if ide == 'e':
1244*c2e18aaaSAndroid Build Coastguard Worker        return IdeLinuxEclipse(installed_path, config_reset)
1245*c2e18aaaSAndroid Build Coastguard Worker    if ide == 's':
1246*c2e18aaaSAndroid Build Coastguard Worker        return IdeLinuxStudio(installed_path, config_reset)
1247*c2e18aaaSAndroid Build Coastguard Worker    if ide == 'c':
1248*c2e18aaaSAndroid Build Coastguard Worker        return IdeLinuxCLion(installed_path, config_reset)
1249*c2e18aaaSAndroid Build Coastguard Worker    if ide == 'v':
1250*c2e18aaaSAndroid Build Coastguard Worker        return IdeLinuxVSCode(installed_path, config_reset)
1251*c2e18aaaSAndroid Build Coastguard Worker    return IdeLinuxIntelliJ(installed_path, config_reset)
1252