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}")