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