1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Project config class.""" 18 19import os 20 21from aidegen import constant 22from aidegen.lib import common_util 23from aidegen.lib import errors 24 25SKIP_BUILD_INFO = ('If you are sure the related modules and dependencies have ' 26 'been already built, please try to use command {} to skip ' 27 'the building process.') 28_SKIP_BUILD_CMD = 'aidegen {} -s' 29_SKIP_BUILD_WARN = ( 30 'You choose "--skip-build". Skip building jar and module might increase ' 31 'the risk of the absence of some jar or R/AIDL/logtags java files and ' 32 'cause the red lines to appear in IDE tool.') 33_INSTANCE_NOT_EXIST_ERROR = ('The instance of {} does not exist. Please ' 34 'initialize it before using.') 35 36 37class ProjectConfig(): 38 """A singleton class manages AIDEGen's configurations. 39 40 ProjectConfig is a singleton class that can be accessed in other modules. 41 42 Usage: 43 1. Main module should do it once by instantiating a ProjectConfig with 44 users' input arguments and calling init_environment(). 45 args = aidegen_main.main(sys.argv[1:]) 46 project_config.ProjectConfig(args).init_environment() 47 2. All others can get the ProjectConfig instance by calling 48 get_instance(). 49 project_config.ProjectConfig.get_instance() 50 51 Class attributes: 52 _instance: A singleton instance of ProjectConfig. 53 54 Attributes: 55 ide_name: The IDE name which users prefer to launch. 56 is_launch_ide: A boolean for launching IDE in the end of AIDEGen. 57 depth: The depth of module referenced by source. 58 full_repo: A boolean decides import whole Android source repo. 59 is_skip_build: A boolean decides skipping building jars or modules. 60 targets: A string list with Android module names or paths. 61 verbose: A boolean. If true, display DEBUG level logs. 62 ide_installed_path: A string of IDE installed path. 63 config_reset: A boolean if true to reset all saved configurations. 64 atest_module_info: A ModuleInfo instance. 65 language: The programming language users prefer to deal with. 66 """ 67 68 _instance = None 69 70 def __init__(self, args): 71 """ProjectConfig initialize. 72 73 Args: 74 An argparse.Namespace object holds parsed args. 75 """ 76 self.language, self.ide_name = common_util.determine_language_ide( 77 args.language[0], args.ide[0]) 78 self.is_launch_ide = not args.no_launch 79 self.depth = args.depth 80 self.full_repo = args.android_tree 81 self.is_skip_build = args.skip_build 82 self.targets = args.targets.copy() 83 self.verbose = args.verbose 84 self.ide_installed_path = args.ide_installed_path 85 self.config_reset = args.config_reset 86 self.exclude_paths = args.exclude_paths 87 self.atest_module_info = None 88 ProjectConfig._instance = self 89 90 def init_environment(self): 91 """Initialize the environment settings for the whole project.""" 92 self._show_skip_build_msg() 93 # TODO(b/159078170): Avoid CLion IDE case for now, we should avoid 94 # Android Studio's native project's case in the future. 95 targets = self.targets if self.language == constant.JAVA else None 96 self.atest_module_info = common_util.get_atest_module_info(targets) 97 self.exclude_paths = _transform_exclusive_paths( 98 self.atest_module_info, self.exclude_paths) 99 self.targets = _check_whole_android_tree(self.targets, self.full_repo) 100 self.full_repo = (self.targets[0] == constant.WHOLE_ANDROID_TREE_TARGET) 101 102 def _show_skip_build_msg(self): 103 """Display different messages if users skip building targets or not.""" 104 if self.is_skip_build: 105 print('\n{} {}\n'.format( 106 common_util.COLORED_INFO('Warning:'), _SKIP_BUILD_WARN)) 107 else: 108 msg = SKIP_BUILD_INFO.format( 109 common_util.COLORED_INFO( 110 _SKIP_BUILD_CMD.format(' '.join(self.targets)))) 111 print('\n{} {}\n'.format(common_util.COLORED_INFO('INFO:'), msg)) 112 113 @classmethod 114 def get_instance(cls): 115 """Get a singleton's instance. 116 117 Returns: 118 A singleton instance of ProjectConfig. 119 120 Raises: 121 An exception of errors.InstanceNotExistError if users didn't 122 instantiate a ProjectConfig object before calling this method. 123 """ 124 if not cls._instance: 125 raise errors.InstanceNotExistError( 126 _INSTANCE_NOT_EXIST_ERROR.format(str(cls))) 127 return cls._instance 128 129 130def _check_whole_android_tree(targets, android_tree): 131 """Check if it's a building project file for the whole Android tree. 132 133 The rules: 134 1. If users command aidegen under Android root, e.g., 135 root$ aidegen 136 that implies users would like to launch the whole Android tree, AIDEGen 137 should set the flag android_tree True. 138 2. If android_tree is True, add whole Android tree to the project. 139 140 Args: 141 targets: A list of targets to be imported. 142 android_tree: A boolean, True if it's a whole Android tree case, 143 otherwise False. 144 145 Returns: 146 A list of targets to be built. 147 """ 148 if common_util.is_android_root(os.getcwd()) and targets == ['']: 149 return [constant.WHOLE_ANDROID_TREE_TARGET] 150 new_targets = targets.copy() 151 if android_tree: 152 new_targets.insert(0, constant.WHOLE_ANDROID_TREE_TARGET) 153 return new_targets 154 155 156def is_whole_android_tree(targets, android_tree): 157 """Checks is AIDEGen going to process whole android tree. 158 159 Args: 160 targets: A list of targets to be imported. 161 android_tree: A boolean, True if it's a whole Android tree case, 162 otherwise False. 163 Returns: 164 A boolean, True when user is going to import whole Android tree. 165 """ 166 return (android_tree or 167 (common_util.is_android_root(os.getcwd()) and targets == [''])) 168 169 170def _transform_exclusive_paths(atest_module_info, exclude_paths): 171 """Transforms exclusive paths to relative paths. 172 173 Args: 174 exclude_paths: A list of strings of exclusive paths. 175 176 Returns: 177 A list of relative paths. 178 """ 179 if not exclude_paths: 180 return None 181 excludes = [] 182 for path in exclude_paths: 183 exclude_path, _ = common_util.get_related_paths(atest_module_info, path) 184 excludes.append(exclude_path) 185 return excludes 186