• 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
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 traget
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 check.
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        targets: 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