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 18import os 19 20from services.interface.load_interface import LoadInterface 21from containers.status import throw_exception 22from exceptions.ohos_exception import OHOSException 23from util.loader import platforms_loader # noqa: E402 24from util.loader import generate_targets_gn # noqa: E402 25from util.loader import load_ohos_build # noqa: E402 26from util.loader import subsystem_scan # noqa: E402 27from util.loader import subsystem_info # noqa: E402 28from scripts.util.file_utils import read_json_file, write_json_file, write_file # noqa: E402, E501 29from util.log_util import LogUtil 30 31 32class OHOSLoader(LoadInterface): 33 34 def __init__(self): 35 super().__init__() 36 self.source_root_dir = "" 37 self.gn_root_out_dir = "" 38 self.os_level = "" 39 self.target_cpu = "" 40 self.target_os = "" 41 self.config_output_relpath = "" 42 self.config_output_dir = "" 43 self.target_arch = "" 44 self.subsystem_config_file = "" 45 self.subsystem_config_overlay_file = "" 46 self.platforms_config_file = "" 47 self.exclusion_modules_config_file = "" 48 self.example_subsystem_file = "" 49 self.build_example = "" 50 self.scalable_build = "" 51 self.build_platform_name = "" 52 self.build_xts = "" 53 self.ignore_api_check = "" 54 self.load_test_config = "" 55 self.subsystem_configs = "" 56 self._subsystem_info = "" 57 self.skip_partlist_check = "" 58 59 def __post_init__(self): 60 self.source_root_dir = self.config.root_path + '/' 61 self.gn_root_out_dir = self.config.out_path if not self.config.out_path.startswith( 62 '/') else os.path.relpath(self.config.out_path, self.config.root_path) 63 self.os_level = self.config.os_level if self.config.os_level else "standard" 64 self.target_cpu = self.config.target_cpu if self.config.target_cpu else "arm" 65 self.target_os = self.config.target_os if self.config.target_os else "ohos" 66 self.config_output_relpath = os.path.join( 67 self.gn_root_out_dir, 'build_configs') 68 self.config_output_dir = os.path.join( 69 self.source_root_dir, self.config_output_relpath) 70 self.target_arch = '{}_{}'.format(self.target_os, self.target_cpu) 71 self.subsystem_config_file = os.path.join( 72 self.config.root_path, 'out/preloader', self.config.product, 'subsystem_config.json') 73 self.platforms_config_file = os.path.join( 74 self.config.root_path, 'out/preloader', self.config.product, 'platforms.build') 75 self.exclusion_modules_config_file = os.path.join( 76 self.config.root_path, 'out/preloader', self.config.product, 'exclusion_modules.json') 77 self.example_subsystem_file = os.path.join( 78 self.config.root_path, 'build', 'subsystem_config_example.json') 79 80 compile_standard_allow_file = os.path.join( 81 self.config.root_path, 'out/preloader', self.config.product, 'compile_standard_whitelist.json') 82 compile_standard_allow_info = read_json_file(compile_standard_allow_file) 83 bundle_subsystem_allow_list = compile_standard_allow_info.get("bundle_subsystem_error", []) 84 85 # check config args 86 self._check_args() 87 88 self.build_example = self.args_dict.get('build_example') 89 if not self.build_example: 90 self.example_subsystem_file = "" 91 self.scalable_build = self.args_dict.get('scalable_build') 92 self.build_platform_name = self.args_dict.get('build_platform_name') 93 self.build_xts = self.args_dict.get('build_xts') 94 self.ignore_api_check = self.args_dict.get('ignore_api_check') 95 self.load_test_config = self.args_dict.get('load_test_config') 96 self.skip_partlist_check = self.args_dict.get('skip_partlist_check') 97 98 self._subsystem_info = subsystem_info.get_subsystem_info( 99 self.subsystem_config_file, 100 self.example_subsystem_file, 101 self.source_root_dir, 102 self.config_output_relpath, 103 self.os_level) 104 overrided_components = self._override_components() 105 106 self._platforms_info = platforms_loader.get_platforms_info( 107 self.platforms_config_file, 108 self.source_root_dir, 109 self.gn_root_out_dir, 110 self.target_arch, 111 self.config_output_relpath, 112 self.scalable_build) 113 self.variant_toolchains = self._platforms_info.get( 114 'variant_toolchain_info').get('platform_toolchain') 115 self._all_platforms = self.variant_toolchains.keys() 116 self.build_platforms = self._get_build_platforms() 117 self.parts_config_info = load_ohos_build.get_parts_info( 118 self.source_root_dir, 119 self.config_output_relpath, 120 self._subsystem_info, 121 self.variant_toolchains, 122 self.target_arch, 123 self.ignore_api_check, 124 self.exclusion_modules_config_file, 125 self.load_test_config, 126 overrided_components, 127 bundle_subsystem_allow_list, 128 self.skip_partlist_check, 129 self.build_xts) 130 self.parts_targets = self.parts_config_info.get('parts_targets') 131 self.phony_targets = self.parts_config_info.get('phony_target') 132 self.parts_info = self.parts_config_info.get('parts_info') 133 self.target_platform_parts = self._get_platforms_all_parts() 134 self.target_platform_stubs = self._get_platforms_all_stubs() 135 self.required_parts_targets_list = self._get_required_build_parts_list() 136 self.required_phony_targets = self._get_required_phony_targets() 137 self.required_parts_targets = self._get_required_build_targets() 138 139# check method 140 141 '''Description: Check the parameters passed in config. If the parameters are not 142 specified or the file content pointed to by the parameters does not 143 exist, an exception will be thrown directly. 144 @parameter:none 145 @return :none 146 ''' 147 @throw_exception 148 def _check_args(self): 149 LogUtil.hb_info("Checking all build args...") 150 # check subsystem_config_file 151 if not read_json_file(self.subsystem_config_file): 152 self.subsystem_config_file = os.path.join( 153 self.source_root_dir, 'build/subsystem_config.json') 154 if not read_json_file(self.subsystem_config_file): 155 raise OHOSException("Cannot get the content from platform config file, \ 156 please check whether the corresponding file('out/preloader/{}/subsystem_config.json' or \ 157 'build/subsystem_config.json') is written correctly.".format(self.config.product), "2001") 158 159 # check gn_root_out_dir 160 if not self.gn_root_out_dir: 161 raise OHOSException("Args gn_root_out_dir is required.", "2002") 162 if not os.path.realpath(self.gn_root_out_dir).startswith(self.source_root_dir): 163 raise OHOSException("Args gn_root_out_dir is incorrect.", "2003") 164 165 # check platform config file 166 if not read_json_file(self.platforms_config_file): 167 raise OHOSException("Cannot get the content from platform config file, \ 168 please check whether the corresponding file('out/preloader/${product_name}/platforms.build') \ 169 is written correctly.".format(self.config.product), "2004") 170 171 # check example subsystem file 172 if not read_json_file(self.example_subsystem_file): 173 raise OHOSException("Cannot get the content from example subsystem file, please check whether \ 174 the corresponding file ('build/subsystem_config_example.json') exists.", "2005") 175 176 @throw_exception 177 def _check_product_part_feature(self): 178 LogUtil.hb_info("Checking all product features...") 179 product_preloader_dir = os.path.dirname(self.platforms_config_file) 180 _preloader_feature_file = os.path.join(product_preloader_dir, 181 'features.json') 182 _preloader_feature_info = read_json_file(_preloader_feature_file) 183 part_to_feature = _preloader_feature_info.get('part_to_feature') 184 _feature_whitelist_file = os.path.join( 185 self.source_root_dir, "out/products_ext", "component_feature_whitelist.json" 186 ) 187 if not os.path.exists(_feature_whitelist_file): 188 _feature_whitelist_file = os.path.join( 189 self.source_root_dir, "build/", "component_feature_whitelist.json" 190 ) 191 _feature_whitelist_info = read_json_file(_feature_whitelist_file) 192 _feature_whitelist_list = [] 193 if _feature_whitelist_info: 194 _feature_whitelist_list = list(_feature_whitelist_info.keys()) 195 for key, vals in part_to_feature.items(): 196 part = self.parts_info.get(key) 197 if part is None: 198 continue 199 _p_info = part[0] 200 def_feature_list = _p_info.get('feature_list') 201 if vals and not def_feature_list: 202 message = "The product use a feature vals='{}', but that is not defined " \ 203 "in this part bundle.json file, part_name='{}'".format(vals, key) 204 if key not in _feature_whitelist_list: 205 raise OHOSException(message, "2006") 206 LogUtil.hb_warning(message) 207 continue 208 for _f_name in vals: 209 if _f_name not in def_feature_list: 210 raise OHOSException( 211 "The product use a feature that is not supported" 212 " by this part, part_name='{}', feature='{}'".format( 213 key, _f_name), "2006") 214 215 @throw_exception 216 def _check_parts_config_info(self): 217 LogUtil.hb_info("Checking parts config...") 218 if not ('parts_info' in self.parts_config_info 219 and 'subsystem_parts' in self.parts_config_info 220 and 'parts_variants' in self.parts_config_info 221 and 'parts_kits_info' in self.parts_config_info 222 and 'parts_inner_kits_info' in self.parts_config_info 223 and 'parts_targets' in self.parts_config_info): 224 raise OHOSException( 225 "Loading ohos.build information is incorrect.", "2007") 226 227# generate method 228 229 '''Description: Generate SystemCapability.json & syscap.json & syscap.para, dir:[ 230 (//out/preloader/${product_name}/system/etc/SystemCapability.json), 231 (//out/preloader/${product_name}/system/etc/syscap.json), 232 (//out/preloader/${product_name}/system/etc/param/syscap.para)] 233 @parameter:none 234 @return :none 235 ''' 236 @throw_exception 237 def _generate_syscap_files(self): 238 pre_syscap_info_path = os.path.dirname(self.platforms_config_file) 239 system_path = os.path.join(self.source_root_dir, os.path.join( 240 os.path.dirname(self.platforms_config_file), "system/")) 241 syscap_product_dict = read_json_file( 242 os.path.join(pre_syscap_info_path, "syscap.json")) 243 syscap_info_list = self.parts_config_info.get('syscap_info') 244 target_syscap_with_part_name_list = [] 245 target_syscap_list = [] 246 target_syscap_for_init_list = [] 247 all_syscap_list = [] 248 for syscap in syscap_info_list: 249 if syscap['component'] not in self.required_parts_targets_list: 250 continue 251 if 'syscap' not in syscap or syscap['syscap'] is None \ 252 or len(syscap['syscap']) == 0 or syscap['syscap'] == [""]: 253 continue 254 for syscap_string in syscap['syscap']: 255 all_syscap_list.append(syscap_string.split('=')[0].strip()) 256 257 for key, value in syscap_product_dict['part_to_syscap'].items(): 258 part = self.parts_info.get(key) 259 if part is None: 260 continue 261 for syscap in value: 262 if syscap not in all_syscap_list: 263 raise OHOSException( 264 "In config.json of part [{}],the syscap[{}] is incorrect, \ 265 please check the syscap name".format(key, syscap), "2008") 266 267 for syscap in syscap_info_list: 268 remove_list = [] 269 if syscap['component'] not in self.required_parts_targets_list: 270 continue 271 if 'syscap' not in syscap or syscap['syscap'] is None \ 272 or len(syscap['syscap']) == 0 or syscap['syscap'] == [""]: 273 continue 274 for syscap_string in syscap['syscap']: 275 if syscap_string.startswith("SystemCapability.") is True: 276 target_syscap_init_str = "const." 277 syscap_name = syscap_string.split('=')[0].strip() 278 all_syscap_product = syscap_product_dict['syscap'] 279 if syscap_name in all_syscap_product and not all_syscap_product[syscap_name]: 280 remove_list.append(syscap_string) 281 continue 282 elif syscap_name in all_syscap_product and all_syscap_product[syscap_name]: 283 target_syscap_init_str += syscap_name + '=true\n' 284 else: 285 if syscap_string.endswith('true'): 286 target_syscap_init_str += syscap_name + '=true\n' 287 elif syscap_string.endswith('false'): 288 remove_list.append(syscap_string) 289 continue 290 else: 291 target_syscap_init_str += syscap_string + "=true\n" 292 if target_syscap_init_str not in target_syscap_for_init_list: 293 target_syscap_for_init_list.append( 294 target_syscap_init_str) 295 else: 296 raise OHOSException("""In bundle.json of part [{}], The syscap string [{}] is incorrect, 297 need start with \"SystemCapability.\"""".format(syscap['component'], syscap_string), "2009") 298 299 for remove_str in remove_list: 300 syscap['syscap'].remove(remove_str) 301 for i in range(len(syscap['syscap'])): 302 if syscap['syscap'][i].endswith('true') or syscap['syscap'][i].endswith('false'): 303 syscap['syscap'][i] = syscap['syscap'][i].split('=')[ 304 0].strip() 305 306 syscap['syscap'].sort() 307 target_syscap_with_part_name_list.append(syscap) 308 target_syscap_list.extend(syscap['syscap']) 309 310 # Generate SystemCapability.json & syscap.json & syscap.para 311 target_syscap_list.sort() 312 syscap_info_dict = read_json_file(os.path.join( 313 pre_syscap_info_path, "SystemCapability.json")) 314 syscap_info_dict.update({'syscap': {'os': target_syscap_list}}) 315 system_etc_path = os.path.join(system_path, "etc/") 316 if not os.path.exists(system_path): 317 os.mkdir(system_path) 318 if not os.path.exists(system_etc_path): 319 os.mkdir(system_etc_path) 320 syscap_info_json = os.path.join( 321 system_etc_path, "SystemCapability.json") 322 write_json_file(syscap_info_json, syscap_info_dict) 323 LogUtil.hb_info( 324 "generate syscap info file to '{}'".format(syscap_info_json), mode=self.config.log_mode) 325 target_syscap_with_part_name_list.sort( 326 key=lambda syscap: syscap['component']) 327 syscap_info_with_part_name_file = os.path.join( 328 system_etc_path, "syscap.json") 329 write_json_file(syscap_info_with_part_name_file, { 330 'components': target_syscap_with_part_name_list}) 331 LogUtil.hb_info("generate syscap info with part name list to '{}'".format( 332 syscap_info_with_part_name_file), mode=self.config.log_mode) 333 if not os.path.exists(os.path.join(system_etc_path, "param/")): 334 os.mkdir(os.path.join(system_etc_path, "param/")) 335 target_syscap_for_init_file = os.path.join( 336 system_etc_path, "param/syscap.para") 337 with open(target_syscap_for_init_file, "w") as file: 338 file.writelines(sorted(target_syscap_for_init_list)) 339 LogUtil.hb_info("generate target syscap for init list to '{}'".format( 340 target_syscap_for_init_file), mode=self.config.log_mode) 341 342# get method 343 @throw_exception 344 def _get_build_platforms(self) -> list: 345 build_platforms = [] 346 if self.build_platform_name == 'all': 347 build_platforms = self._all_platforms 348 elif self.build_platform_name in self._all_platforms: 349 build_platforms = [self.build_platform_name] 350 else: 351 raise OHOSException( 352 "The target_platform is incorrect, only allows [{}].".format( 353 ', '.join(self._all_platforms)), "2010") 354 return build_platforms 355 356 '''Description: output infos for testfwk into a json file. \ 357 (/out/${product_name}/build_configs/infos_for_testfwk.json) 358 @parameter:none 359 @return :none 360 ''' 361 362 def _generate_infos_for_testfwk(self): 363 infos_for_testfwk_file = os.path.join(self.config_output_dir, 364 "infos_for_testfwk.json") 365 parts_info = self.parts_config_info.get('parts_info') 366 parts_info_dict = {} 367 for _part_name, _parts in parts_info.items(): 368 for _info in _parts: 369 parts_info_dict[_info.get('part_name')] = _info 370 _output_infos = {} 371 for _platform, _parts in self.target_platform_parts.items(): 372 result = self._output_infos_by_platform(_parts, parts_info_dict) 373 _output_infos[_platform] = result 374 write_json_file(infos_for_testfwk_file, 375 _output_infos, check_changes=True) 376 LogUtil.hb_info("generate infos for testfwk to '{}'".format( 377 infos_for_testfwk_file), mode=self.config.log_mode) 378 379 '''Description: output all target platform parts into a json file \ 380 (/out/${product_name}/build_configs/target_platforms_parts.json) 381 @parameter:none 382 @return :none 383 ''' 384 385 def _generate_target_platform_parts(self): 386 target_platform_parts_file = os.path.join(self.config_output_dir, 387 "target_platforms_parts.json") 388 write_json_file(target_platform_parts_file, 389 self.target_platform_parts, 390 check_changes=True) 391 LogUtil.hb_info("generate target platform parts to '{}'".format( 392 target_platform_parts_file), mode=self.config.log_mode) 393 394 '''Description: Generate parts differences in different platforms, using phone as base. \ 395 (/out/${product_name}/build_configs/parts_different_info.json) 396 @parameter: none 397 @return :none 398 ''' 399 400 def _generate_part_different_info(self): 401 parts_different_info = self._get_parts_by_platform() 402 parts_different_info_file = os.path.join(self.config_output_dir, 403 "parts_different_info.json") 404 write_json_file(parts_different_info_file, 405 parts_different_info, 406 check_changes=True) 407 LogUtil.hb_info("generate part different info to '{}'".format( 408 parts_different_info_file), mode=self.config.log_mode) 409 410 '''Description: output platforms list into a gni file. \ 411 (/out/${product_name}/build_configs/platforms_list.gni) 412 @parameter: none 413 @return: none 414 ''' 415 416 def _generate_platforms_list(self): 417 platforms_list_gni_file = os.path.join(self.config_output_dir, 418 "platforms_list.gni") 419 _platforms = set(self.build_platforms) 420 _gni_file_content = ['target_platform_list = [', ' "{}"'.format('",\n "'.join(_platforms)), ']', 421 'kits_platform_list = [', ' "{}",'.format('",\n "'.join(_platforms))] 422 if 'phone' not in self.build_platforms: 423 _gni_file_content.append(' "phone"') 424 _gni_file_content.append(']') 425 write_file(platforms_list_gni_file, '\n'.join(_gni_file_content)) 426 LogUtil.hb_info("generate platforms list to '{}'".format( 427 platforms_list_gni_file), mode=self.config.log_mode) 428 429 '''Description: output auto install part into a json file. \ 430 (/out/${product_name}/build_configs/auto_install_parts.json) 431 @parameter: none 432 @return: none 433 ''' 434 435 def _generate_auto_install_part(self): 436 parts_path_info = self.parts_config_info.get("parts_path_info") 437 auto_install_part_list = [] 438 for part, path in parts_path_info.items(): 439 if str(path).startswith("drivers/interface") or \ 440 str(path).startswith("third_party"): 441 auto_install_part_list.append(part) 442 auto_install_list_file = os.path.join( 443 self.config_output_dir, "auto_install_parts.json") 444 write_json_file(auto_install_list_file, auto_install_part_list) 445 LogUtil.hb_info("generate auto install part to '{}'".format( 446 auto_install_list_file), mode=self.config.log_mode) 447 448 '''Description: output src flag into a json file. \ 449 (/out/${product_name}/build_configs/parts_src_flag.json) 450 @parameter: none 451 @return :none 452 ''' 453 454 def _generate_src_flag(self): 455 parts_src_flag_file = os.path.join(self.config_output_dir, 456 "parts_src_flag.json") 457 write_json_file(parts_src_flag_file, 458 self._get_parts_src_list(), 459 check_changes=True) 460 LogUtil.hb_info( 461 "generated parts src flag to '{}/subsystem_info/parts_src_flag.json'".format( 462 self.config_output_dir), mode=self.config.log_mode) 463 464 '''Description: output build target list into a json file.\ 465 (/out/${product_name}/build_configs/required_parts_targets_list.json) 466 @parameter: none 467 @return :none 468 ''' 469 470 def _generate_required_parts_targets_list(self): 471 build_targets_list_file = os.path.join(self.config_output_dir, 472 "required_parts_targets_list.json") 473 write_json_file(build_targets_list_file, 474 list(self.required_parts_targets.values())) 475 LogUtil.hb_info("generate build targets list file to '{}'".format( 476 build_targets_list_file), mode=self.config.log_mode) 477 478 '''Description: output build target info into a json file. \ 479 (/out/${product_name}/build_configs/required_parts_targets.json) 480 @parameter: none 481 @return: none 482 ''' 483 484 def _generate_required_parts_targets(self): 485 build_targets_info_file = os.path.join(self.config_output_dir, 486 "required_parts_targets.json") 487 write_json_file(build_targets_info_file, self.required_parts_targets) 488 LogUtil.hb_info("generate required parts targets to '{}'".format( 489 build_targets_info_file), mode=self.config.log_mode) 490 491 '''Description: output platforms part by src into a json file. \ 492 (/out/${product_name}/build_configs/platforms_parts_by_src.json) 493 @parameter: none 494 @return :none 495 ''' 496 497 def _generate_platforms_part_by_src(self): 498 platforms_parts_by_src = self._get_platforms_parts() 499 platforms_parts_by_src_file = os.path.join(self.source_root_dir, 500 self.config_output_relpath, 501 "platforms_parts_by_src.json") 502 write_json_file(platforms_parts_by_src_file, 503 platforms_parts_by_src, 504 check_changes=True) 505 LogUtil.hb_info("generated platforms parts by src to '{}'".format( 506 platforms_parts_by_src_file), mode=self.config.log_mode) 507 508 '''Description: output system configs info into 4 files:[ 509 (/out/${product_name}/build_configs/subsystem_info/parts_list.gni), 510 (/out/${product_name}/build_configs/subsystem_info/inner_kits_list.gni), 511 (/out/${product_name}/build_configs/subsystem_info/system_kits_list.gni), 512 (/out/${product_name}/build_configs/subsystem_info/parts_test_list.gni), 513 (/out/${product_name}/build_configs/subsystem_info/BUILD.gn)] 514 @parameter: none 515 @return :none 516 ''' 517 518 def _generate_target_gn(self): 519 generate_targets_gn.gen_targets_gn(self.required_parts_targets, 520 self.config_output_dir) 521 522 '''Description: output phony targets build file. \ 523 (/out/${product_name}/build_configs/phony_target/BUILD.gn) 524 @parameter: none 525 @return :none 526 ''' 527 528 def _generate_phony_targets_build_file(self): 529 generate_targets_gn.gen_phony_targets(self.required_phony_targets, 530 self.config_output_dir) 531 532 '''Description: output system configs info into 2 files:[ 533 (/out/${product_name}/build_configs/subsystem_info/${platform}-stub/BUILG.gn), 534 (/out/${product_name}/build_configs/subsystem_info/${platform}-stub/zframework_stub_exists.gni)] 535 @parameter: none 536 @return :none 537 ''' 538 539 def _generate_stub_targets(self): 540 generate_targets_gn.gen_stub_targets( 541 self.parts_config_info.get('parts_kits_info'), 542 self.target_platform_stubs, 543 self.config_output_dir) 544 545 '''Description: output system capabilities into a json file. \ 546 (/out/${product_name}/build_configs/${platform}_system_capabilities.json) 547 @parameter: none 548 @return :none 549 ''' 550 551 def _generate_system_capabilities(self): 552 for platform in self.build_platforms: 553 platform_parts = self.target_platform_parts.get(platform) 554 platform_capabilities = [] 555 for _, origin in platform_parts.items(): 556 # parts_info.get() might be None if the part is a binary package 557 all_parts_variants = self.parts_info.get(origin) 558 if all_parts_variants is None: 559 continue 560 part = all_parts_variants[0] 561 if part.get('system_capabilities'): 562 entry = part.get('system_capabilities') 563 if len(entry) > 0: 564 platform_capabilities.extend(entry) 565 platform_part_json_file = os.path.join( 566 self.config_output_dir, "{0}_system_capabilities.json".format(platform)) 567 write_json_file(platform_part_json_file, 568 sorted(platform_capabilities), 569 check_changes=True) 570 LogUtil.hb_info( 571 "generated system capabilities to '{}/{}_system_capabilities.json'".format( 572 self.config_output_dir, platform), mode=self.config.log_mode) 573 574 '''Description: output system configs info into three json files:[ 575 (/out/${product_name}/build_configs/subsystem_info/subsystem_build_config.json), 576 (/out/${product_name}/build_configs/subsystem_info/src_subsystem_info.json), 577 (/out/${product_name}/build_configs/subsystem_info/no_src_subsystem_info.json)] 578 @parameter: none 579 @return :none 580 ''' 581 582 def _generate_subsystem_configs(self): 583 584 # The function has been implemented in module util/loader/subsystem_info.py 585 LogUtil.hb_info( 586 "generated subsystem build config to '{}/subsystem_info/subsystem_build_config.json'".format( 587 self.config_output_dir), mode=self.config.log_mode) 588 LogUtil.hb_info( 589 "generated src subsystem info to '{}/subsystem_info/src_subsystem_info.json'".format( 590 self.config_output_dir), mode=self.config.log_mode) 591 LogUtil.hb_info( 592 "generated no src subsystem info to '{}/subsystem_info/no_src_subsystem_info.json'".format( 593 self.config_output_dir), mode=self.config.log_mode) 594 595 def _get_parts_by_platform(self) -> dict: 596 parts_info = {} 597 if 'phone' in self.target_platform_parts: 598 phone_parts_list = self.target_platform_parts.get('phone').keys() 599 else: 600 phone_parts_list = [] 601 for _platform, _parts_info in self.target_platform_parts.items(): 602 base_parts_list = [] 603 curr_parts_list = [] 604 for _real_name, _original_name in _parts_info.items(): 605 if _real_name in phone_parts_list: 606 base_parts_list.append(_real_name) 607 elif _original_name in phone_parts_list: 608 base_parts_list.append(_real_name) 609 else: 610 curr_parts_list.append(_real_name) 611 result_data = { 612 "base_parts_list": base_parts_list, 613 "curr_parts_list": curr_parts_list 614 } 615 parts_info[_platform] = result_data 616 return parts_info 617 618 def _get_platforms_all_parts(self) -> dict: 619 _dist_parts_variants = self._load_component_dist() 620 target_platform_parts = {} 621 all_parts = self._platforms_info.get('all_parts') 622 parts_variants = self.parts_config_info.get('parts_variants') 623 for _platform, _parts in all_parts.items(): 624 if _platform not in self.build_platforms: 625 continue 626 part_name_info = {} 627 for part_def in _parts: 628 real_name, original_name = self._get_real_part_name( 629 part_def, _platform, parts_variants) 630 if real_name is None: 631 # find this from component_dist 632 real_name, original_name = self._get_real_part_name( 633 part_def, _platform, _dist_parts_variants) 634 if real_name is None: 635 continue 636 part_name_info[real_name] = original_name 637 target_platform_parts[_platform] = part_name_info 638 return target_platform_parts 639 640 def _get_platforms_all_stubs(self) -> dict: 641 _dist_parts_variants = self._load_component_dist() 642 platform_stubs = {} 643 all_stubs = self._platforms_info.get('all_stubs') 644 parts_variants = self.parts_config_info.get('parts_variants') 645 for _platform, _part_names in all_stubs.items(): 646 if _platform not in self.build_platforms: 647 continue 648 stub_parts_from_src = [] 649 stub_parts_from_dist = [] 650 for part_name in _part_names: 651 real_name, original_name = self._get_real_part_name( 652 part_name, _platform, parts_variants) 653 # real_name=None means part_name doesn't exist in source tree, 654 # use binary in component_dist then. 655 if real_name is None: 656 # find this from component_dist 657 real_name, original_name = self._get_real_part_name( 658 part_name, _platform, _dist_parts_variants) 659 if real_name is None: 660 continue 661 else: 662 stub_sources = os.path.join( 663 self.source_root_dir, 664 "component_dist/{}-{}/api_stubs/{}/stubs_sources_list.txt" # noqa: E501 665 .format(self.target_os, self.target_cpu, real_name)) 666 stub_parts_from_dist.append( 667 '"{}"'.format(stub_sources)) 668 else: 669 stub_parts_from_src.append(real_name) 670 platform_stubs[_platform] = { 671 "src": stub_parts_from_src, 672 "dist": stub_parts_from_dist, 673 } 674 return platform_stubs 675 676 def _get_platforms_parts(self) -> dict: 677 platforms_parts = {} 678 src_parts_targets = self.parts_targets 679 src_all_parts = src_parts_targets.keys() 680 for _platform, _all_parts in self.target_platform_parts.items(): 681 src_parts_list = [] 682 no_src_parts_list = [] 683 for _part in _all_parts.keys(): 684 if _part in src_all_parts: 685 src_parts_list.append(_part) 686 else: 687 no_src_parts_list.append(_part) 688 _data = { 689 'src_parts': src_parts_list, 690 'no_src_parts': no_src_parts_list 691 } 692 platforms_parts[_platform] = _data 693 return platforms_parts 694 695 def _get_parts_src_list(self) -> list: 696 parts_name_map = {} 697 for _list in self.parts_info.values(): 698 for _info in _list: 699 parts_name_map[_info.get('part_name')] = _info.get( 700 'origin_part_name') 701 _src_set = set() 702 for _name in self.required_parts_targets.keys(): 703 _origin_name = parts_name_map.get(_name) 704 if _origin_name is None: 705 continue 706 _src_set.add(_origin_name) 707 return list(_src_set) 708 709 def _get_required_build_targets(self) -> dict: 710 required_build_targets = {} 711 for _p_name, _info in self.parts_targets.items(): 712 if _p_name not in self.required_parts_targets_list: 713 continue 714 required_build_targets[_p_name] = _info 715 return required_build_targets 716 717 def _get_required_phony_targets(self) -> dict: 718 required_build_targets = {} 719 for _p_name, _info in self.phony_targets.items(): 720 if _p_name not in self.required_parts_targets_list: 721 continue 722 required_build_targets[_p_name] = _info 723 return required_build_targets 724 725 def _get_required_build_parts_list(self) -> list: 726 parts_set = set() 727 for _parts_list in self.target_platform_parts.values(): 728 parts_set.update(_parts_list) 729 return list(parts_set) 730 731# util method 732 733 def _load_component_dist(self) -> dict: 734 _parts_variants_info = {} 735 _dir = "component_dist/{}-{}/packages_to_install".format( 736 self.target_os, self.target_cpu) 737 _file_name = "dist_parts_info.json" 738 _dist_parts_info_file = os.path.join( 739 self.source_root_dir, _dir, _file_name) 740 if not os.path.exists(_dist_parts_info_file): 741 # If the file does not exist, do nothing and return 742 return _parts_variants_info 743 _parts_info = read_json_file(_dist_parts_info_file) 744 if _parts_info is None: 745 raise Exception("read file '{}' failed.".format( 746 _dist_parts_info_file)) 747 for _part_info in _parts_info: 748 origin_part_name = _part_info.get('origin_part_name') 749 if origin_part_name in _parts_variants_info: 750 variants = _parts_variants_info.get(origin_part_name) 751 else: 752 variants = [] 753 _variant_name = _part_info.get('variant_name') 754 variants.append(_variant_name) 755 _parts_variants_info[origin_part_name] = variants 756 return _parts_variants_info 757 758 def _get_real_part_name(self, original_part_name: str, current_platform: str, parts_variants: dict): 759 part_info = parts_variants.get(original_part_name) 760 if part_info is None: 761 return None, None 762 if current_platform in part_info and current_platform != 'phone': 763 real_name = '{}_{}'.format(original_part_name, current_platform) 764 else: 765 real_name = original_part_name 766 return real_name, original_part_name 767 768 '''Description: called by _out_infos_for_testfwk, output information by platform 769 @parameter:none 770 @return :none 771 ''' 772 773 def _output_infos_by_platform(self, part_name_infos: dict, parts_info_dict: dict): 774 required_parts = {} 775 subsystem_infos = {} 776 for part_name, origin_part_name in part_name_infos.items(): 777 part_info = parts_info_dict.get(part_name) 778 if part_info is None: 779 continue 780 if origin_part_name != part_info.get('origin_part_name'): 781 raise Exception("part configuration is incorrect.") 782 required_parts[origin_part_name] = part_info 783 _subsystem_name = part_info.get('subsystem_name') 784 if _subsystem_name in subsystem_infos: 785 p_list = subsystem_infos.get(_subsystem_name) 786 else: 787 p_list = [] 788 p_list.append(origin_part_name) 789 subsystem_infos[_subsystem_name] = p_list 790 result = {} 791 result['subsystem_infos'] = subsystem_infos 792 result['part_infos'] = required_parts 793 return result 794 795 def _execute_loader_args_display(self): 796 LogUtil.hb_info('Loading configuration file...') 797 args = [] 798 args.append('platforms_config_file="{}"'.format( 799 self.platforms_config_file)) 800 args.append('subsystem_config_file="{}"'.format( 801 self.subsystem_config_file)) 802 args.append('example_subsystem_file="{}"'.format( 803 self.example_subsystem_file)) 804 args.append('exclusion_modules_config_file="{}"'.format( 805 self.exclusion_modules_config_file)) 806 args.append('source_root_dir="{}"'.format(self.source_root_dir)) 807 args.append('gn_root_out_dir="{}"'.format(self.gn_root_out_dir)) 808 args.append('build_platform_name={}'.format(self.build_platform_name)) 809 args.append('build_xts={}'.format(self.build_xts)) 810 args.append('load_test_config={}'.format(self.load_test_config)) 811 args.append('target_os={}'.format(self.target_os)) 812 args.append('target_cpu={}'.format(self.target_cpu)) 813 args.append('os_level={}'.format(self.os_level)) 814 args.append('ignore_api_check={}'.format(self.ignore_api_check)) 815 args.append('scalable_build={}'.format(self.scalable_build)) 816 args.append('skip_partlist_check={}'.format(self.skip_partlist_check)) 817 LogUtil.write_log(self.config.log_path, 818 'loader args:{}'.format(args), 'info') 819 820 def _override_components(self): 821 '''Description: Check whether there are components that need to be replaced, and if so, 822 replace the component configuration file bundle.json in subsystem_info and update 823 the component list generated by the preloader. 824 @parameter:none 825 @return :overrided_components 826 ''' 827 parts_file = self.platforms_config_file.replace( 828 "platforms.build", "parts.json") 829 all_parts = read_json_file(parts_file) 830 if "parts" not in all_parts: 831 LogUtil.hb_warning("{} does not contain parts!".format(parts_file)) 832 return {} 833 overrided = False 834 overrided_components = {} 835 all_parts = all_parts["parts"] 836 component_override_map = {} 837 all_component_override_map = {} 838 for subsystem_name, build_config_info in self._subsystem_info.items(): 839 if "build_files" not in build_config_info: 840 continue 841 842 # scan all bundle.json or ohos.build files with named groups 843 for build_file in build_config_info["build_files"]: 844 845 # ohos.build does not support overrided components 846 if not build_file.endswith('bundle.json'): 847 continue 848 849 # Only device or vendor components can do named groups extensions 850 if (not build_file.startswith(self.source_root_dir + 'device/')) \ 851 and (not build_file.startswith(self.source_root_dir + 'vendor/')): 852 continue 853 854 # "subsystem", "name" and "override" is required 855 component = read_json_file(build_file).get("component") 856 857 if (not component) or (not all(key in component for key in ("subsystem", "name", "override"))): 858 continue 859 860 full_part_name = f"{component.get('subsystem')}:{component.get('name')}" 861 if full_part_name not in all_parts: 862 LogUtil.hb_warning("{} was not configured for this product: {}".format( 863 build_file, full_part_name)) 864 continue 865 866 if self._override_one_component(self._subsystem_info, component, build_file, all_parts, overrided_components, component_override_map): 867 overrided = True 868 869 if overrided: 870 # Update parts.json and parts_config.json generated by preloader 871 write_json_file(parts_file, {"parts": all_parts}) 872 parts_file = self.platforms_config_file.replace( 873 "platforms.build", "parts_config.json") 874 self._output_parts_config_json(all_parts, parts_file) 875 all_component_override_map.update(component_override_map) 876 write_json_file( 877 f"{self.config_output_dir}/component_override_map.json", all_component_override_map) 878 return overrided_components 879 880 def _override_one_component(self, subsystem_info: dict, component: dict, build_file: str, all_parts: dict, overrided_components: dict, component_override_map: dict): 881 '''Description: Perform a replacement of a single component and return the component list update result. 882 @parameter:subsystem_info, component, build_file, all_parts, overrided_components 883 @return :True or False(Whether replacement has been performed) 884 ''' 885 splits = component["override"].split(":") 886 if len(splits) != 2: 887 LogUtil.hb_warning( 888 "{} override value is invalid format. Skip override process".format(build_file)) 889 return False 890 overrided_subsystem = splits[0] 891 overrided_component = splits[1] 892 if overrided_subsystem not in subsystem_info: 893 LogUtil.hb_warning( 894 "{} override invalid subsystem. Skip override process".format(build_file)) 895 return False 896 897 founded_bundle = "" 898 899 for bundle in subsystem_info[overrided_subsystem]["build_files"]: 900 if not bundle.endswith('bundle.json'): 901 continue 902 903 bundle_obj = read_json_file(bundle) 904 905 if bundle_obj.get("component", {}).get("name") == overrided_component: 906 founded_bundle = bundle 907 break 908 909 if founded_bundle: 910 origin_component = read_json_file(build_file).get('component') 911 LogUtil.hb_warning( 912 f"You are trying to override \"{component['override']}\" with \"{origin_component.get('subsystem')}:{origin_component.get('name')}\". \nPlease ensure that the modules in \"{component['override']}\" only rely on the interfaces of other components through \"external_deps\"") 913 914 # replace bundle.json in subsystem_info's build_files 915 subsystem_info[overrided_subsystem]["build_files"].remove( 916 founded_bundle) 917 918 # Update parts.json generated by preloader, which means that new added components will not be installed 919 # Ensure that the overrided components will be installed 920 full_partname = f"{overrided_subsystem}:{overrided_component}" 921 if full_partname in all_parts: 922 all_parts.remove(full_partname) 923 924 overrided_components[f"{component['subsystem']}:{component['name']}"] = { 925 'subsystem': overrided_subsystem, 926 'partName': overrided_component 927 } 928 component_override_map[overrided_component] = component["name"] 929 return True 930 LogUtil.hb_warning( 931 "{}:{} is not configured in product, \new add component will be installed!".format( 932 overrided_subsystem, overrided_component)) 933 return False 934 935 def _output_parts_config_json(self, all_parts: dict, output_file: dict): 936 '''Description: Update the parts list file generated by preloader 937 @parameter: all_parts, output_file 938 @return :none 939 ''' 940 parts_config = {} 941 for part in all_parts: 942 part = part.replace(":", "_") 943 part = part.replace("-", "_") 944 part = part.replace(".", "_") 945 part = part.replace("/", "_") 946 parts_config[part] = True 947 write_json_file(output_file, parts_config) 948