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 LogUtil.hb_info( 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 files you updated : {}'.format(e)) 66 if not bundle_path: 67 LogUtil.hb_info('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 LogUtil.hb_info("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 LogUtil.hb_info("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_gn_flags(target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 210 indep_build_module.indep_build.regist_flag('gn-flags', target_arg.arg_value) 211 212 @staticmethod 213 def resolve_ninja_args(target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 214 indep_build_module.indep_build.regist_flag('ninja-args', target_arg.arg_value) 215 216 @staticmethod 217 def resolve_skip_download(target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 218 indep_build_module.hpm.regist_flag('skip-download', target_arg.arg_value) 219 indep_build_module.indep_build.regist_flag('skip-download', target_arg.arg_value) 220 221 @staticmethod 222 def resolve_build_target(target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 223 indep_build_module.indep_build.regist_flag('build-target', target_arg.arg_value) 224 225 @staticmethod 226 def resolve_fast_rebuild(target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 227 indep_build_module.indep_build.regist_flag('fast-rebuild', target_arg.arg_value) 228 indep_build_module.hpm.regist_flag('fast-rebuild', target_arg.arg_value) 229 230 @staticmethod 231 def resolve_ccache(target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 232 # 检查是否启用了 ccache 233 if target_arg.arg_value: 234 # 查找 ccache 可执行文件的路径 235 ccache_path = find_executable('ccache') 236 if ccache_path is None: 237 LogUtil.hb_warning('Failed to find ccache, ccache disabled.') 238 return 239 else: 240 # 注册 ccache 启用标志 241 indep_build_module.indep_build.regist_arg( 242 'ohos_build_enable_ccache', target_arg.arg_value) 243 244 # 设置缓存目录 245 ccache_local_dir = os.environ.get('CCACHE_LOCAL_DIR') 246 ccache_base = os.environ.get('CCACHE_BASE') 247 if not ccache_local_dir: 248 ccache_local_dir = '.ccache' 249 if not ccache_base: 250 ccache_base = os.environ.get('HOME') 251 ccache_base = os.path.join(ccache_base, ccache_local_dir) 252 if not os.path.exists(ccache_base): 253 os.makedirs(ccache_base, exist_ok=True) 254 255 # 日志文件处理 256 ccache_log_suffix = os.environ.get('CCACHE_LOG_SUFFIX') 257 if ccache_log_suffix: 258 logfile = os.path.join( 259 ccache_base, "ccache.{}.log".format(ccache_log_suffix)) 260 elif os.environ.get('CCACHE_LOGFILE'): 261 logfile = os.environ.get('CCACHE_LOGFILE') 262 if not os.path.exists(os.path.dirname(logfile)): 263 os.makedirs(os.path.dirname(logfile), exist_ok=True) 264 else: 265 logfile = os.path.join(ccache_base, "ccache.log") 266 if os.path.exists(logfile): 267 oldfile = '{}.old'.format(logfile) 268 if os.path.exists(oldfile): 269 os.unlink(oldfile) 270 rename_file(logfile, oldfile) 271 # 获取项目根目录 272 src_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) 273 # 设置ccache相关环境变量 274 os.environ['CCACHE_EXEC'] = ccache_path 275 os.environ['CCACHE_LOGFILE'] = logfile 276 os.environ['USE_CCACHE'] = '1' 277 os.environ['CCACHE_DIR'] = ccache_base 278 os.environ['CCACHE_UMASK'] = '002' 279 os.environ['CCACHE_BASEDIR'] = src_root 280 ccache_max_size = os.environ.get('CCACHE_MAXSIZE') 281 if not ccache_max_size: 282 ccache_max_size = '100G' 283 284 # 构建设置 ccache 最大缓存大小的命令 285 cmd = ['ccache', '-M', ccache_max_size] 286 try: 287 subprocess.check_output(cmd, text=True) 288 except FileNotFoundError: 289 LogUtil.hb_info("Error: ccache command not found") 290 except subprocess.CalledProcessError as e: 291 LogUtil.hb_info(f"Failed to execute ccache command: {e}") 292 293 def resolve_prebuilts_download(self, target_arg: Arg, indep_build_module: IndepBuildModuleInterface): 294 indep_build_module.prebuilts.regist_flag('skip-prebuilts', target_arg.arg_value)