#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (c) 2023 Huawei Device Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from services.interface.load_interface import LoadInterface from containers.status import throw_exception from exceptions.ohos_exception import OHOSException from util.loader import platforms_loader # noqa: E402 from util.loader import generate_targets_gn # noqa: E402 from util.loader import load_ohos_build # noqa: E402 from util.loader import subsystem_scan # noqa: E402 from util.loader import subsystem_info # noqa: E402 from scripts.util.file_utils import read_json_file, write_json_file, write_file # noqa: E402, E501 from util.log_util import LogUtil class OHOSLoader(LoadInterface): def __init__(self): super().__init__() self.source_root_dir = "" self.gn_root_out_dir = "" self.os_level = "" self.target_cpu = "" self.target_os = "" self.config_output_relpath = "" self.config_output_dir = "" self.target_arch = "" self.subsystem_config_file = "" self.subsystem_config_overlay_file = "" self.platforms_config_file = "" self.exclusion_modules_config_file = "" self.example_subsystem_file = "" self.build_example = "" self.scalable_build = "" self.build_platform_name = "" self.build_xts = "" self.ignore_api_check = "" self.load_test_config = "" self.subsystem_configs = "" self._subsystem_info = "" self.skip_partlist_check = "" def __post_init__(self): self.source_root_dir = self.config.root_path + '/' self.gn_root_out_dir = self.config.out_path if not self.config.out_path.startswith( '/') else os.path.relpath(self.config.out_path, self.config.root_path) self.os_level = self.config.os_level if self.config.os_level else "standard" self.target_cpu = self.config.target_cpu if self.config.target_cpu else "arm" self.target_os = self.config.target_os if self.config.target_os else "ohos" self.config_output_relpath = os.path.join( self.gn_root_out_dir, 'build_configs') self.config_output_dir = os.path.join( self.source_root_dir, self.config_output_relpath) self.target_arch = '{}_{}'.format(self.target_os, self.target_cpu) self.subsystem_config_file = os.path.join( self.config.root_path, 'out/preloader', self.config.product, 'subsystem_config.json') self.platforms_config_file = os.path.join( self.config.root_path, 'out/preloader', self.config.product, 'platforms.build') self.exclusion_modules_config_file = os.path.join( self.config.root_path, 'out/preloader', self.config.product, 'exclusion_modules.json') self.example_subsystem_file = os.path.join( self.config.root_path, 'build', 'subsystem_config_example.json') compile_standard_allow_file = os.path.join( self.config.root_path, 'out/preloader', self.config.product, 'compile_standard_whitelist.json') compile_standard_allow_info = read_json_file(compile_standard_allow_file) bundle_subsystem_allow_list = compile_standard_allow_info.get("bundle_subsystem_error", []) # check config args self._check_args() self.build_example = self.args_dict.get('build_example') if not self.build_example: self.example_subsystem_file = "" self.scalable_build = self.args_dict.get('scalable_build') self.build_platform_name = self.args_dict.get('build_platform_name') self.build_xts = self.args_dict.get('build_xts') self.ignore_api_check = self.args_dict.get('ignore_api_check') self.load_test_config = self.args_dict.get('load_test_config') self.skip_partlist_check = self.args_dict.get('skip_partlist_check') self.subsystem_configs = subsystem_scan.scan(self.subsystem_config_file, self.example_subsystem_file, self.source_root_dir) self._subsystem_info = subsystem_info.get_subsystem_info( self.subsystem_config_file, self.example_subsystem_file, self.source_root_dir, self.config_output_relpath, self.os_level) overrided_components = self._override_components() self._platforms_info = platforms_loader.get_platforms_info( self.platforms_config_file, self.source_root_dir, self.gn_root_out_dir, self.target_arch, self.config_output_relpath, self.scalable_build) self.variant_toolchains = self._platforms_info.get( 'variant_toolchain_info').get('platform_toolchain') self._all_platforms = self.variant_toolchains.keys() self.build_platforms = self._get_build_platforms() self.parts_config_info = load_ohos_build.get_parts_info( self.source_root_dir, self.config_output_relpath, self._subsystem_info, self.variant_toolchains, self.target_arch, self.ignore_api_check, self.exclusion_modules_config_file, self.load_test_config, overrided_components, bundle_subsystem_allow_list, self.skip_partlist_check, self.build_xts) self.parts_targets = self.parts_config_info.get('parts_targets') self.phony_targets = self.parts_config_info.get('phony_target') self.parts_info = self.parts_config_info.get('parts_info') self.target_platform_parts = self._get_platforms_all_parts() self.target_platform_stubs = self._get_platforms_all_stubs() self.required_parts_targets_list = self._get_required_build_parts_list() self.required_phony_targets = self._get_required_phony_targets() self.required_parts_targets = self._get_required_build_targets() # check method '''Description: Check the parameters passed in config. If the parameters are not specified or the file content pointed to by the parameters does not exist, an exception will be thrown directly. @parameter:none @return :none ''' @throw_exception def _check_args(self): LogUtil.hb_info("Checking all build args...") # check subsystem_config_file if not read_json_file(self.subsystem_config_file): self.subsystem_config_file = os.path.join( self.source_root_dir, 'build/subsystem_config.json') if not read_json_file(self.subsystem_config_file): raise OHOSException("Cannot get the content from platform config file, \ please check whether the corresponding file('out/preloader/{}/subsystem_config.json' or \ 'build/subsystem_config.json') is written correctly.".format(self.config.product), "2001") # check gn_root_out_dir if not self.gn_root_out_dir: raise OHOSException("Args gn_root_out_dir is required.", "2002") if not os.path.realpath(self.gn_root_out_dir).startswith(self.source_root_dir): raise OHOSException("Args gn_root_out_dir is incorrect.", "2003") # check platform config file if not read_json_file(self.platforms_config_file): raise OHOSException("Cannot get the content from platform config file, \ please check whether the corresponding file('out/preloader/${product_name}/platforms.build') \ is written correctly.".format(self.config.product), "2004") # check example subsystem file if not read_json_file(self.example_subsystem_file): raise OHOSException("Cannot get the content from example subsystem file, please check whether \ the corresponding file ('build/subsystem_config_example.json') exists.", "2005") @throw_exception def _check_product_part_feature(self): LogUtil.hb_info("Checking all product features...") product_preloader_dir = os.path.dirname(self.platforms_config_file) _preloader_feature_file = os.path.join(product_preloader_dir, 'features.json') _preloader_feature_info = read_json_file(_preloader_feature_file) part_to_feature = _preloader_feature_info.get('part_to_feature') for key, vals in part_to_feature.items(): part = self.parts_info.get(key) if part is None: continue _p_info = part[0] def_feature_list = _p_info.get('feature_list') if not def_feature_list: continue for _f_name in vals: if _f_name not in def_feature_list: raise OHOSException( "The product use a feature that is not supported" " by this part, part_name='{}', feature='{}'".format( key, _f_name), "2006") @throw_exception def _check_parts_config_info(self): LogUtil.hb_info("Checking parts config...") if not ('parts_info' in self.parts_config_info and 'subsystem_parts' in self.parts_config_info and 'parts_variants' in self.parts_config_info and 'parts_kits_info' in self.parts_config_info and 'parts_inner_kits_info' in self.parts_config_info and 'parts_targets' in self.parts_config_info): raise OHOSException( "Loading ohos.build information is incorrect.", "2007") # generate method '''Description: Generate SystemCapability.json & syscap.json & syscap.para, dir:[ (//out/preloader/${product_name}/system/etc/SystemCapability.json), (//out/preloader/${product_name}/system/etc/syscap.json), (//out/preloader/${product_name}/system/etc/param/syscap.para)] @parameter:none @return :none ''' @throw_exception def _generate_syscap_files(self): pre_syscap_info_path = os.path.dirname(self.platforms_config_file) system_path = os.path.join(self.source_root_dir, os.path.join( os.path.dirname(self.platforms_config_file), "system/")) syscap_product_dict = read_json_file( os.path.join(pre_syscap_info_path, "syscap.json")) syscap_info_list = self.parts_config_info.get('syscap_info') target_syscap_with_part_name_list = [] target_syscap_list = [] target_syscap_for_init_list = [] all_syscap_list = [] for syscap in syscap_info_list: if syscap['component'] not in self.required_parts_targets_list: continue if 'syscap' not in syscap or syscap['syscap'] is None \ or len(syscap['syscap']) == 0 or syscap['syscap'] == [""]: continue for syscap_string in syscap['syscap']: all_syscap_list.append(syscap_string.split('=')[0].strip()) for key, value in syscap_product_dict['part_to_syscap'].items(): for syscap in value: if syscap not in all_syscap_list: raise OHOSException( "In config.json of part [{}],the syscap[{}] is incorrect, \ please check the syscap name".format(key, syscap), "2008") for syscap in syscap_info_list: remove_list = [] if syscap['component'] not in self.required_parts_targets_list: continue if 'syscap' not in syscap or syscap['syscap'] is None \ or len(syscap['syscap']) == 0 or syscap['syscap'] == [""]: continue for syscap_string in syscap['syscap']: if syscap_string.startswith("SystemCapability.") is True: target_syscap_init_str = "const." syscap_name = syscap_string.split('=')[0].strip() all_syscap_product = syscap_product_dict['syscap'] if syscap_name in all_syscap_product and not all_syscap_product[syscap_name]: remove_list.append(syscap_string) continue elif syscap_name in all_syscap_product and all_syscap_product[syscap_name]: target_syscap_init_str += syscap_name + '=true\n' else: if syscap_string.endswith('true'): target_syscap_init_str += syscap_name + '=true\n' elif syscap_string.endswith('false'): remove_list.append(syscap_string) continue else: target_syscap_init_str += syscap_string + "=true\n" if target_syscap_init_str not in target_syscap_for_init_list: target_syscap_for_init_list.append( target_syscap_init_str) else: raise OHOSException("""In bundle.json of part [{}], The syscap string [{}] is incorrect, need start with \"SystemCapability.\"""".format(syscap['component'], syscap_string), "2009") for remove_str in remove_list: syscap['syscap'].remove(remove_str) for i in range(len(syscap['syscap'])): if syscap['syscap'][i].endswith('true') or syscap['syscap'][i].endswith('false'): syscap['syscap'][i] = syscap['syscap'][i].split('=')[ 0].strip() syscap['syscap'].sort() target_syscap_with_part_name_list.append(syscap) target_syscap_list.extend(syscap['syscap']) # Generate SystemCapability.json & syscap.json & syscap.para target_syscap_list.sort() syscap_info_dict = read_json_file(os.path.join( pre_syscap_info_path, "SystemCapability.json")) syscap_info_dict.update({'syscap': {'os': target_syscap_list}}) system_etc_path = os.path.join(system_path, "etc/") if not os.path.exists(system_path): os.mkdir(system_path) if not os.path.exists(system_etc_path): os.mkdir(system_etc_path) syscap_info_json = os.path.join( system_etc_path, "SystemCapability.json") write_json_file(syscap_info_json, syscap_info_dict) LogUtil.hb_info( "generate syscap info file to '{}'".format(syscap_info_json)) target_syscap_with_part_name_list.sort( key=lambda syscap: syscap['component']) syscap_info_with_part_name_file = os.path.join( system_etc_path, "syscap.json") write_json_file(syscap_info_with_part_name_file, { 'components': target_syscap_with_part_name_list}) LogUtil.hb_info("generate syscap info with part name list to '{}'".format( syscap_info_with_part_name_file)) if not os.path.exists(os.path.join(system_etc_path, "param/")): os.mkdir(os.path.join(system_etc_path, "param/")) target_syscap_for_init_file = os.path.join( system_etc_path, "param/syscap.para") with open(target_syscap_for_init_file, "w") as file: file.writelines(target_syscap_for_init_list) LogUtil.hb_info("generate target syscap for init list to '{}'".format( target_syscap_for_init_file)) '''Description: output infos for testfwk into a json file. \ (/out/${product_name}/build_configs/infos_for_testfwk.json) @parameter:none @return :none ''' def _generate_infos_for_testfwk(self): infos_for_testfwk_file = os.path.join(self.config_output_dir, "infos_for_testfwk.json") parts_info = self.parts_config_info.get('parts_info') parts_info_dict = {} for _part_name, _parts in parts_info.items(): for _info in _parts: parts_info_dict[_info.get('part_name')] = _info _output_infos = {} for _platform, _parts in self.target_platform_parts.items(): result = self._output_infos_by_platform(_parts, parts_info_dict) _output_infos[_platform] = result write_json_file(infos_for_testfwk_file, _output_infos, check_changes=True) LogUtil.hb_info("generate infos for testfwk to '{}'".format( infos_for_testfwk_file)) '''Description: output all target platform parts into a json file \ (/out/${product_name}/build_configs/target_platforms_parts.json) @parameter:none @return :none ''' def _generate_target_platform_parts(self): target_platform_parts_file = os.path.join(self.config_output_dir, "target_platforms_parts.json") write_json_file(target_platform_parts_file, self.target_platform_parts, check_changes=True) LogUtil.hb_info("generate target platform parts to '{}'".format( target_platform_parts_file)) '''Description: Generate parts differences in different platforms, using phone as base. \ (/out/${product_name}/build_configs/parts_different_info.json) @parameter: none @return :none ''' def _generate_part_different_info(self): parts_different_info = self._get_parts_by_platform() parts_different_info_file = os.path.join(self.config_output_dir, "parts_different_info.json") write_json_file(parts_different_info_file, parts_different_info, check_changes=True) LogUtil.hb_info("generate part different info to '{}'".format( parts_different_info_file)) '''Description: output platforms list into a gni file. \ (/out/${product_name}/build_configs/platforms_list.gni) @parameter: none @return: none ''' def _generate_platforms_list(self): platforms_list_gni_file = os.path.join(self.config_output_dir, "platforms_list.gni") _platforms = set(self.build_platforms) _gni_file_content = ['target_platform_list = [', ' "{}"'.format('",\n "'.join(_platforms)), ']', 'kits_platform_list = [', ' "{}",'.format('",\n "'.join(_platforms))] if 'phone' not in self.build_platforms: _gni_file_content.append(' "phone"') _gni_file_content.append(']') write_file(platforms_list_gni_file, '\n'.join(_gni_file_content)) LogUtil.hb_info("generate platforms list to '{}'".format( platforms_list_gni_file)) '''Description: output auto install part into a json file. \ (/out/${product_name}/build_configs/auto_install_parts.json) @parameter: none @return: none ''' def _generate_auto_install_part(self): parts_path_info = self.parts_config_info.get("parts_path_info") auto_install_part_list = [] for part, path in parts_path_info.items(): if str(path).startswith("drivers/interface") or \ str(path).startswith("third_party"): auto_install_part_list.append(part) auto_install_list_file = os.path.join( self.config_output_dir, "auto_install_parts.json") write_json_file(auto_install_list_file, auto_install_part_list) LogUtil.hb_info("generate auto install part to '{}'".format( auto_install_list_file)) '''Description: output src flag into a json file. \ (/out/${product_name}/build_configs/parts_src_flag.json) @parameter: none @return :none ''' def _generate_src_flag(self): parts_src_flag_file = os.path.join(self.config_output_dir, "parts_src_flag.json") write_json_file(parts_src_flag_file, self._get_parts_src_list(), check_changes=True) LogUtil.hb_info( "generated parts src flag to '{}/subsystem_info/parts_src_flag.json'".format(self.config_output_dir)) '''Description: output build target list into a json file.\ (/out/${product_name}/build_configs/required_parts_targets_list.json) @parameter: none @return :none ''' def _generate_required_parts_targets_list(self): build_targets_list_file = os.path.join(self.config_output_dir, "required_parts_targets_list.json") write_json_file(build_targets_list_file, list(self.required_parts_targets.values())) LogUtil.hb_info("generate build targets list file to '{}'".format( build_targets_list_file)) '''Description: output build target info into a json file. \ (/out/${product_name}/build_configs/required_parts_targets.json) @parameter: none @return: none ''' def _generate_required_parts_targets(self): build_targets_info_file = os.path.join(self.config_output_dir, "required_parts_targets.json") write_json_file(build_targets_info_file, self.required_parts_targets) LogUtil.hb_info("generate required parts targets to '{}'".format( build_targets_info_file)) '''Description: output platforms part by src into a json file. \ (/out/${product_name}/build_configs/platforms_parts_by_src.json) @parameter: none @return :none ''' def _generate_platforms_part_by_src(self): platforms_parts_by_src = self._get_platforms_parts() platforms_parts_by_src_file = os.path.join(self.source_root_dir, self.config_output_relpath, "platforms_parts_by_src.json") write_json_file(platforms_parts_by_src_file, platforms_parts_by_src, check_changes=True) LogUtil.hb_info("generated platforms parts by src to '{}'".format( platforms_parts_by_src_file)) '''Description: output system configs info into 4 files:[ (/out/${product_name}/build_configs/subsystem_info/parts_list.gni), (/out/${product_name}/build_configs/subsystem_info/inner_kits_list.gni), (/out/${product_name}/build_configs/subsystem_info/system_kits_list.gni), (/out/${product_name}/build_configs/subsystem_info/parts_test_list.gni), (/out/${product_name}/build_configs/subsystem_info/BUILD.gn)] @parameter: none @return :none ''' def _generate_target_gn(self): generate_targets_gn.gen_targets_gn(self.required_parts_targets, self.config_output_dir) '''Description: output phony targets build file. \ (/out/${product_name}/build_configs/phony_target/BUILD.gn) @parameter: none @return :none ''' def _generate_phony_targets_build_file(self): generate_targets_gn.gen_phony_targets(self.required_phony_targets, self.config_output_dir) '''Description: output system configs info into 2 files:[ (/out/${product_name}/build_configs/subsystem_info/${platform}-stub/BUILG.gn), (/out/${product_name}/build_configs/subsystem_info/${platform}-stub/zframework_stub_exists.gni)] @parameter: none @return :none ''' def _generate_stub_targets(self): generate_targets_gn.gen_stub_targets( self.parts_config_info.get('parts_kits_info'), self.target_platform_stubs, self.config_output_dir) '''Description: output system capabilities into a json file. \ (/out/${product_name}/build_configs/${platform}_system_capabilities.json) @parameter: none @return :none ''' def _generate_system_capabilities(self): for platform in self.build_platforms: platform_parts = self.target_platform_parts.get(platform) platform_capabilities = [] for _, origin in platform_parts.items(): # parts_info.get() might be None if the part is a binary package all_parts_variants = self.parts_info.get(origin) if all_parts_variants is None: continue part = all_parts_variants[0] if part.get('system_capabilities'): entry = part.get('system_capabilities') if len(entry) > 0: platform_capabilities.extend(entry) platform_part_json_file = os.path.join( self.config_output_dir, "{0}_system_capabilities.json".format(platform)) write_json_file(platform_part_json_file, sorted(platform_capabilities), check_changes=True) LogUtil.hb_info( "generated system capabilities to '{}/{}_system_capabilities.json'".format( self.config_output_dir, platform)) '''Description: output system configs info into three json files:[ (/out/${product_name}/build_configs/subsystem_info/subsystem_build_config.json), (/out/${product_name}/build_configs/subsystem_info/src_subsystem_info.json), (/out/${product_name}/build_configs/subsystem_info/no_src_subsystem_info.json)] @parameter: none @return :none ''' def _generate_subsystem_configs(self): # The function has been implemented in module util/loader/subsystem_info.py LogUtil.hb_info( "generated subsystem build config to '{}/subsystem_info/subsystem_build_config.json'".format( self.config_output_dir)) LogUtil.hb_info( "generated src subsystem info to '{}/subsystem_info/src_subsystem_info.json'".format( self.config_output_dir)) LogUtil.hb_info( "generated no src subsystem info to '{}/subsystem_info/no_src_subsystem_info.json'".format( self.config_output_dir)) # get method @throw_exception def _get_build_platforms(self) -> list: build_platforms = [] if self.build_platform_name == 'all': build_platforms = self._all_platforms elif self.build_platform_name in self._all_platforms: build_platforms = [self.build_platform_name] else: raise OHOSException( "The target_platform is incorrect, only allows [{}].".format( ', '.join(self._all_platforms)), "2010") return build_platforms def _get_parts_by_platform(self) -> dict: parts_info = {} if 'phone' in self.target_platform_parts: phone_parts_list = self.target_platform_parts.get('phone').keys() else: phone_parts_list = [] for _platform, _parts_info in self.target_platform_parts.items(): base_parts_list = [] curr_parts_list = [] for _real_name, _original_name in _parts_info.items(): if _real_name in phone_parts_list: base_parts_list.append(_real_name) elif _original_name in phone_parts_list: base_parts_list.append(_real_name) else: curr_parts_list.append(_real_name) result_data = { "base_parts_list": base_parts_list, "curr_parts_list": curr_parts_list } parts_info[_platform] = result_data return parts_info def _get_platforms_all_parts(self) -> dict: _dist_parts_variants = self._load_component_dist() target_platform_parts = {} all_parts = self._platforms_info.get('all_parts') parts_variants = self.parts_config_info.get('parts_variants') for _platform, _parts in all_parts.items(): if _platform not in self.build_platforms: continue part_name_info = {} for part_def in _parts: real_name, original_name = self._get_real_part_name( part_def, _platform, parts_variants) if real_name is None: # find this from component_dist real_name, original_name = self._get_real_part_name( part_def, _platform, _dist_parts_variants) if real_name is None: continue part_name_info[real_name] = original_name target_platform_parts[_platform] = part_name_info return target_platform_parts def _get_platforms_all_stubs(self) -> dict: _dist_parts_variants = self._load_component_dist() platform_stubs = {} all_stubs = self._platforms_info.get('all_stubs') parts_variants = self.parts_config_info.get('parts_variants') for _platform, _part_names in all_stubs.items(): if _platform not in self.build_platforms: continue stub_parts_from_src = [] stub_parts_from_dist = [] for part_name in _part_names: real_name, original_name = self._get_real_part_name( part_name, _platform, parts_variants) # real_name=None means part_name doesn't exist in source tree, # use binary in component_dist then. if real_name is None: # find this from component_dist real_name, original_name = self._get_real_part_name( part_name, _platform, _dist_parts_variants) if real_name is None: continue else: stub_sources = os.path.join( self.source_root_dir, "component_dist/{}-{}/api_stubs/{}/stubs_sources_list.txt" # noqa: E501 .format(self.target_os, self.target_cpu, real_name)) stub_parts_from_dist.append( '"{}"'.format(stub_sources)) else: stub_parts_from_src.append(real_name) platform_stubs[_platform] = { "src": stub_parts_from_src, "dist": stub_parts_from_dist, } return platform_stubs def _get_platforms_parts(self) -> dict: platforms_parts = {} src_parts_targets = self.parts_targets src_all_parts = src_parts_targets.keys() for _platform, _all_parts in self.target_platform_parts.items(): src_parts_list = [] no_src_parts_list = [] for _part in _all_parts.keys(): if _part in src_all_parts: src_parts_list.append(_part) else: no_src_parts_list.append(_part) _data = { 'src_parts': src_parts_list, 'no_src_parts': no_src_parts_list } platforms_parts[_platform] = _data return platforms_parts def _get_parts_src_list(self) -> list: parts_name_map = {} for _list in self.parts_info.values(): for _info in _list: parts_name_map[_info.get('part_name')] = _info.get( 'origin_part_name') _src_set = set() for _name in self.required_parts_targets.keys(): _origin_name = parts_name_map.get(_name) if _origin_name is None: continue _src_set.add(_origin_name) return list(_src_set) def _get_required_build_targets(self) -> dict: required_build_targets = {} for _p_name, _info in self.parts_targets.items(): if _p_name not in self.required_parts_targets_list: continue required_build_targets[_p_name] = _info return required_build_targets def _get_required_phony_targets(self) -> dict: required_build_targets = {} for _p_name, _info in self.phony_targets.items(): if _p_name not in self.required_parts_targets_list: continue required_build_targets[_p_name] = _info return required_build_targets def _get_required_build_parts_list(self) -> list: parts_set = set() for _parts_list in self.target_platform_parts.values(): parts_set.update(_parts_list) return list(parts_set) # util method def _load_component_dist(self) -> dict: _parts_variants_info = {} _dir = "component_dist/{}-{}/packages_to_install".format( self.target_os, self.target_cpu) _file_name = "dist_parts_info.json" _dist_parts_info_file = os.path.join( self.source_root_dir, _dir, _file_name) if not os.path.exists(_dist_parts_info_file): # If the file does not exist, do nothing and return return _parts_variants_info _parts_info = read_json_file(_dist_parts_info_file) if _parts_info is None: raise Exception("read file '{}' failed.".format( _dist_parts_info_file)) for _part_info in _parts_info: origin_part_name = _part_info.get('origin_part_name') if origin_part_name in _parts_variants_info: variants = _parts_variants_info.get(origin_part_name) else: variants = [] _variant_name = _part_info.get('variant_name') variants.append(_variant_name) _parts_variants_info[origin_part_name] = variants return _parts_variants_info def _get_real_part_name(self, original_part_name, current_platform, parts_variants): part_info = parts_variants.get(original_part_name) if part_info is None: return None, None if current_platform in part_info and current_platform != 'phone': real_name = '{}_{}'.format(original_part_name, current_platform) else: real_name = original_part_name return real_name, original_part_name '''Description: called by _out_infos_for_testfwk, output information by platform @parameter:none @return :none ''' def _output_infos_by_platform(self, part_name_infos, parts_info_dict): required_parts = {} subsystem_infos = {} for part_name, origin_part_name in part_name_infos.items(): part_info = parts_info_dict.get(part_name) if part_info is None: continue if origin_part_name != part_info.get('origin_part_name'): raise Exception("part configuration is incorrect.") required_parts[origin_part_name] = part_info _subsystem_name = part_info.get('subsystem_name') if _subsystem_name in subsystem_infos: p_list = subsystem_infos.get(_subsystem_name) else: p_list = [] p_list.append(origin_part_name) subsystem_infos[_subsystem_name] = p_list result = {} result['subsystem_infos'] = subsystem_infos result['part_infos'] = required_parts return result def _execute_loader_args_display(self): LogUtil.hb_info('Loading configuration file...') args = [] args.append('platforms_config_file="{}"'.format( self.platforms_config_file)) args.append('subsystem_config_file="{}"'.format( self.subsystem_config_file)) args.append('example_subsystem_file="{}"'.format( self.example_subsystem_file)) args.append('exclusion_modules_config_file="{}"'.format( self.exclusion_modules_config_file)) args.append('source_root_dir="{}"'.format(self.source_root_dir)) args.append('gn_root_out_dir="{}"'.format(self.gn_root_out_dir)) args.append('build_platform_name={}'.format(self.build_platform_name)) args.append('build_xts={}'.format(self.build_xts)) args.append('load_test_config={}'.format(self.load_test_config)) args.append('target_os={}'.format(self.target_os)) args.append('target_cpu={}'.format(self.target_cpu)) args.append('os_level={}'.format(self.os_level)) args.append('ignore_api_check={}'.format(self.ignore_api_check)) args.append('scalable_build={}'.format(self.scalable_build)) args.append('skip_partlist_check={}'.format(self.skip_partlist_check)) LogUtil.write_log(self.config.log_path, 'loader args:{}'.format(args), 'info') def _override_components(self): '''Description: Check whether there are components that need to be replaced, and if so, replace the component configuration file bundle.json in subsystem_info and update the component list generated by the preloader. @parameter:none @return :overrided_components ''' parts_file = self.platforms_config_file.replace( "platforms.build", "parts.json") all_parts = read_json_file(parts_file) if "parts" not in all_parts: LogUtil.hb_warning("{} does not contain parts!".format(parts_file)) return {} overrided = False overrided_components = {} all_parts = all_parts["parts"] component_override_map = {} for subsystem_name, build_config_info in self._subsystem_info.items(): if "build_files" not in build_config_info: continue # scan all bundle.json or ohos.build files with named groups for build_file in build_config_info["build_files"]: # ohos.build does not support overrided components if not build_file.endswith('bundle.json'): continue # Only device or vendor components can do named groups extensions if (not build_file.startswith(self.source_root_dir + 'device/')) \ and (not build_file.startswith(self.source_root_dir + 'vendor/')): continue # "subsystem", "name" and "override" is required component = read_json_file(build_file).get("component") if (not component) or (not all(key in component for key in ("subsystem", "name", "override"))): continue full_part_name = f"{component.get('subsystem')}:{component.get('name')}" if full_part_name not in all_parts: LogUtil.hb_warning("{} was not configured for this product: {}".format( build_file, full_part_name)) continue if self._override_one_component(self._subsystem_info, component, build_file, all_parts, overrided_components, component_override_map): overrided = True if overrided: # Update parts.json and parts_config.json generated by preloader write_json_file(parts_file, {"parts": all_parts}) parts_file = self.platforms_config_file.replace( "platforms.build", "parts_config.json") self._output_parts_config_json(all_parts, parts_file) write_json_file( f"{self.config_output_dir}/component_override_map.json", component_override_map) return overrided_components def _override_one_component(self, subsystem_info, component, build_file, all_parts, overrided_components, component_override_map): '''Description: Perform a replacement of a single component and return the component list update result. @parameter:subsystem_info, component, build_file, all_parts, overrided_components @return :True or False(Whether replacement has been performed) ''' splits = component["override"].split(":") if len(splits) != 2: LogUtil.hb_warning( "{} override value is invalid format. Skip override process".format(build_file)) return False overrided_subsystem = splits[0] overrided_component = splits[1] if overrided_subsystem not in subsystem_info: LogUtil.hb_warning( "{} override invalid subsystem. Skip override process".format(build_file)) return False founded_bundle = "" for bundle in subsystem_info[overrided_subsystem]["build_files"]: if not bundle.endswith('bundle.json'): continue bundle_obj = read_json_file(bundle) if bundle_obj.get("component", {}).get("name") == overrided_component: founded_bundle = bundle break if founded_bundle: origin_component = read_json_file(build_file).get('component') LogUtil.hb_warning( 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\"") # replace bundle.json in subsystem_info's build_files subsystem_info[overrided_subsystem]["build_files"].remove( founded_bundle) # Update parts.json generated by preloader, which means that new added components will not be installed # Ensure that the overrided components will be installed full_partname = f"{overrided_subsystem}:{overrided_component}" if full_partname in all_parts: all_parts.remove(full_partname) overrided_components[f"{component['subsystem']}:{component['name']}"] = { 'subsystem': overrided_subsystem, 'partName': overrided_component } component_override_map[overrided_component] = component["name"] return True LogUtil.hb_warning( "{}:{} is not configured in product, \new add component will be installed!".format( overrided_subsystem, overrided_component)) return False def _output_parts_config_json(self, all_parts, output_file): '''Description: Update the parts list file generated by preloader @parameter: all_parts, output_file @return :none ''' parts_config = {} for part in all_parts: part = part.replace(":", "_") part = part.replace("-", "_") part = part.replace(".", "_") part = part.replace("/", "_") parts_config[part] = True write_json_file(output_file, parts_config)