• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4#
5# Copyright (c) 2023 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import os
20import sys
21
22from containers.arg import Arg
23from containers.arg import ModuleType
24from resolver.interface.args_resolver_interface import ArgsResolverInterface
25from modules.interface.indep_build_module_interface import IndepBuildModuleInterface
26from util.component_util import ComponentUtil
27from exceptions.ohos_exception import OHOSException
28from util.log_util import LogUtil
29from util.io_util import IoUtil
30import subprocess
31from distutils.spawn import find_executable
32from resources.global_var import COMPONENTS_PATH_DIR
33
34
35def get_part_name():
36    part_name_list = []
37    if len(sys.argv) > 2 and not sys.argv[2].startswith("-"):
38        for name in sys.argv[2:]:
39            if not name.startswith('-'):
40                part_name_list.append(name)
41            else:
42                break
43    return part_name_list
44
45
46def search_bundle_file_from_ccache(part_name: str) -> str:
47    if os.path.exists(COMPONENTS_PATH_DIR):
48        data = IoUtil.read_json_file(COMPONENTS_PATH_DIR)
49        if data.get(part_name):
50            return data.get(part_name)
51    return ""
52
53
54def _search_bundle_path(part_name: str) -> str:
55    bundle_path = None
56    try:
57        bundle_path = search_bundle_file_from_ccache(part_name)
58        if not bundle_path:
59            bundle_path = ComponentUtil.search_bundle_file(part_name)
60        else:
61            print(
62                "The bundle.json path of component {} is {}, if it's incorrect, please delete {} and try again. ".format(
63                    part_name, bundle_path, COMPONENTS_PATH_DIR))
64    except Exception as e:
65        raise OHOSException('Please check the bundle.json file of {} : {}'.format(part_name, e))
66    if not bundle_path:
67        print('ERROR argument "hb build <part_name>": Invalid part_name "{}". '.format(part_name))
68        sys.exit(1)
69    return bundle_path
70
71
72def rename_file(source_file, target_file):
73    try:
74        os.rename(source_file, target_file)
75    except FileNotFoundError as rename_error:
76        LogUtil.hb_warning(rename_error)
77
78
79class IndepBuildArgsResolver(ArgsResolverInterface):
80
81    def __init__(self, args_dict: dict):
82        super().__init__(args_dict)
83
84    @staticmethod
85    def resolve_target_cpu(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
86        build_executor = indep_build_module.hpm
87        arg_value = ""
88        if target_arg.arg_value:
89            arg_value = target_arg.arg_value
90        else:
91            args_dict = Arg.read_args_file(ModuleType.ENV)
92            arg_value = args_dict.get("target_cpu").get("argDefault")
93        build_executor.regist_flag('cpu', arg_value)
94        Arg.write_args_file("target_cpu", arg_value, ModuleType.INDEP_BUILD)
95
96    @staticmethod
97    def resolve_target_os(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
98        build_executor = indep_build_module.hpm
99        arg_value = ""
100        if target_arg.arg_value:
101            arg_value = target_arg.arg_value
102        else:
103            args_dict = Arg.read_args_file(ModuleType.ENV)
104            arg_value = args_dict.get("target_os").get("argDefault")
105        build_executor.regist_flag('os', arg_value)
106        Arg.write_args_file("target_os", arg_value, ModuleType.INDEP_BUILD)
107
108    @staticmethod
109    def resolve_part(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
110        '''
111        编译部件名获取优先级: hb build 指定的部件名参数  > hb build 在部件源码仓运行时通过找到bundle.json获取到的部件名 > hb env 设置的部件名参数
112        '''
113        hpm_executor = indep_build_module.hpm
114        indep_build_executor = indep_build_module.indep_build
115        target_arg.arg_value_list = get_part_name()
116        arg_value = ""
117
118        if target_arg.arg_value_list:
119            if hasattr(IndepBuildArgsResolver, "bundle_path_ccache"):
120                arg_value = IndepBuildArgsResolver.bundle_path_ccache
121            else:
122                bundle_path_list = []
123                print("collecting bundle.json, please wait")
124                for part_name in target_arg.arg_value_list:
125                    bundle_path = _search_bundle_path(part_name)
126                    bundle_path_list.append(bundle_path)
127                print("collect done")
128                arg_value = ','.join(bundle_path_list)
129                IndepBuildArgsResolver.bundle_path_ccache = arg_value
130        elif ComponentUtil.is_in_component_dir(os.getcwd()):
131            part_name, bundle_path = ComponentUtil.get_component(os.getcwd())
132            if part_name:
133                target_arg.arg_value_list = part_name
134                arg_value = bundle_path
135            else:
136                raise OHOSException('ERROR argument "no bundle.json": Invalid directory "{}". '.format(os.getcwd()))
137        else:
138            args_dict = Arg.read_args_file(ModuleType.ENV)
139            arg = args_dict.get("part")
140            if arg.get("argDefault"):
141                bundle_path = ComponentUtil.search_bundle_file(arg.get("argDefault"))
142                if not bundle_path:
143                    raise OHOSException('ERROR argument "hb env --part <part_name>": Invalid part_name "{}". '.format(
144                        target_arg.arg_value_list))
145                arg_value = bundle_path
146            else:
147                raise OHOSException('ERROR argument "hb build <part_name>": no part_name . ')
148        hpm_executor.regist_flag('path', arg_value)
149        indep_build_executor.regist_flag('path', arg_value)
150        Arg.write_args_file("part", arg_value, ModuleType.INDEP_BUILD)
151
152    @staticmethod
153    def resolve_variant(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
154        build_executor = indep_build_module.hpm
155        arg_value = ""
156        if target_arg.arg_value:
157            build_executor.regist_flag('defaultDeps', ComponentUtil.get_default_deps(target_arg.arg_value,
158                                                                                     True if '-t' in sys.argv else False))
159            arg_value = target_arg.arg_value
160        else:
161            build_executor.regist_flag('defaultDeps', ComponentUtil.get_default_deps("argDefault"))
162            args_dict = Arg.read_args_file(ModuleType.ENV)
163            arg_value = args_dict.get("variant").get("argDefault")
164
165        build_executor.regist_flag('variant', arg_value)
166        indep_build_module.indep_build.regist_flag('variant', arg_value)
167        Arg.write_args_file("variant", arg_value, ModuleType.INDEP_BUILD)
168
169    @staticmethod
170    def resolve_branch(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
171        build_executor = indep_build_module.hpm
172        arg_value = ""
173        if target_arg.arg_value:
174            arg_value = target_arg.arg_value
175        else:
176            args_dict = Arg.read_args_file(ModuleType.ENV)
177            arg_value = args_dict.get("branch").get("argDefault")
178        build_executor.regist_flag('branch', arg_value)
179        Arg.write_args_file("branch", arg_value, ModuleType.INDEP_BUILD)
180
181    @staticmethod
182    def resolve_build_type(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
183        arg_value = ""
184        if (sys.argv[1] == 'build' and
185                '-i' in sys.argv[3:] and
186                {'-t', '-test'} & set(sys.argv[3:])):
187            arg_value = "both"
188        elif (sys.argv[1] == 'build' and
189              '-i' not in sys.argv[3:] and
190              (sys.argv[-1] == "-t" or ("-t" in sys.argv and sys.argv[sys.argv.index("-t") + 1][0] == '-'))):
191            arg_value = "onlytest"
192        else:
193            arg_value = "onlysrc"
194            indep_build_module.indep_build.regist_flag("buildType", "onlysrc")
195        if arg_value != "onlysrc":
196            indep_build_module.hpm.regist_flag("buildType", arg_value)
197        indep_build_module.indep_build.regist_flag("buildType", arg_value)
198        Arg.write_args_file("build_type", arg_value, ModuleType.INDEP_BUILD)
199
200    @staticmethod
201    def resolve_keep_ninja_going(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
202        indep_build_module.indep_build.regist_flag('keep-ninja-going', target_arg.arg_value)
203
204    @staticmethod
205    def resolve_gn_args(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
206        indep_build_module.indep_build.regist_flag('gn-args', target_arg.arg_value)
207
208    @staticmethod
209    def resolve_skip_download(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
210        indep_build_module.hpm.regist_flag('skip-download', target_arg.arg_value)
211
212    @staticmethod
213    def resolve_build_target(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
214        indep_build_module.indep_build.regist_flag('build-target', target_arg.arg_value)
215
216    @staticmethod
217    def resolve_keep_out(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
218        indep_build_module.indep_build.regist_flag('keep-out', target_arg.arg_value)
219
220    @staticmethod
221    def resolve_ccache(target_arg: Arg, indep_build_module: IndepBuildModuleInterface):
222        # 检查是否启用了 ccache
223        if target_arg.arg_value:
224            # 查找 ccache 可执行文件的路径
225            ccache_path = find_executable('ccache')
226            if ccache_path is None:
227                LogUtil.hb_warning('Failed to find ccache, ccache disabled.')
228                return
229            else:
230                # 注册 ccache 启用标志
231                indep_build_module.indep_build.regist_arg(
232                    'ohos_build_enable_ccache', target_arg.arg_value)
233
234            # 设置缓存目录
235            ccache_local_dir = os.environ.get('CCACHE_LOCAL_DIR')
236            ccache_base = os.environ.get('CCACHE_BASE')
237            if not ccache_local_dir:
238                ccache_local_dir = '.ccache'
239            if not ccache_base:
240                ccache_base = os.environ.get('HOME')
241            ccache_base = os.path.join(ccache_base, ccache_local_dir)
242            if not os.path.exists(ccache_base):
243                os.makedirs(ccache_base, exist_ok=True)
244
245            # 日志文件处理
246            ccache_log_suffix = os.environ.get('CCACHE_LOG_SUFFIX')
247            if ccache_log_suffix:
248                logfile = os.path.join(
249                    ccache_base, "ccache.{}.log".format(ccache_log_suffix))
250            elif os.environ.get('CCACHE_LOGFILE'):
251                logfile = os.environ.get('CCACHE_LOGFILE')
252                if not os.path.exists(os.path.dirname(logfile)):
253                    os.makedirs(os.path.dirname(logfile), exist_ok=True)
254            else:
255                logfile = os.path.join(ccache_base, "ccache.log")
256            if os.path.exists(logfile):
257                oldfile = '{}.old'.format(logfile)
258                if os.path.exists(oldfile):
259                    os.unlink(oldfile)
260                rename_file(logfile, oldfile)
261            # 获取项目根目录
262            src_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
263            # 设置ccache相关环境变量
264            os.environ['CCACHE_EXEC'] = ccache_path
265            os.environ['CCACHE_LOGFILE'] = logfile
266            os.environ['USE_CCACHE'] = '1'
267            os.environ['CCACHE_DIR'] = ccache_base
268            os.environ['CCACHE_UMASK'] = '002'
269            os.environ['CCACHE_BASEDIR'] = src_root
270            ccache_max_size = os.environ.get('CCACHE_MAXSIZE')
271            if not ccache_max_size:
272                ccache_max_size = '100G'
273
274            # 构建设置 ccache 最大缓存大小的命令
275            cmd = ['ccache', '-M', ccache_max_size]
276            try:
277                subprocess.check_output(cmd, text=True)
278            except FileNotFoundError:
279                print("错误:找不到 ccache 命令")
280            except subprocess.CalledProcessError as e:
281                print(f"执行 ccache 命令失败: {e}")