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"""native_util 18 19This module has a collection of functions that provide helper functions for 20launching native projects in IDE. 21""" 22 23import os 24 25from aidegen import constant 26from aidegen.lib import clion_project_file_gen 27from aidegen.lib import common_util 28from aidegen.lib import native_module_info 29from aidegen.lib import project_info 30 31_RUST_JSON_NOT_EXIST = 'The json file: {} does not exist.' 32_RUST_DICT_BROKEN = 'The rust dictionary does not have "{}" key. It\'s broken.' 33_CRATES_KEY = 'crates' 34_ROOT_MODULE = 'root_module' 35_DISPLAY_NAME = 'display_name' 36 37 38def generate_clion_projects(targets): 39 """Generates CLion projects by targets. 40 41 Generates base CLion project file in the common parent directory of the 42 targets. 43 44 Usages: 45 >>>aidegen frameworks/native/libs/ui frameworks/native/lib/gui 46 the base will be generated in, 47 out/development/ide/clion/frameworks/native/libs/ 48 >>>aidegen frameworks/native/libs/ui art/libnativeloader 49 the base will be generated in, 50 out/development/ide/clion/ 51 but we expect normally native devs rarely use it in this way. 52 53 Args: 54 targets: A list of targets to check and generate their native projects. 55 56 Returns: 57 A symbolic link CLion project file path. 58 """ 59 cc_module_info = native_module_info.NativeModuleInfo() 60 parent_dir, targets = _get_merged_native_target(cc_module_info, targets) 61 rel_path = os.path.relpath(parent_dir, common_util.get_android_root_dir()) 62 # If the relative path is Android root, we won't show '.' in the path. 63 if rel_path == '.': 64 rel_path = '' 65 module_names = [] 66 for target in targets: 67 mod_info = cc_module_info.get_module_info(target) 68 clion_gen = clion_project_file_gen.CLionProjectFileGenerator( 69 mod_info, rel_path) 70 clion_gen.generate_cmakelists_file() 71 module_names.append(mod_info[constant.KEY_MODULE_NAME]) 72 return clion_project_file_gen.generate_base_cmakelists_file( 73 cc_module_info, rel_path, module_names) 74 75 76def _find_parent(abs_path, current_parent): 77 """Finds parent directory of two directories. 78 79 Args: 80 abs_path: A string of an absolute path of a directory. 81 current_parent: A string of the absolute path of current parent 82 directory. It could be None int the beginning. 83 84 Returns: 85 A string of new parent directory. 86 """ 87 if not current_parent: 88 return abs_path 89 if common_util.is_source_under_relative_path(abs_path, current_parent): 90 return current_parent 91 if common_util.is_source_under_relative_path(current_parent, abs_path): 92 return abs_path 93 return _find_parent( 94 os.path.dirname(abs_path), os.path.dirname(current_parent)) 95 96 97def _filter_out_modules(targets, filter_func): 98 """Filters out target from targets if it passes the filter function. 99 100 Args: 101 targets: A list of targets to be analyzed. 102 filter_func: A filter function reference. 103 104 Returns: 105 A tuple of a list of filtered module target and a list of lefted 106 targets. 107 """ 108 jtargets = [] 109 lefts = [] 110 for target in targets: 111 if filter_func(target): 112 jtargets.append(target) 113 continue 114 lefts.append(target) 115 return jtargets, lefts 116 117 118def _get_merged_native_target(cc_module_info, targets): 119 """Gets merged native parent target from original native targets. 120 121 If a target is a module, we put it directly into the new list. If a target 122 is a path we put all the native modules under the path into the new list. 123 124 Args: 125 cc_module_info: A ModuleInfo instance contains the data of 126 module_bp_cc_deps.json. 127 targets: A list of targets to be merged. 128 129 Returns: 130 A tuple of a string of merged native project's relative path and a list 131 of new targets we have dealt with. 132 """ 133 parent_folder = None 134 new_targets = [] 135 for target in targets: 136 _, abs_path = common_util.get_related_paths(cc_module_info, target) 137 parent_folder = _find_parent(abs_path, parent_folder) 138 if cc_module_info.is_module(target): 139 new_targets.append(target) 140 continue 141 mod_names = cc_module_info.get_module_names_in_targets_paths([target]) 142 new_targets.extend(mod_names) 143 return parent_folder, new_targets 144 145 146def get_java_cc_and_rust_projects(atest_module_info, cc_module_info, targets): 147 """Gets native and java projects from targets. 148 149 Separates native and java projects from targets. 150 1. If it's a native module, add it to native projects. 151 2. If it's a java module, add it to java projects. 152 3. If it's a rust module, add it to rust targets. 153 154 Args: 155 atest_module_info: A ModuleInfo instance contains the merged data of 156 module-info.json and module_bp_java_deps.json. 157 cc_module_info: A ModuleInfo instance contains the data of 158 module_bp_cc_deps.json. 159 targets: A list of targets to be analyzed. 160 161 Returns: 162 A tuple of a list of java build targets, a list of C/C++ build 163 targets and a list of rust build targets. 164 """ 165 rtargets = _filter_out_rust_projects(targets) 166 ctargets, lefts = _filter_out_modules(targets, cc_module_info.is_module) 167 jtargets, lefts = _filter_out_modules(lefts, atest_module_info.is_module) 168 path_info = cc_module_info.path_to_module_info 169 jtars, ctars = _analyze_native_and_java_projects( 170 atest_module_info, path_info, lefts) 171 ctargets.extend(ctars) 172 jtargets.extend(jtars) 173 return jtargets, ctargets, rtargets 174 175 176def _analyze_native_and_java_projects(atest_module_info, path_info, targets): 177 """Analyzes native and java projects from targets. 178 179 Args: 180 atest_module_info: A ModuleInfo instance contains the merged data of 181 module-info.json and module_bp_java_deps.json. 182 path_info: A dictionary contains C/C++ projects' path as key 183 and module's info dictionary as value. 184 targets: A list of targets to be analyzed. 185 186 Returns: 187 A tuple of a list of java build targets and a list of C/C++ build 188 targets. 189 """ 190 jtargets = [] 191 ctargets = [] 192 for target in targets: 193 rel_path, abs_path = common_util.get_related_paths( 194 atest_module_info, target) 195 if common_util.check_java_or_kotlin_file_exists(abs_path): 196 jtargets.append(target) 197 if _check_native_project_exists(path_info, rel_path): 198 ctargets.append(target) 199 return jtargets, ctargets 200 201 202def _check_native_project_exists(path_to_module_info, rel_path): 203 """Checks if any C/C++ project exists in a rel_path directory. 204 205 Args: 206 path_to_module_info: A dictionary contains data of relative path as key 207 and module info dictionary as value. 208 rel_path: A string of relative path of a directory to be checked. 209 210 Returns: 211 True if any C/C++ project exists otherwise False. 212 """ 213 for path in path_to_module_info: 214 if common_util.is_source_under_relative_path(path, rel_path): 215 return True 216 return False 217 218 219def _filter_out_rust_projects(targets): 220 """Filters out if the input targets contain any Rust project. 221 222 Args: 223 targets: A list of targets to be checked. 224 225 Returns: 226 A list of Rust projects. 227 """ 228 root_dir = common_util.get_android_root_dir() 229 rust_project_json = os.path.join( 230 root_dir, 231 common_util.get_blueprint_json_path(constant.RUST_PROJECT_JSON)) 232 if not os.path.isfile(rust_project_json): 233 message = _RUST_JSON_NOT_EXIST.format(rust_project_json) 234 print(constant.WARN_MSG.format( 235 common_util.COLORED_INFO('Warning:'), message)) 236 return None 237 rust_dict = common_util.get_json_dict(rust_project_json) 238 if _CRATES_KEY not in rust_dict: 239 message = _RUST_DICT_BROKEN.format(_CRATES_KEY) 240 print(constant.WARN_MSG.format( 241 common_util.COLORED_INFO('Warning:'), message)) 242 return None 243 return _get_rust_targets(targets, rust_dict[_CRATES_KEY], root_dir) 244 245 246def _get_rust_targets(targets, rust_modules_info, root_dir): 247 """Gets Rust targets by checking input targets with a rust info dictionary. 248 249 Collects targets' relative rust modules and rebuild them. 250 251 Args: 252 targets: A list of targets to be checked. 253 rust_modules_info: A list of the Android Rust modules info. 254 root_dir: A string of the Android root directory. 255 256 Returns: 257 A list of Rust targets. 258 """ 259 rtargets = set() 260 rebuild_targets = set() 261 for target in targets: 262 # The Rust project can be expressed only in the path but not the module 263 # right now. 264 rel_target = _get_relative_path(target, root_dir) 265 if not os.path.isdir(os.path.join(root_dir, rel_target)): 266 continue 267 for mod_info in rust_modules_info: 268 if _ROOT_MODULE not in mod_info or _DISPLAY_NAME not in mod_info: 269 continue 270 path = mod_info[_ROOT_MODULE] 271 if common_util.is_source_under_relative_path(path, target): 272 rtargets.add(target) 273 if _is_target_relative_module(path, rel_target): 274 rebuild_targets.add(mod_info[_DISPLAY_NAME]) 275 project_info.batch_build_dependencies(rebuild_targets) 276 return list(rtargets) 277 278 279def _get_relative_path(target, root_dir): 280 """Gets a target's relative path from root and if it needs to add os sep. 281 282 Args: 283 target: A string of a target's path to be checked. 284 root_dir: A string of the Android root directory. 285 286 Returns: 287 A string of the relative path of target to root_dir. 288 """ 289 if target == '.': 290 target = os.getcwd() 291 return os.path.relpath(target, root_dir) 292 293 294def _is_target_relative_module(path, target): 295 """Checks if a module's path is contains a rust target. 296 297 Args: 298 path: A string of a module's path to be checked. 299 target: A string of a target's path without os.sep in the end. 300 301 Returns: 302 A boolean of True if path contains the path of target otherwise False. 303 """ 304 return target == path or target + os.sep in path 305