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 fnmatch 24import os 25 26from aidegen import constant 27from aidegen.lib import clion_project_file_gen 28from aidegen.lib import common_util 29from aidegen.lib import native_module_info 30 31 32def generate_clion_projects(targets): 33 """Generates CLion projects by targets. 34 35 Generates base CLion project file in the common parent directory of the 36 targets. 37 38 Usages: 39 >>>aidegen frameworks/native/libs/ui frameworks/native/lib/gui 40 the base will be generated in, 41 out/development/ide/clion/frameworks/native/libs/ 42 >>>aidegen frameworks/native/libs/ui art/libnativeloader 43 the base will be generated in, 44 out/development/ide/clion/ 45 but we expect normally native devs rarely use it in this way. 46 47 Args: 48 targets: A list of targets to check and generate their native projects. 49 50 Returns: 51 A symbolic link CLion project file path. 52 """ 53 cc_module_info = native_module_info.NativeModuleInfo() 54 parent_dir, targets = _get_merged_native_target(cc_module_info, targets) 55 rel_path = os.path.relpath(parent_dir, common_util.get_android_root_dir()) 56 # If the relative path is Android root, we won't show '.' in the path. 57 if rel_path == '.': 58 rel_path = '' 59 module_names = [] 60 for target in targets: 61 mod_info = cc_module_info.get_module_info(target) 62 clion_gen = clion_project_file_gen.CLionProjectFileGenerator( 63 mod_info, rel_path) 64 clion_gen.generate_cmakelists_file() 65 module_names.append(mod_info[constant.KEY_MODULE_NAME]) 66 return clion_project_file_gen.generate_base_cmakelists_file( 67 cc_module_info, rel_path, module_names) 68 69 70def _find_parent(abs_path, current_parent): 71 """Finds parent directory of two directories. 72 73 Args: 74 abs_path: A string of an absolute path of a directory. 75 current_parent: A string of the absolute path of current parent 76 directory. It could be None int the beginning. 77 78 Returns: 79 A string of new parent directory. 80 """ 81 if not current_parent: 82 return abs_path 83 if common_util.is_source_under_relative_path(abs_path, current_parent): 84 return current_parent 85 if common_util.is_source_under_relative_path(current_parent, abs_path): 86 return abs_path 87 return _find_parent( 88 os.path.dirname(abs_path), os.path.dirname(current_parent)) 89 90 91def _filter_out_modules(targets, filter_func): 92 """Filters out target from targets if it passes the filter function. 93 94 Args: 95 targets: A list of targets to be analyzed. 96 filter_func: A filter function reference. 97 98 Returns: 99 A tuple of a list of filtered module target and a list of lefted 100 targets. 101 """ 102 jtargets = [] 103 lefts = [] 104 for target in targets: 105 if filter_func(target): 106 jtargets.append(target) 107 continue 108 lefts.append(target) 109 return jtargets, lefts 110 111 112def _get_merged_native_target(cc_module_info, targets): 113 """Gets merged native parent target from original native targets. 114 115 If a target is a module, we put it directly into the new list. If a traget 116 is a path we put all the native modules under the path into the new list. 117 118 Args: 119 cc_module_info: A ModuleInfo instance contains the data of 120 module_bp_cc_deps.json. 121 targets: A list of targets to be merged. 122 123 Returns: 124 A tuple of a string of merged native project's relative path and a list 125 of new targets we have dealt with. 126 """ 127 parent_folder = None 128 new_targets = [] 129 for target in targets: 130 _, abs_path = common_util.get_related_paths(cc_module_info, target) 131 parent_folder = _find_parent(abs_path, parent_folder) 132 if cc_module_info.is_module(target): 133 new_targets.append(target) 134 continue 135 mod_names = cc_module_info.get_module_names_in_targets_paths([target]) 136 new_targets.extend(mod_names) 137 return parent_folder, new_targets 138 139 140def get_native_and_java_projects(atest_module_info, cc_module_info, targets): 141 """Gets native and java projects from targets. 142 143 Separates native and java projects from targets. 144 1. If it's a native module, add it to native projects. 145 2. If it's a java module, add it to java projects. 146 3. Calls _analyze_native_and_java_projects to analyze the remaining targets. 147 148 Args: 149 atest_module_info: A ModuleInfo instance contains the merged data of 150 module-info.json and module_bp_java_deps.json. 151 cc_module_info: A ModuleInfo instance contains the data of 152 module_bp_cc_deps.json. 153 targets: A list of targets to be analyzed. 154 155 Returns: 156 A tuple of a list of java build targets and a list of native build 157 targets. 158 """ 159 ctargets, lefts = _filter_out_modules(targets, cc_module_info.is_module) 160 jtargets, lefts = _filter_out_modules(lefts, atest_module_info.is_module) 161 path_info = cc_module_info.path_to_module_info 162 jtars, ctars = _analyze_native_and_java_projects( 163 atest_module_info, path_info, lefts) 164 ctargets.extend(ctars) 165 jtargets.extend(jtars) 166 return jtargets, ctargets 167 168 169def _analyze_native_and_java_projects(atest_module_info, path_info, targets): 170 """Analyzes native and java projects from targets. 171 172 Args: 173 atest_module_info: A ModuleInfo instance contains the merged data of 174 module-info.json and module_bp_java_deps.json. 175 path_info: A dictionary contains native projects' path as key 176 and module's info dictionary as value. 177 targets: A list of targets to be analyzed. 178 179 Returns: 180 A tuple of a list of java build targets and a list of native build 181 targets. 182 """ 183 jtargets = [] 184 ctargets = [] 185 for target in targets: 186 rel_path, abs_path = common_util.get_related_paths( 187 atest_module_info, target) 188 if _check_java_file_exists(abs_path): 189 jtargets.append(target) 190 if _check_native_project_exists(path_info, rel_path): 191 ctargets.append(target) 192 return jtargets, ctargets 193 194 195def _check_java_file_exists(abs_path): 196 """Checks if any Java files exist in an abs_path directory. 197 198 Args: 199 abs_path: A string of absolute path of a directory to be check. 200 201 Returns: 202 True if any Java files exist otherwise False. 203 """ 204 for _, _, filenames in os.walk(abs_path): 205 if fnmatch.filter(filenames, constant.JAVA_FILES): 206 return True 207 return False 208 209 210def _check_native_project_exists(path_to_module_info, rel_path): 211 """Checks if any native project exists in a rel_path directory. 212 213 Args: 214 path_to_module_info: A dictionary contains data of relative path as key 215 and module info dictionary as value. 216 rel_path: A string of relative path of a directory to be check. 217 218 Returns: 219 True if any native project exists otherwise False. 220 """ 221 for path in path_to_module_info: 222 if common_util.is_source_under_relative_path(path, rel_path): 223 return True 224 return False 225