1#!/usr/bin/env python3
2#
3# Copyright 2020 - 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"""It is an AIDEGen sub task: generate VSCode related config files."""
18
19import logging
20import os
21
22from aidegen import constant
23from aidegen.lib import common_util
24from aidegen.lib import native_module_info
25from atest import constants
26
27_C_CPP_PROPERTIES_CONFIG_FILE_NAME = 'c_cpp_properties.json'
28_ENV = 'env'
29_MY_DEFAULT_INCLUDE_PATH = 'myDefaultIncludePath'
30_MY_COMPILER_PATH = 'myCompilerPath'
31_CONFIG = 'configurations'
32_NAME = 'name'
33_LINUX = 'Linux'
34_INTELL_SENSE = 'intelliSenseMode'
35_GCC_X64 = 'clang-x64'
36_INC_PATH = 'includePath'
37_SYS_INC_PATH = 'systemIncludePath'
38_COMPILE_PATH = 'compilerPath'
39_C_STANDARD = 'cStandard'
40_C_11 = 'c11'
41_CPP_STANDARD = 'cppStandard'
42_CPP_17 = 'c++17'
43_COMPILE_CMD = 'compileCommands'
44_COMPILE_COMMANDS_FILE_DIR = 'out/soong/development/ide/compdb'
45_COMPILE_COMMANDS_FILE_NAME = 'compile_commands.json'
46_BROWSE = 'browse'
47_PATH = 'path'
48_WORKSPACE_DIR = 'workspaceFolder'
49_LIMIT_SYM = 'limitSymbolsToIncludedHeaders'
50_DB_FILE_NAME = 'databaseFilename'
51_COMPILER_PATH = '/usr/bin/gcc'
52_COMPILER_EMPTY = '"compilerPath" is empty and will skip querying a compiler.'
53_FALSE = 'false'
54_INTELI_SENSE_ENGINE = 'C_Cpp.intelliSenseEngine'
55_DEFAULT = 'Default'
56
57
58class VSCodeNativeProjectFileGenerator:
59    """VSCode native project file generator.
60
61    Attributes:
62        mod_dir: A string of native project path.
63        config_dir: A string of native project's configuration path.
64    """
65
66    def __init__(self, mod_dir):
67        """VSCodeNativeProjectFileGenerator initialize.
68
69        Args:
70            mod_dir: An absolute path of native project directory.
71        """
72        self.mod_dir = mod_dir
73        config_dir = os.path.join(mod_dir, constant.VSCODE_CONFIG_DIR)
74        if not os.path.isdir(config_dir):
75            os.mkdir(config_dir)
76        self.config_dir = config_dir
77
78    def generate_c_cpp_properties_json_file(self):
79        """Generates c_cpp_properties.json file for VSCode project."""
80        native_mod_info = native_module_info.NativeModuleInfo()
81        root_dir = common_util.get_android_root_dir()
82        mod_names = native_mod_info.get_module_names_in_targets_paths(
83            [os.path.relpath(self.mod_dir, root_dir)])
84        data = self._create_c_cpp_properties_dict(native_mod_info, mod_names)
85        common_util.dump_json_dict(
86            os.path.join(self.config_dir, _C_CPP_PROPERTIES_CONFIG_FILE_NAME),
87            data)
88
89    def _create_c_cpp_properties_dict(self, native_mod_info, mod_names):
90        """Creates the dictionary of 'c_cpp_properties.json' file.
91
92        Args:
93            native_mod_info: An instance of native_module_info.NativeModuleInfo
94                             class.
95            mod_names: A list of module names.
96
97        Returns:
98            A dictionary contains the formats of c_cpp_properties.json file.
99        """
100        configs = {}
101        configs[_NAME] = _LINUX
102        includes = set()
103        for mod_name in mod_names:
104            includes.update(native_mod_info.get_module_includes(mod_name))
105        browse = {_LIMIT_SYM: _FALSE, _INTELI_SENSE_ENGINE: _DEFAULT}
106        if includes:
107            paths = _make_header_file_paths(includes)
108            configs[_INC_PATH] = paths
109            browse[_PATH] = paths
110        configs[_COMPILE_PATH] = ''
111        if os.path.isfile(_COMPILER_PATH):
112            configs[_COMPILE_PATH] = _COMPILER_PATH
113        if not configs[_COMPILE_PATH]:
114            logging.warning(_COMPILER_EMPTY)
115        configs[_C_STANDARD] = _C_11
116        configs[_CPP_STANDARD] = _CPP_17
117        root_var = _make_var(constants.ANDROID_BUILD_TOP)
118        configs[_COMPILE_CMD] = os.path.join(
119            root_var, _COMPILE_COMMANDS_FILE_DIR, _COMPILE_COMMANDS_FILE_NAME)
120        configs[_BROWSE] = browse
121        configs[_INTELL_SENSE] = _GCC_X64
122        data = {}
123        data[_CONFIG] = [configs]
124        return data
125
126
127def _make_var(text):
128    """Adds '${}' to make the text become a variable.
129
130    Args:
131        text: A string of text to be added '${}'.
132
133    Returns:
134        A string after adding '${}'.
135    """
136    return ''.join(['${', text, '}'])
137
138
139def _make_header_file_paths(paths):
140    """Adds prefix '${ANDROID_BUILD_TOP}' and suffix '**/*.h' to a path.
141
142    Args:
143        paths: An iterative set of relative paths' strings.
144
145    Returns:
146        A list of new paths after adding prefix and suffix.
147    """
148    header_list = []
149    for path in paths:
150        header_list.append(os.path.join(
151            _make_var(constants.ANDROID_BUILD_TOP), path))
152    return header_list
153