xref: /aosp_15_r20/tools/asuite/aidegen/lib/ide_common_util.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2019 - 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"""ide_common_util
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard WorkerThis module has a collection of helper functions of the ide_util module.
20*c2e18aaaSAndroid Build Coastguard Worker"""
21*c2e18aaaSAndroid Build Coastguard Worker
22*c2e18aaaSAndroid Build Coastguard Workerimport fnmatch
23*c2e18aaaSAndroid Build Coastguard Workerimport glob
24*c2e18aaaSAndroid Build Coastguard Workerimport logging
25*c2e18aaaSAndroid Build Coastguard Workerimport os
26*c2e18aaaSAndroid Build Coastguard Workerimport subprocess
27*c2e18aaaSAndroid Build Coastguard Worker
28*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen import constant
29*c2e18aaaSAndroid Build Coastguard Worker
30*c2e18aaaSAndroid Build Coastguard Worker_IDEA_FOLDER = '.idea'
31*c2e18aaaSAndroid Build Coastguard Worker_IML_EXTENSION = '.iml'
32*c2e18aaaSAndroid Build Coastguard Worker
33*c2e18aaaSAndroid Build Coastguard Worker
34*c2e18aaaSAndroid Build Coastguard Workerdef get_script_from_internal_path(ide_paths, ide_name):
35*c2e18aaaSAndroid Build Coastguard Worker    """Get the studio.sh script path from internal path.
36*c2e18aaaSAndroid Build Coastguard Worker
37*c2e18aaaSAndroid Build Coastguard Worker    Args:
38*c2e18aaaSAndroid Build Coastguard Worker        ide_paths: A list of IDE installed paths to be checked.
39*c2e18aaaSAndroid Build Coastguard Worker        ide_name: The IDE name.
40*c2e18aaaSAndroid Build Coastguard Worker
41*c2e18aaaSAndroid Build Coastguard Worker    Returns:
42*c2e18aaaSAndroid Build Coastguard Worker        The list of the full path of IDE or None if the IDE doesn't exist.
43*c2e18aaaSAndroid Build Coastguard Worker    """
44*c2e18aaaSAndroid Build Coastguard Worker    for ide_path in ide_paths:
45*c2e18aaaSAndroid Build Coastguard Worker        ls_output = glob.glob(ide_path, recursive=True)
46*c2e18aaaSAndroid Build Coastguard Worker        ls_output = sorted(ls_output)
47*c2e18aaaSAndroid Build Coastguard Worker        if ls_output:
48*c2e18aaaSAndroid Build Coastguard Worker            logging.debug('The script%s for %s %s found.',
49*c2e18aaaSAndroid Build Coastguard Worker                          's' if len(ls_output) > 1 else '', ide_name,
50*c2e18aaaSAndroid Build Coastguard Worker                          'are' if len(ls_output) > 1 else 'is')
51*c2e18aaaSAndroid Build Coastguard Worker            return ls_output
52*c2e18aaaSAndroid Build Coastguard Worker    logging.error('There is not any script of %s found.', ide_name)
53*c2e18aaaSAndroid Build Coastguard Worker    return None
54*c2e18aaaSAndroid Build Coastguard Worker
55*c2e18aaaSAndroid Build Coastguard Worker
56*c2e18aaaSAndroid Build Coastguard Workerdef _run_ide_sh(run_sh_cmd, project_path):
57*c2e18aaaSAndroid Build Coastguard Worker    """Run IDE launching script with an IntelliJ project path as argument.
58*c2e18aaaSAndroid Build Coastguard Worker
59*c2e18aaaSAndroid Build Coastguard Worker    Args:
60*c2e18aaaSAndroid Build Coastguard Worker        run_sh_cmd: The command to launch IDE.
61*c2e18aaaSAndroid Build Coastguard Worker        project_path: The path of IntelliJ IDEA project content.
62*c2e18aaaSAndroid Build Coastguard Worker    """
63*c2e18aaaSAndroid Build Coastguard Worker    assert run_sh_cmd, 'No suitable IDE installed.'
64*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Run command: "%s" to launch project.', run_sh_cmd)
65*c2e18aaaSAndroid Build Coastguard Worker    try:
66*c2e18aaaSAndroid Build Coastguard Worker        subprocess.check_call(run_sh_cmd, shell=True)
67*c2e18aaaSAndroid Build Coastguard Worker    except subprocess.CalledProcessError as err:
68*c2e18aaaSAndroid Build Coastguard Worker        logging.error('Launch project path %s failed with error: %s.',
69*c2e18aaaSAndroid Build Coastguard Worker                      project_path, err)
70*c2e18aaaSAndroid Build Coastguard Worker
71*c2e18aaaSAndroid Build Coastguard Worker
72*c2e18aaaSAndroid Build Coastguard Workerdef _walk_tree_find_ide_exe_file(top, ide_script_name):
73*c2e18aaaSAndroid Build Coastguard Worker    """Recursively descend the directory tree rooted at top and filter out the
74*c2e18aaaSAndroid Build Coastguard Worker       IDE executable script we need.
75*c2e18aaaSAndroid Build Coastguard Worker
76*c2e18aaaSAndroid Build Coastguard Worker    Args:
77*c2e18aaaSAndroid Build Coastguard Worker        top: the tree root to be checked.
78*c2e18aaaSAndroid Build Coastguard Worker        ide_script_name: IDE file name such i.e. IdeIntelliJ._INTELLIJ_EXE_FILE.
79*c2e18aaaSAndroid Build Coastguard Worker
80*c2e18aaaSAndroid Build Coastguard Worker    Returns:
81*c2e18aaaSAndroid Build Coastguard Worker        the IDE executable script file(s) found.
82*c2e18aaaSAndroid Build Coastguard Worker    """
83*c2e18aaaSAndroid Build Coastguard Worker    logging.info('Searching IDE script %s in path: %s.', ide_script_name, top)
84*c2e18aaaSAndroid Build Coastguard Worker    for root, _, files in os.walk(top):
85*c2e18aaaSAndroid Build Coastguard Worker        logging.debug('Search all files under %s to get %s, %s.', top, root,
86*c2e18aaaSAndroid Build Coastguard Worker                      files)
87*c2e18aaaSAndroid Build Coastguard Worker        for file_ in fnmatch.filter(files, ide_script_name):
88*c2e18aaaSAndroid Build Coastguard Worker            exe_file = os.path.join(root, file_)
89*c2e18aaaSAndroid Build Coastguard Worker            if os.access(exe_file, os.X_OK):
90*c2e18aaaSAndroid Build Coastguard Worker                logging.debug('Use file name filter to find %s in path %s.',
91*c2e18aaaSAndroid Build Coastguard Worker                              file_, exe_file)
92*c2e18aaaSAndroid Build Coastguard Worker                yield exe_file
93*c2e18aaaSAndroid Build Coastguard Worker
94*c2e18aaaSAndroid Build Coastguard Worker
95*c2e18aaaSAndroid Build Coastguard Workerdef get_run_ide_cmd(sh_path, project_file, new_process=True):
96*c2e18aaaSAndroid Build Coastguard Worker    """Get the command to launch IDE.
97*c2e18aaaSAndroid Build Coastguard Worker
98*c2e18aaaSAndroid Build Coastguard Worker    Args:
99*c2e18aaaSAndroid Build Coastguard Worker        sh_path: The idea.sh path where IDE is installed.
100*c2e18aaaSAndroid Build Coastguard Worker        project_file: The path of IntelliJ IDEA project file.
101*c2e18aaaSAndroid Build Coastguard Worker        new_process: Default is True, means to run command in a new process.
102*c2e18aaaSAndroid Build Coastguard Worker
103*c2e18aaaSAndroid Build Coastguard Worker    Returns:
104*c2e18aaaSAndroid Build Coastguard Worker        A string: The IDE launching command.
105*c2e18aaaSAndroid Build Coastguard Worker    """
106*c2e18aaaSAndroid Build Coastguard Worker    process_flag = '&' if new_process else ''
107*c2e18aaaSAndroid Build Coastguard Worker    # In command usage, the space ' ' should be '\ ' for correctness.
108*c2e18aaaSAndroid Build Coastguard Worker    return ' '.join([
109*c2e18aaaSAndroid Build Coastguard Worker        constant.NOHUP, sh_path.replace(' ', r'\ '), project_file,
110*c2e18aaaSAndroid Build Coastguard Worker        constant.IGNORE_STD_OUT_ERR_CMD, process_flag
111*c2e18aaaSAndroid Build Coastguard Worker    ])
112*c2e18aaaSAndroid Build Coastguard Worker
113*c2e18aaaSAndroid Build Coastguard Worker
114*c2e18aaaSAndroid Build Coastguard Workerdef _get_scripts_from_file_path(input_path, ide_file_name):
115*c2e18aaaSAndroid Build Coastguard Worker    """Get IDE executable script file from input file path.
116*c2e18aaaSAndroid Build Coastguard Worker
117*c2e18aaaSAndroid Build Coastguard Worker    Args:
118*c2e18aaaSAndroid Build Coastguard Worker        input_path: the file path to be checked.
119*c2e18aaaSAndroid Build Coastguard Worker        ide_file_name: the IDE executable script file name.
120*c2e18aaaSAndroid Build Coastguard Worker
121*c2e18aaaSAndroid Build Coastguard Worker    Returns:
122*c2e18aaaSAndroid Build Coastguard Worker        A list of the IDE executable script path if exists otherwise None.
123*c2e18aaaSAndroid Build Coastguard Worker    """
124*c2e18aaaSAndroid Build Coastguard Worker    if os.path.basename(input_path).startswith(ide_file_name):
125*c2e18aaaSAndroid Build Coastguard Worker        files_found = glob.glob(input_path)
126*c2e18aaaSAndroid Build Coastguard Worker        if files_found:
127*c2e18aaaSAndroid Build Coastguard Worker            return sorted(files_found)
128*c2e18aaaSAndroid Build Coastguard Worker    return None
129*c2e18aaaSAndroid Build Coastguard Worker
130*c2e18aaaSAndroid Build Coastguard Worker
131*c2e18aaaSAndroid Build Coastguard Workerdef get_scripts_from_dir_path(input_path, ide_file_name):
132*c2e18aaaSAndroid Build Coastguard Worker    """Get an IDE executable script file from input directory path.
133*c2e18aaaSAndroid Build Coastguard Worker
134*c2e18aaaSAndroid Build Coastguard Worker    Args:
135*c2e18aaaSAndroid Build Coastguard Worker        input_path: the directory to be searched.
136*c2e18aaaSAndroid Build Coastguard Worker        ide_file_name: the IDE executable script file name.
137*c2e18aaaSAndroid Build Coastguard Worker
138*c2e18aaaSAndroid Build Coastguard Worker    Returns:
139*c2e18aaaSAndroid Build Coastguard Worker        A list of an IDE executable script paths if exist otherwise None.
140*c2e18aaaSAndroid Build Coastguard Worker    """
141*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Call get_scripts_from_dir_path with %s, and %s', input_path,
142*c2e18aaaSAndroid Build Coastguard Worker                  ide_file_name)
143*c2e18aaaSAndroid Build Coastguard Worker    files_found = list(_walk_tree_find_ide_exe_file(input_path,
144*c2e18aaaSAndroid Build Coastguard Worker                                                    ide_file_name + '*'))
145*c2e18aaaSAndroid Build Coastguard Worker    if files_found:
146*c2e18aaaSAndroid Build Coastguard Worker        return sorted(files_found)
147*c2e18aaaSAndroid Build Coastguard Worker    return None
148*c2e18aaaSAndroid Build Coastguard Worker
149*c2e18aaaSAndroid Build Coastguard Worker
150*c2e18aaaSAndroid Build Coastguard Workerdef launch_ide(project_path, run_ide_cmd, ide_name):
151*c2e18aaaSAndroid Build Coastguard Worker    """Launches relative IDE by opening the passed project file.
152*c2e18aaaSAndroid Build Coastguard Worker
153*c2e18aaaSAndroid Build Coastguard Worker    Args:
154*c2e18aaaSAndroid Build Coastguard Worker        project_path: The full path of the IDE project content.
155*c2e18aaaSAndroid Build Coastguard Worker        run_ide_cmd: The command to launch IDE.
156*c2e18aaaSAndroid Build Coastguard Worker        ide_name: the IDE name is to be launched.
157*c2e18aaaSAndroid Build Coastguard Worker    """
158*c2e18aaaSAndroid Build Coastguard Worker    assert project_path, 'Empty content path is not allowed.'
159*c2e18aaaSAndroid Build Coastguard Worker    if ide_name == constant.IDE_ECLIPSE:
160*c2e18aaaSAndroid Build Coastguard Worker        logging.info(
161*c2e18aaaSAndroid Build Coastguard Worker            'Launch %s with workspace: %s.', ide_name, constant.ECLIPSE_WS)
162*c2e18aaaSAndroid Build Coastguard Worker    else:
163*c2e18aaaSAndroid Build Coastguard Worker        logging.info('Launch %s for project content path: %s.', ide_name,
164*c2e18aaaSAndroid Build Coastguard Worker                     project_path)
165*c2e18aaaSAndroid Build Coastguard Worker    _run_ide_sh(run_ide_cmd, project_path)
166*c2e18aaaSAndroid Build Coastguard Worker
167*c2e18aaaSAndroid Build Coastguard Worker
168*c2e18aaaSAndroid Build Coastguard Workerdef is_intellij_project(project_path):
169*c2e18aaaSAndroid Build Coastguard Worker    """Checks if the path passed in is an IntelliJ project content.
170*c2e18aaaSAndroid Build Coastguard Worker
171*c2e18aaaSAndroid Build Coastguard Worker    Args:
172*c2e18aaaSAndroid Build Coastguard Worker        project_path: The full path of IDEA project content, which contains
173*c2e18aaaSAndroid Build Coastguard Worker        .idea folder and .iml file(s).
174*c2e18aaaSAndroid Build Coastguard Worker
175*c2e18aaaSAndroid Build Coastguard Worker    Returns:
176*c2e18aaaSAndroid Build Coastguard Worker        True if project_path is an IntelliJ project, False otherwise.
177*c2e18aaaSAndroid Build Coastguard Worker    """
178*c2e18aaaSAndroid Build Coastguard Worker    if not os.path.isfile(project_path):
179*c2e18aaaSAndroid Build Coastguard Worker        return os.path.isdir(project_path) and os.path.isdir(
180*c2e18aaaSAndroid Build Coastguard Worker            os.path.join(project_path, _IDEA_FOLDER))
181*c2e18aaaSAndroid Build Coastguard Worker
182*c2e18aaaSAndroid Build Coastguard Worker    _, ext = os.path.splitext(os.path.basename(project_path))
183*c2e18aaaSAndroid Build Coastguard Worker    if ext and _IML_EXTENSION == ext.lower():
184*c2e18aaaSAndroid Build Coastguard Worker        path = os.path.dirname(project_path)
185*c2e18aaaSAndroid Build Coastguard Worker        logging.debug('Extracted path is: %s.', path)
186*c2e18aaaSAndroid Build Coastguard Worker        return os.path.isdir(os.path.join(path, _IDEA_FOLDER))
187*c2e18aaaSAndroid Build Coastguard Worker    return False
188*c2e18aaaSAndroid Build Coastguard Worker
189*c2e18aaaSAndroid Build Coastguard Worker
190*c2e18aaaSAndroid Build Coastguard Workerdef get_script_from_input_path(input_path, ide_file_name):
191*c2e18aaaSAndroid Build Coastguard Worker    """Get correct IntelliJ executable script path from input path.
192*c2e18aaaSAndroid Build Coastguard Worker
193*c2e18aaaSAndroid Build Coastguard Worker    1. If input_path is a file, check if it is an IDE executable script file.
194*c2e18aaaSAndroid Build Coastguard Worker    2. It input_path is a directory, search if it contains IDE executable script
195*c2e18aaaSAndroid Build Coastguard Worker       file(s).
196*c2e18aaaSAndroid Build Coastguard Worker
197*c2e18aaaSAndroid Build Coastguard Worker    Args:
198*c2e18aaaSAndroid Build Coastguard Worker        input_path: input path to be checked if it's an IDE executable
199*c2e18aaaSAndroid Build Coastguard Worker                    script.
200*c2e18aaaSAndroid Build Coastguard Worker        ide_file_name: the IDE executable script file name.
201*c2e18aaaSAndroid Build Coastguard Worker
202*c2e18aaaSAndroid Build Coastguard Worker    Returns:
203*c2e18aaaSAndroid Build Coastguard Worker        IDE executable file(s) if exists otherwise None.
204*c2e18aaaSAndroid Build Coastguard Worker    """
205*c2e18aaaSAndroid Build Coastguard Worker    if not input_path:
206*c2e18aaaSAndroid Build Coastguard Worker        return None
207*c2e18aaaSAndroid Build Coastguard Worker    ide_path = []
208*c2e18aaaSAndroid Build Coastguard Worker    if os.path.isfile(input_path):
209*c2e18aaaSAndroid Build Coastguard Worker        ide_path = _get_scripts_from_file_path(input_path, ide_file_name)
210*c2e18aaaSAndroid Build Coastguard Worker    if os.path.isdir(input_path):
211*c2e18aaaSAndroid Build Coastguard Worker        ide_path = get_scripts_from_dir_path(input_path, ide_file_name)
212*c2e18aaaSAndroid Build Coastguard Worker    if ide_path:
213*c2e18aaaSAndroid Build Coastguard Worker        logging.debug('IDE installed path from user input: %s.', ide_path)
214*c2e18aaaSAndroid Build Coastguard Worker        return ide_path
215*c2e18aaaSAndroid Build Coastguard Worker    return None
216*c2e18aaaSAndroid Build Coastguard Worker
217*c2e18aaaSAndroid Build Coastguard Worker
218*c2e18aaaSAndroid Build Coastguard Workerdef get_intellij_version_path(version_path):
219*c2e18aaaSAndroid Build Coastguard Worker    """Locates the IntelliJ IDEA launch script path by version.
220*c2e18aaaSAndroid Build Coastguard Worker
221*c2e18aaaSAndroid Build Coastguard Worker    Args:
222*c2e18aaaSAndroid Build Coastguard Worker        version_path: IntelliJ CE or UE version launch script path.
223*c2e18aaaSAndroid Build Coastguard Worker
224*c2e18aaaSAndroid Build Coastguard Worker    Returns:
225*c2e18aaaSAndroid Build Coastguard Worker        A list of the sh full paths, or None if no such IntelliJ version is
226*c2e18aaaSAndroid Build Coastguard Worker        installed.
227*c2e18aaaSAndroid Build Coastguard Worker    """
228*c2e18aaaSAndroid Build Coastguard Worker    ls_output = glob.glob(version_path, recursive=True)
229*c2e18aaaSAndroid Build Coastguard Worker    if not ls_output:
230*c2e18aaaSAndroid Build Coastguard Worker        return None
231*c2e18aaaSAndroid Build Coastguard Worker    ls_output = sorted(ls_output, reverse=True)
232*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Result for checking IntelliJ path %s after sorting:%s.',
233*c2e18aaaSAndroid Build Coastguard Worker                  version_path, ls_output)
234*c2e18aaaSAndroid Build Coastguard Worker    return ls_output
235*c2e18aaaSAndroid Build Coastguard Worker
236*c2e18aaaSAndroid Build Coastguard Worker
237*c2e18aaaSAndroid Build Coastguard Workerdef ask_preference(all_versions, ide_name):
238*c2e18aaaSAndroid Build Coastguard Worker    """Ask users which version they prefer.
239*c2e18aaaSAndroid Build Coastguard Worker
240*c2e18aaaSAndroid Build Coastguard Worker    Args:
241*c2e18aaaSAndroid Build Coastguard Worker        all_versions: A list of all CE and UE version launch script paths.
242*c2e18aaaSAndroid Build Coastguard Worker        ide_name: The IDE name is going to be launched.
243*c2e18aaaSAndroid Build Coastguard Worker
244*c2e18aaaSAndroid Build Coastguard Worker    Returns:
245*c2e18aaaSAndroid Build Coastguard Worker        An users selected version.
246*c2e18aaaSAndroid Build Coastguard Worker    """
247*c2e18aaaSAndroid Build Coastguard Worker    options = []
248*c2e18aaaSAndroid Build Coastguard Worker    for i, sfile in enumerate(all_versions, 1):
249*c2e18aaaSAndroid Build Coastguard Worker        options.append('\t{}. {}'.format(i, sfile))
250*c2e18aaaSAndroid Build Coastguard Worker    query = ('You installed {} versions of {}:\n{}\nPlease select '
251*c2e18aaaSAndroid Build Coastguard Worker             'one.\t').format(len(all_versions), ide_name, '\n'.join(options))
252*c2e18aaaSAndroid Build Coastguard Worker    return _select_intellij_version(query, all_versions)
253*c2e18aaaSAndroid Build Coastguard Worker
254*c2e18aaaSAndroid Build Coastguard Worker
255*c2e18aaaSAndroid Build Coastguard Workerdef _select_intellij_version(query, all_versions):
256*c2e18aaaSAndroid Build Coastguard Worker    """Select one from different IntelliJ versions users installed.
257*c2e18aaaSAndroid Build Coastguard Worker
258*c2e18aaaSAndroid Build Coastguard Worker    Args:
259*c2e18aaaSAndroid Build Coastguard Worker        query: The query message.
260*c2e18aaaSAndroid Build Coastguard Worker        all_versions: A list of all CE and UE version launch script paths.
261*c2e18aaaSAndroid Build Coastguard Worker    """
262*c2e18aaaSAndroid Build Coastguard Worker    all_numbers = []
263*c2e18aaaSAndroid Build Coastguard Worker    for i in range(len(all_versions)):
264*c2e18aaaSAndroid Build Coastguard Worker        all_numbers.append(str(i + 1))
265*c2e18aaaSAndroid Build Coastguard Worker    input_data = input(query)
266*c2e18aaaSAndroid Build Coastguard Worker    while input_data not in all_numbers:
267*c2e18aaaSAndroid Build Coastguard Worker        input_data = input('Please select a number:\t')
268*c2e18aaaSAndroid Build Coastguard Worker    return all_versions[int(input_data) - 1]
269