| #!/usr/bin/env python3 |
| # |
| # Copyright 2018 - The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """It is an AIDEGen sub task : IDE operation task! |
| |
| Takes a project file path as input, after passing the needed check(file |
| existence, IDE type, etc.), launch the project in related IDE. |
| |
| Typical usage example: |
| |
| ide_util_obj = IdeUtil() |
| if ide_util_obj.is_ide_installed(): |
| ide_util_obj.config_ide(project_file) |
| ide_util_obj.launch_ide() |
| """ |
| |
| import fnmatch |
| import glob |
| import logging |
| import os |
| import platform |
| import re |
| import subprocess |
| |
| from aidegen import constant |
| from aidegen.lib import common_util |
| from aidegen.lib import config |
| from aidegen.lib import sdk_config |
| |
| # Add 'nohup' to prevent IDE from being terminated when console is terminated. |
| _NOHUP = 'nohup' |
| _IGNORE_STD_OUT_ERR_CMD = '2>/dev/null >&2' |
| _IDEA_FOLDER = '.idea' |
| _IML_EXTENSION = '.iml' |
| _JDK_PATH_TOKEN = '@JDKpath' |
| _COMPONENT_END_TAG = ' </component>' |
| |
| |
| class IdeUtil: |
| """Provide a set of IDE operations, e.g., launch and configuration. |
| |
| Attributes: |
| _ide: IdeBase derived instance, the related IDE object. |
| |
| For example: |
| 1. Check if IDE is installed. |
| 2. Config IDE, e.g. config code style, SDK path, and etc. |
| 3. Launch an IDE. |
| """ |
| |
| def __init__(self, |
| installed_path=None, |
| ide='j', |
| config_reset=False, |
| is_mac=False): |
| logging.debug('IdeUtil with OS name: %s%s', platform.system(), |
| '(Mac)' if is_mac else '') |
| self._ide = _get_ide(installed_path, ide, config_reset, is_mac) |
| |
| def is_ide_installed(self): |
| """Checks if the IDE is already installed. |
| |
| Returns: |
| True if IDE is installed already, otherwise False. |
| """ |
| return self._ide.is_ide_installed() |
| |
| def launch_ide(self): |
| """Launches the relative IDE by opening the passed project file.""" |
| return self._ide.launch_ide() |
| |
| def config_ide(self, project_abspath): |
| """To config the IDE, e.g., setup code style, init SDK, and etc. |
| |
| Args: |
| project_abspath: An absolute path of the project. |
| """ |
| self._ide.project_abspath = project_abspath |
| if self.is_ide_installed() and self._ide: |
| self._ide.apply_optional_config() |
| |
| def get_default_path(self): |
| """Gets IDE default installed path.""" |
| return self._ide.default_installed_path |
| |
| def ide_name(self): |
| """Gets IDE name.""" |
| return self._ide.ide_name |
| |
| |
| class IdeBase: |
| """The most base class of IDE, provides interface and partial path init. |
| |
| Attributes: |
| _installed_path: String for the IDE binary path. |
| _config_reset: Boolean, True for reset configuration, else not reset. |
| _bin_file_name: String for IDE executable file name. |
| _bin_paths: A list of all possible IDE executable file absolute paths. |
| _ide_name: String for IDE name. |
| _bin_folders: A list of all possible IDE installed paths. |
| project_abspath: The absolute path of the project. |
| |
| For example: |
| 1. Check if IDE is installed. |
| 2. Launch IDE. |
| 3. Config IDE. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| self._installed_path = installed_path |
| self._config_reset = config_reset |
| self._ide_name = '' |
| self._bin_file_name = '' |
| self._bin_paths = [] |
| self._bin_folders = [] |
| self.project_abspath = '' |
| |
| def is_ide_installed(self): |
| """Checks if IDE is already installed. |
| |
| Returns: |
| True if IDE is installed already, otherwise False. |
| """ |
| return bool(self._installed_path) |
| |
| def launch_ide(self): |
| """Launches IDE by opening the passed project file.""" |
| _launch_ide(self.project_abspath, self._get_ide_cmd(), self._ide_name) |
| |
| def apply_optional_config(self): |
| """Handles IDE relevant configs.""" |
| # Default does nothing, the derived classes know what need to config. |
| |
| @property |
| def default_installed_path(self): |
| """Gets IDE default installed path.""" |
| return ' '.join(self._bin_folders) |
| |
| @property |
| def ide_name(self): |
| """Gets IDE name.""" |
| return self._ide_name |
| |
| def _get_ide_cmd(self): |
| """Compose launch IDE command to run a new process and redirect output. |
| |
| Returns: |
| A string of launch IDE command. |
| """ |
| return _get_run_ide_cmd(self._installed_path, self.project_abspath) |
| |
| def _init_installed_path(self, installed_path): |
| """Initialize IDE installed path. |
| |
| Args: |
| installed_path: the installed path to be checked. |
| """ |
| if installed_path: |
| self._installed_path = _get_script_from_input_path( |
| installed_path, self._bin_file_name) |
| else: |
| self._installed_path = self._get_script_from_system() |
| |
| def _get_script_from_system(self): |
| """Get correct IDE installed path from internal path. |
| |
| Returns: |
| The sh full path, or None if no IntelliJ version is installed. |
| """ |
| return _get_script_from_internal_path(self._bin_paths, self._ide_name) |
| |
| def _get_possible_bin_paths(self): |
| """Gets all possible IDE installed paths.""" |
| return [os.path.join(f, self._bin_file_name) for f in self._bin_folders] |
| |
| |
| class IdeIntelliJ(IdeBase): |
| """Provide basic IntelliJ ops, e.g., launch IDEA, and config IntelliJ. |
| |
| Class Attributes: |
| _JDK_PATH: The path of JDK in android project. |
| _IDE_JDK_TABLE_PATH: The path of JDK table which record JDK info in IDE. |
| _JDK_PART_TEMPLATE_PATH: The path of the template of partial JDK table. |
| _SYMBOLIC_VERSIONS: A string list of the symbolic link paths of |
| IntelliJ. |
| |
| For example: |
| 1. Check if IntelliJ is installed. |
| 2. Launch an IntelliJ. |
| 3. Config IntelliJ. |
| """ |
| |
| _JDK_PATH = '' |
| _IDE_JDK_TABLE_PATH = '' |
| _JDK_PART_TEMPLATE_PATH = '' |
| _DEFAULT_ANDROID_SDK_PATH = '' |
| _SYMBOLIC_VERSIONS = [] |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._ide_name = constant.IDE_INTELLIJ |
| self._ls_ce_path = '' |
| self._ls_ue_path = '' |
| self._init_installed_path(installed_path) |
| |
| def apply_optional_config(self): |
| """Do IDEA global config action. |
| |
| Run code style config, SDK config. |
| """ |
| if not self._installed_path: |
| return |
| # Skip config action if there's no config folder exists. |
| _path_list = self._get_config_root_paths() |
| if not _path_list: |
| return |
| |
| for _config_path in _path_list: |
| jdk_file = os.path.join(_config_path, self._IDE_JDK_TABLE_PATH) |
| jdk_table = sdk_config.SDKConfig( |
| jdk_file, self._JDK_PART_TEMPLATE_PATH, self._JDK_PATH, |
| self._DEFAULT_ANDROID_SDK_PATH) |
| jdk_table.config_jdk_file() |
| jdk_table.gen_enable_debugger_module(self.project_abspath) |
| |
| def _get_config_root_paths(self): |
| """Get the config root paths from derived class. |
| |
| Returns: |
| A string list of IDE config paths, return multiple paths if more |
| than one path are found, return None if no path is found. |
| """ |
| raise NotImplementedError() |
| |
| def _get_config_folder_name(self): |
| """Get the config sub folder name from derived class. |
| |
| Returns: |
| A string of the sub path for the config folder. |
| """ |
| raise NotImplementedError('Method overriding is needed.') |
| |
| def _get_preferred_version(self): |
| """Get users' preferred IntelliJ version. |
| |
| Locates the IntelliJ IDEA launch script path by following rule. |
| |
| 1. If config file recorded user's preference version, load it. |
| 2. If config file didn't record, search them form default path if there |
| are more than one version, ask user and record it. |
| |
| Returns: |
| The sh full path, or None if no IntelliJ version is installed. |
| """ |
| ce_paths = _get_intellij_version_path(self._ls_ce_path) |
| ue_paths = _get_intellij_version_path(self._ls_ue_path) |
| all_versions = self._get_all_versions(ce_paths, ue_paths) |
| if len(all_versions) > 1: |
| with config.AidegenConfig() as aconf: |
| if not self._config_reset and ( |
| aconf.preferred_version in all_versions): |
| return aconf.preferred_version |
| display_versions = self._merge_symbolic_version(all_versions) |
| preferred = _ask_preference(display_versions) |
| if preferred: |
| aconf.preferred_version = self._get_real_path(preferred) |
| return aconf.preferred_version |
| elif all_versions: |
| return all_versions[0] |
| return None |
| |
| @staticmethod |
| def _merge_symbolic_version(versions): |
| """Merge the duplicate version of symbolic links. |
| |
| Stable and beta versions are a symbolic link to a version. |
| This function combine two versions to one. |
| Ex: |
| ['/opt/intellij-ce-stable/bin/idea.sh', |
| '/opt/intellij-ce-2019.1/bin/idea.sh'] to |
| ['/opt/intellij-ce-stable/bin/idea.sh -> |
| /opt/intellij-ce-2019.1/bin/idea.sh', |
| '/opt/intellij-ce-2019.1/bin/idea.sh'] |
| |
| Args: |
| versions: A list of all installed versions. |
| |
| Returns: |
| A list of versions to show for user to select. It may contain |
| 'symbolic_path/idea.sh -> original_path/idea.sh'. |
| """ |
| display_versions = versions[:] |
| for symbolic_version in IdeIntelliJ._SYMBOLIC_VERSIONS: |
| if symbolic_version in display_versions and os.path.isfile( |
| symbolic_version): |
| real_path = os.path.realpath(symbolic_version) |
| for index, path in enumerate(display_versions): |
| if path == symbolic_version: |
| display_versions[index] = ' -> '.join( |
| [display_versions[index], real_path]) |
| break |
| return display_versions |
| |
| @staticmethod |
| def _get_real_path(path): |
| """ Get real path from merged path. |
| |
| Args: |
| path: A path string may be merged with symbolic path. |
| Returns: |
| The real IntelliJ installed path. |
| """ |
| return path.split()[0] |
| |
| def _get_script_from_system(self): |
| """Get correct IntelliJ installed path from internal path. |
| |
| Returns: |
| The sh full path, or None if no IntelliJ version is installed. |
| """ |
| found = self._get_preferred_version() |
| if found: |
| logging.debug('IDE internal installed path: %s.', found) |
| return found |
| |
| @staticmethod |
| def _get_all_versions(cefiles, uefiles): |
| """Get all versions of launch script files. |
| |
| Args: |
| cefiles: CE version launch script paths. |
| uefiles: UE version launch script paths. |
| |
| Returns: |
| A list contains all versions of launch script files. |
| """ |
| all_versions = [] |
| if cefiles: |
| all_versions.extend(cefiles) |
| if uefiles: |
| all_versions.extend(uefiles) |
| return all_versions |
| |
| @staticmethod |
| def _get_code_style_config(): |
| """Get Android build-in IntelliJ code style config file. |
| |
| Returns: |
| None if the file is not found, otherwise a full path string of |
| Intellij Android code style file. |
| """ |
| _config_source = os.path.join(common_util.get_android_root_dir(), |
| 'development', 'ide', 'intellij', |
| 'codestyles', 'AndroidStyle.xml') |
| |
| return _config_source if os.path.isfile(_config_source) else None |
| |
| |
| class IdeLinuxIntelliJ(IdeIntelliJ): |
| """Provide the IDEA behavior implementation for OS Linux. |
| |
| Class Attributes: |
| _INTELLIJ_RE: Regular expression of IntelliJ installed name in GLinux. |
| |
| For example: |
| 1. Check if IntelliJ is installed. |
| 2. Launch an IntelliJ. |
| 3. Config IntelliJ. |
| """ |
| |
| _JDK_PATH = os.path.join(common_util.get_android_root_dir(), |
| 'prebuilts/jdk/jdk8/linux-x86') |
| # TODO(b/127899277): Preserve a config for jdk version option case. |
| _IDE_JDK_TABLE_PATH = 'config/options/jdk.table.xml' |
| _JDK_PART_TEMPLATE_PATH = os.path.join( |
| common_util.get_aidegen_root_dir(), |
| 'templates/jdkTable/part.jdk.table.xml') |
| _DEFAULT_ANDROID_SDK_PATH = os.path.join(os.getenv('HOME'), 'Android/Sdk') |
| IdeIntelliJ._SYMBOLIC_VERSIONS = ['/opt/intellij-ce-stable/bin/idea.sh', |
| '/opt/intellij-ue-stable/bin/idea.sh', |
| '/opt/intellij-ce-beta/bin/idea.sh', |
| '/opt/intellij-ue-beta/bin/idea.sh'] |
| _INTELLIJ_RE = re.compile(r'intellij-(ce|ue)-') |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._bin_file_name = 'idea.sh' |
| self._bin_folders = ['/opt/intellij-*/bin'] |
| self._ls_ce_path = os.path.join('/opt/intellij-ce-*/bin', |
| self._bin_file_name) |
| self._ls_ue_path = os.path.join('/opt/intellij-ue-*/bin', |
| self._bin_file_name) |
| self._init_installed_path(installed_path) |
| |
| def _get_config_root_paths(self): |
| """To collect the global config folder paths of IDEA as a string list. |
| |
| The config folder of IntelliJ IDEA is under the user's home directory, |
| .IdeaIC20xx.x and .IntelliJIdea20xx.x are folder names for different |
| versions. |
| |
| Returns: |
| A string list for IDE config root paths, and return None for failed |
| to found case. |
| """ |
| if not self._installed_path: |
| return None |
| |
| _config_folders = [] |
| _config_folder = '' |
| if IdeLinuxIntelliJ._INTELLIJ_RE.search(self._installed_path): |
| # GLinux case. |
| if self._installed_path in IdeIntelliJ._SYMBOLIC_VERSIONS: |
| _path_data = os.path.realpath(self._installed_path).split('-') |
| else: |
| _path_data = self._installed_path.split('-') |
| _ide_version = _path_data[2].split(os.sep)[0] |
| if _path_data[1] == 'ce': |
| _config_folder = ''.join(['.IdeaIC', _ide_version]) |
| else: |
| _config_folder = ''.join(['.IntelliJIdea', _ide_version]) |
| |
| _config_folders.append( |
| os.path.join(os.getenv('HOME'), _config_folder)) |
| else: |
| # TODO(b/123459239): For the case that the user provides the IDEA |
| # binary path, we now collect all possible IDEA config root paths. |
| _config_folders = glob.glob( |
| os.path.join(os.getenv('HOME'), '.IdeaI?20*')) |
| _config_folders.extend( |
| glob.glob(os.path.join(os.getenv('HOME'), '.IntelliJIdea20*'))) |
| logging.info('The config path list: %s.', _config_folders) |
| |
| return _config_folders |
| |
| def _get_config_folder_name(self): |
| """A interface used to provide the config sub folder name. |
| |
| Returns: |
| A sub path string of the config folder. |
| """ |
| return os.path.join('config', 'codestyles') |
| |
| |
| class IdeMacIntelliJ(IdeIntelliJ): |
| """Provide the IDEA behavior implementation for OS Mac. |
| |
| For example: |
| 1. Check if IntelliJ is installed. |
| 2. Launch an IntelliJ. |
| 3. Config IntelliJ. |
| """ |
| |
| _JDK_PATH = os.path.join(common_util.get_android_root_dir(), |
| 'prebuilts/jdk/jdk8/darwin-x86') |
| _IDE_JDK_TABLE_PATH = 'options/jdk.table.xml' |
| _JDK_PART_TEMPLATE_PATH = os.path.join( |
| common_util.get_aidegen_root_dir(), |
| 'templates/jdkTable/part.mac.jdk.table.xml') |
| _DEFAULT_ANDROID_SDK_PATH = os.path.join( |
| os.getenv('HOME'), 'Library/Android/sdk') |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._bin_file_name = 'idea' |
| self._bin_folders = ['/Applications/IntelliJ IDEA.app/Contents/MacOS'] |
| self._bin_paths = self._get_possible_bin_paths() |
| self._ls_ce_path = os.path.join( |
| '/Applications/IntelliJ IDEA CE.app/Contents/MacOS', |
| self._bin_file_name) |
| self._ls_ue_path = os.path.join( |
| '/Applications/IntelliJ IDEA.app/Contents/MacOS', |
| self._bin_file_name) |
| self._init_installed_path(installed_path) |
| |
| def _get_config_root_paths(self): |
| """To collect the global config folder paths of IDEA as a string list. |
| |
| Returns: |
| A string list for IDE config root paths, and return None for failed |
| to found case. |
| """ |
| if not self._installed_path: |
| return None |
| |
| _config_folders = [] |
| if 'IntelliJ' in self._installed_path: |
| _config_folders = glob.glob( |
| os.path.join( |
| os.getenv('HOME'), 'Library/Preferences/IdeaI?20*')) |
| _config_folders.extend( |
| glob.glob( |
| os.path.join( |
| os.getenv('HOME'), |
| 'Library/Preferences/IntelliJIdea20*'))) |
| return _config_folders |
| |
| def _get_config_folder_name(self): |
| """A interface used to provide the config sub folder name. |
| |
| Returns: |
| A sub path string of the config folder. |
| """ |
| return 'codeStyles' |
| |
| |
| class IdeStudio(IdeBase): |
| """Class offers a set of Android Studio launching utilities. |
| |
| For example: |
| 1. Check if Android Studio is installed. |
| 2. Launch an Android Studio. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._ide_name = constant.IDE_ANDROID_STUDIO |
| |
| |
| class IdeLinuxStudio(IdeStudio): |
| """Class offers a set of Android Studio launching utilities for OS Linux. |
| |
| For example: |
| 1. Check if Android Studio is installed. |
| 2. Launch an Android Studio. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._bin_file_name = 'studio.sh' |
| self._bin_folders = ['/opt/android-*/bin'] |
| self._bin_paths = self._get_possible_bin_paths() |
| self._init_installed_path(installed_path) |
| |
| |
| class IdeMacStudio(IdeStudio): |
| """Class offers a set of Android Studio launching utilities for OS Mac. |
| |
| For example: |
| 1. Check if Android Studio is installed. |
| 2. Launch an Android Studio. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._bin_file_name = 'studio' |
| self._bin_folders = ['/Applications/Android Studio.app/Contents/MacOS'] |
| self._bin_paths = self._get_possible_bin_paths() |
| self._init_installed_path(installed_path) |
| |
| |
| class IdeEclipse(IdeBase): |
| """Class offers a set of Eclipse launching utilities. |
| |
| For example: |
| 1. Check if Eclipse is installed. |
| 2. Launch an Eclipse. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._ide_name = constant.IDE_ECLIPSE |
| self._bin_file_name = 'eclipse' |
| |
| def _get_script_from_system(self): |
| """Get correct IDE installed path from internal path. |
| |
| Remove any file with extension, the filename should be like, 'eclipse', |
| 'eclipse47' and so on, check if the file is executable and filter out |
| file such as 'eclipse.ini'. |
| |
| Returns: |
| The sh full path, or None if no IntelliJ version is installed. |
| """ |
| for ide_path in self._bin_paths: |
| # The binary name of Eclipse could be eclipse47, eclipse49, |
| # eclipse47_testing or eclipse49_testing. So finding the matched |
| # binary by /path/to/ide/eclipse*. |
| ls_output = glob.glob(ide_path + '*', recursive=True) |
| if ls_output: |
| ls_output = sorted(ls_output) |
| match_eclipses = [] |
| for path in ls_output: |
| if os.access(path, os.X_OK): |
| match_eclipses.append(path) |
| if match_eclipses: |
| match_eclipses = sorted(match_eclipses) |
| logging.debug('Result for checking %s after sort: %s.', |
| self._ide_name, match_eclipses[0]) |
| return match_eclipses[0] |
| logging.error('No %s installed.', self._ide_name) |
| return None |
| |
| |
| class IdeLinuxEclipse(IdeEclipse): |
| """Class offers a set of Eclipse launching utilities for OS Linux. |
| |
| For example: |
| 1. Check if Eclipse is installed. |
| 2. Launch an Eclipse. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._bin_folders = ['/opt/eclipse*', '/usr/bin/'] |
| self._bin_paths = self._get_possible_bin_paths() |
| self._init_installed_path(installed_path) |
| |
| |
| class IdeMacEclipse(IdeEclipse): |
| """Class offers a set of Eclipse launching utilities for OS Mac. |
| |
| For example: |
| 1. Check if Eclipse is installed. |
| 2. Launch an Eclipse. |
| """ |
| |
| def __init__(self, installed_path=None, config_reset=False): |
| super().__init__(installed_path, config_reset) |
| self._bin_file_name = 'eclipse' |
| self._bin_folders = [os.path.expanduser('~/eclipse/**')] |
| self._bin_paths = self._get_possible_bin_paths() |
| self._init_installed_path(installed_path) |
| |
| def _get_ide_cmd(self): |
| """Compose launch IDE command to run a new process and redirect output. |
| |
| Returns: |
| A string of launch IDE command. |
| """ |
| return ' '.join([ |
| self._installed_path.replace(' ', r'\ '), |
| os.path.dirname(self.project_abspath), _IGNORE_STD_OUT_ERR_CMD, '&' |
| ]) |
| |
| |
| def _get_script_from_internal_path(ide_paths, ide_name): |
| """Get the studio.sh script path from internal path. |
| |
| Args: |
| ide_paths: A list of IDE installed paths to be checked. |
| ide_name: The IDE name. |
| |
| Returns: |
| The IDE full path or None if no Android Studio or Eclipse is installed. |
| """ |
| for ide_path in ide_paths: |
| ls_output = glob.glob(ide_path, recursive=True) |
| ls_output = sorted(ls_output) |
| if ls_output: |
| logging.debug('Result for checking %s after sort: %s.', ide_name, |
| ls_output[0]) |
| return ls_output[0] |
| logging.error('No %s installed.', ide_name) |
| return None |
| |
| |
| def _run_ide_sh(run_sh_cmd, project_path): |
| """Run IDE launching script with an IntelliJ project path as argument. |
| |
| Args: |
| run_sh_cmd: The command to launch IDE. |
| project_path: The path of IntelliJ IDEA project content. |
| """ |
| assert run_sh_cmd, 'No suitable IDE installed.' |
| logging.debug('Run command: "%s" to launch project.', run_sh_cmd) |
| try: |
| subprocess.check_call(run_sh_cmd, shell=True) |
| except subprocess.CalledProcessError as err: |
| logging.error('Launch project path %s failed with error: %s.', |
| project_path, err) |
| |
| |
| def _walk_tree_find_ide_exe_file(top, ide_script_name): |
| """Recursively descend the directory tree rooted at top and filter out the |
| IDE executable script we need. |
| |
| Args: |
| top: the tree root to be checked. |
| ide_script_name: IDE file name such i.e. IdeIntelliJ._INTELLIJ_EXE_FILE. |
| |
| Returns: |
| the IDE executable script file(s) found. |
| """ |
| logging.info('Searching IDE script %s in path: %s.', ide_script_name, top) |
| for root, _, files in os.walk(top): |
| logging.debug('Search all files under %s to get %s, %s.', top, root, |
| files) |
| for file_ in fnmatch.filter(files, ide_script_name): |
| exe_file = os.path.join(root, file_) |
| if os.access(exe_file, os.X_OK): |
| logging.debug('Use file name filter to find %s in path %s.', |
| file_, exe_file) |
| yield exe_file |
| |
| |
| def _get_run_ide_cmd(sh_path, project_file): |
| """Get the command to launch IDE. |
| |
| Args: |
| sh_path: The idea.sh path where IDE is installed. |
| project_file: The path of IntelliJ IDEA project file. |
| |
| Returns: |
| A string: The IDE launching command. |
| """ |
| # In command usage, the space ' ' should be '\ ' for correctness. |
| return ' '.join([ |
| _NOHUP, |
| sh_path.replace(' ', r'\ '), project_file, _IGNORE_STD_OUT_ERR_CMD, '&' |
| ]) |
| |
| |
| def _get_script_from_file_path(input_path, ide_file_name): |
| """Get IDE executable script file from input file path. |
| |
| Args: |
| input_path: the file path to be checked. |
| ide_file_name: the IDE executable script file name. |
| |
| Returns: |
| An IDE executable script path if exists otherwise None. |
| """ |
| if os.path.basename(input_path).startswith(ide_file_name): |
| files_found = glob.glob(input_path) |
| if files_found: |
| return sorted(files_found)[0] |
| return None |
| |
| |
| def _get_script_from_dir_path(input_path, ide_file_name): |
| """Get an IDE executable script file from input directory path. |
| |
| Args: |
| input_path: the directory to be searched. |
| ide_file_name: the IDE executable script file name. |
| |
| Returns: |
| An IDE executable script path if exists otherwise None. |
| """ |
| logging.debug('Call _get_script_from_dir_path with %s, and %s', input_path, |
| ide_file_name) |
| files_found = list(_walk_tree_find_ide_exe_file(input_path, |
| ide_file_name + '*')) |
| if files_found: |
| return sorted(files_found)[0] |
| return None |
| |
| |
| def _launch_ide(project_path, run_ide_cmd, ide_name): |
| """Launches relative IDE by opening the passed project file. |
| |
| Args: |
| project_path: The full path of the IDE project content. |
| run_ide_cmd: The command to launch IDE. |
| ide_name: the IDE name is to be launched. |
| """ |
| assert project_path, 'Empty content path is not allowed.' |
| logging.info('Launch %s for project content path: %s.', ide_name, |
| project_path) |
| _run_ide_sh(run_ide_cmd, project_path) |
| |
| |
| def _is_intellij_project(project_path): |
| """Checks if the path passed in is an IntelliJ project content. |
| |
| Args: |
| project_path: The full path of IDEA project content, which contains |
| .idea folder and .iml file(s). |
| |
| Returns: |
| True if project_path is an IntelliJ project, False otherwise. |
| """ |
| if not os.path.isfile(project_path): |
| return os.path.isdir(project_path) and os.path.isdir( |
| os.path.join(project_path, _IDEA_FOLDER)) |
| |
| _, ext = os.path.splitext(os.path.basename(project_path)) |
| if ext and _IML_EXTENSION == ext.lower(): |
| path = os.path.dirname(project_path) |
| logging.debug('Extracted path is: %s.', path) |
| return os.path.isdir(os.path.join(path, _IDEA_FOLDER)) |
| return False |
| |
| |
| def _get_script_from_input_path(input_path, ide_file_name): |
| """Get correct IntelliJ executable script path from input path. |
| |
| 1. If input_path is a file, check if it is an IDE executable script file. |
| 2. It input_path is a directory, search if it contains IDE executable script |
| file(s). |
| |
| Args: |
| input_path: input path to be checked if it's an IDE executable |
| script. |
| ide_file_name: the IDE executable script file name. |
| |
| Returns: |
| IDE executable file(s) if exists otherwise None. |
| """ |
| if not input_path: |
| return None |
| ide_path = '' |
| if os.path.isfile(input_path): |
| ide_path = _get_script_from_file_path(input_path, ide_file_name) |
| if os.path.isdir(input_path): |
| ide_path = _get_script_from_dir_path(input_path, ide_file_name) |
| if ide_path: |
| logging.debug('IDE installed path from user input: %s.', ide_path) |
| return ide_path |
| return None |
| |
| |
| def _get_intellij_version_path(version_path): |
| """Locates the IntelliJ IDEA launch script path by version. |
| |
| Args: |
| version_path: IntelliJ CE or UE version launch script path. |
| |
| Returns: |
| The sh full path, or None if no such IntelliJ version is installed. |
| """ |
| ls_output = glob.glob(version_path, recursive=True) |
| if not ls_output: |
| return None |
| ls_output = sorted(ls_output, reverse=True) |
| logging.debug('Result for checking IntelliJ path %s after sorting:%s.', |
| version_path, ls_output) |
| return ls_output |
| |
| |
| def _ask_preference(all_versions): |
| """Ask users which version they prefer. |
| |
| Args: |
| all_versions: A list of all CE and UE version launch script paths. |
| |
| Returns: |
| An users selected version. |
| """ |
| options = [] |
| for i, sfile in enumerate(all_versions, 1): |
| options.append('\t{}. {}'.format(i, sfile)) |
| query = ('You installed {} versions of IntelliJ:\n{}\nPlease select ' |
| 'one.\t').format(len(all_versions), '\n'.join(options)) |
| return _select_intellij_version(query, all_versions) |
| |
| |
| def _select_intellij_version(query, all_versions): |
| """Select one from different IntelliJ versions users installed. |
| |
| Args: |
| query: The query message. |
| all_versions: A list of all CE and UE version launch script paths. |
| """ |
| all_numbers = [] |
| for i in range(len(all_versions)): |
| all_numbers.append(str(i + 1)) |
| input_data = input(query) |
| while input_data not in all_numbers: |
| input_data = input('Please select a number:\t') |
| return all_versions[int(input_data) - 1] |
| |
| |
| def _get_ide(installed_path=None, ide='j', config_reset=False, is_mac=False): |
| """Get IDE to be launched according to the ide input and OS type. |
| |
| Args: |
| installed_path: The IDE installed path to be checked. |
| ide: A key character of IDE to be launched. Default ide='j' is to |
| launch IntelliJ. |
| config_reset: A boolean, if true reset configuration data. |
| |
| Returns: |
| A corresponding IDE instance. |
| """ |
| if is_mac: |
| return _get_mac_ide(installed_path, ide, config_reset) |
| return _get_linux_ide(installed_path, ide, config_reset) |
| |
| |
| def _get_mac_ide(installed_path=None, ide='j', config_reset=False): |
| """Get IDE to be launched according to the ide input for OS Mac. |
| |
| Args: |
| installed_path: The IDE installed path to be checked. |
| ide: A key character of IDE to be launched. Default ide='j' is to |
| launch IntelliJ. |
| config_reset: A boolean, if true reset configuration data. |
| |
| Returns: |
| A corresponding IDE instance. |
| """ |
| if ide == 'e': |
| return IdeMacEclipse(installed_path) |
| if ide == 's': |
| return IdeMacStudio(installed_path) |
| return IdeMacIntelliJ(installed_path, config_reset) |
| |
| |
| def _get_linux_ide(installed_path=None, ide='j', config_reset=False): |
| """Get IDE to be launched according to the ide input for OS Linux. |
| |
| Args: |
| installed_path: The IDE installed path to be checked. |
| ide: A key character of IDE to be launched. Default ide='j' is to |
| launch IntelliJ. |
| config_reset: A boolean, if true reset configuration data. |
| |
| Returns: |
| A corresponding IDE instance. |
| """ |
| if ide == 'e': |
| return IdeLinuxEclipse(installed_path) |
| if ide == 's': |
| return IdeLinuxStudio(installed_path) |
| return IdeLinuxIntelliJ(installed_path, config_reset) |