1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2023 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16 17import os 18 19from resources.config import Config 20from util.log_util import LogUtil 21from util.io_util import IoUtil 22from util.preloader.parse_vendor_product_config import get_vendor_parts_list 23 24 25class Outputs: 26 27 def __init__(self, output_dir): 28 self.__post_init__(output_dir) 29 30 def __post_init__(self, output_dir): 31 os.makedirs(output_dir, exist_ok=True) 32 self.build_prop = os.path.join(output_dir, 'build.prop') 33 self.build_config_json = os.path.join(output_dir, 'build_config.json') 34 self.parts_json = os.path.join(output_dir, 'parts.json') 35 self.parts_config_json = os.path.join(output_dir, 'parts_config.json') 36 self.build_gnargs_prop = os.path.join(output_dir, 'build_gnargs.prop') 37 self.features_json = os.path.join(output_dir, 'features.json') 38 self.syscap_json = os.path.join(output_dir, 'syscap.json') 39 self.exclusion_modules_json = os.path.join(output_dir, 40 'exclusion_modules.json') 41 self.subsystem_config_json = os.path.join(output_dir, 42 'subsystem_config.json') 43 self.subsystem_config_overlay_json = os.path.join(output_dir, 44 'subsystem_config_overlay.json') 45 self.platforms_build = os.path.join(output_dir, 'platforms.build') 46 self.systemcapability_json = os.path.join( 47 output_dir, 'SystemCapability.json') 48 self.compile_standard_whitelist_json = os.path.join(output_dir, 'compile_standard_whitelist.json') 49 self.compile_env_allowlist_json = os.path.join(output_dir, 'compile_env_allowlist.json') 50 self.hvigor_compile_whitelist_json = os.path.join(output_dir, 'hvigor_compile_hap_whitelist.json') 51 52 53class Dirs: 54 55 def __init__(self, config): 56 self.__post_init__(config) 57 58 def __post_init__(self, config): 59 self.source_root_dir = config.root_path 60 self.built_in_product_dir = config.built_in_product_path 61 self.productdefine_dir = os.path.join( 62 self.source_root_dir, 'productdefine/common') 63 self.built_in_base_dir = os.path.join(self.productdefine_dir, 'base') 64 65 # Configs of vendor specified products are stored in ${vendor_dir} directory. 66 self.vendor_dir = config.vendor_path 67 # Configs of device specified products are stored in ${device_dir} directory. 68 self.device_dir = os.path.join(config.root_path, 'device') 69 70 self.subsystem_config_json = os.path.join( 71 config.root_path, config.subsystem_config_json) 72 self.subsystem_config_overlay_json = os.path.join(config.product_path, 73 'subsystem_config_overlay.json') 74 self.lite_components_dir = os.path.join( 75 config.root_path, 'build/lite/components') 76 77 self.preloader_output_dir = os.path.join( 78 config.root_path, 'out/preloader', config.product) 79 80 81class Product(): 82 83 def __init__(self, config_dirs: Dirs, ohos_config: Config): 84 self._ohos_config = None 85 self._dirs = None 86 self._name = "" 87 self._config = {} 88 self._build_vars = {} 89 self._parts = {} 90 self._syscap_info = {} 91 self._device_name = "" 92 self._device_info = {} 93 self._config_file = "" 94 self._version = '' 95 self.__post_init__(config_dirs, ohos_config) 96 97 def __post_init__(self, config_dirs: Dirs, config: Config): 98 self._ohos_config = config 99 self._dirs = config_dirs 100 self._name = config.product 101 self._config_file = config.product_json 102 self._config = self._get_full_product_config() 103 self._version = self._config.get('version', '3.0') 104 self._do_parse() 105 106 # parse product configuration, then generate parts list and build vars 107 def _do_parse(self): 108 self._update_syscap_info() 109 self._update_device() 110 self._update_parts() 111 self._update_build_vars() 112 self._remove_excluded_components() 113 114# update and remove 115 116 # Update the syscap info 117 def _update_syscap_info(self): 118 product_name = self._config.get('product_name') 119 if product_name is None: 120 product_name = "" 121 os_level = self._config.get('type') 122 if os_level is None: 123 os_level = "" 124 api_version = self._config.get('api_version') 125 if api_version is None: 126 api_version = 0 127 manufacturer_id = self._config.get('manufacturer_id') 128 if manufacturer_id is None: 129 manufacturer_id = 0 130 self._syscap_info = {'product': product_name, 'api_version': api_version, 131 'system_type': os_level, 'manufacturer_id': manufacturer_id} 132 133 # Update the _device_name and _device_info based on the product configuration in the vendor warehouse 134 def _update_device(self): 135 if self._version == "2.0": 136 device_name = self._config.get('product_device') 137 if device_name: 138 self._device_name = device_name 139 self._device_info = self._get_device_info_v2( 140 device_name, self._dirs.built_in_device_dir) 141 else: 142 device_name = self._config.get('board') 143 if device_name: 144 self._device_name = device_name 145 self._device_info = self._get_device_info_v3(self._config) 146 if self._ohos_config.target_cpu: 147 self._device_info["target_cpu"] = self._ohos_config.target_cpu 148 if self._ohos_config.target_os: 149 self._device_info["target_os"] = self._ohos_config.target_os 150 if self._ohos_config.compile_config: 151 self._device_info[self._ohos_config["compile_config"]] = True 152 153 # Update the _parts based on the product configuration in the vendor warehouse 154 def _update_parts(self): 155 if self._version == "1.0": 156 _parts = {} 157 self._parts = _parts 158 else: 159 # 1. inherit parts information from base config 160 if self._version == "2.0": 161 os_level = self._config.get("type", "standard") 162 else: 163 os_level = self._config.get("type", "mini") 164 # 2. product config based on default minimum system 165 based_on_mininum_system = self._config.get( 166 'based_on_mininum_system') 167 if based_on_mininum_system == "true": 168 self._parts = self._get_base_parts( 169 self._dirs.built_in_base_dir, os_level) 170 # 3. inherit parts information from inherit config 171 inherit = self._config.get('inherit') 172 if inherit: 173 self._parts.update( 174 self._get_inherit_parts(inherit, self._dirs.source_root_dir)) 175 176 # 4. chipset products relate system parts config 177 sys_info_path = self._config.get('system_component') 178 if sys_info_path: 179 sys_parts = self._get_sys_relate_parts( 180 sys_info_path, self._parts, self._dirs.source_root_dir) 181 self._parts.update(sys_parts) 182 all_parts = {} 183 if self._version == "2.0": 184 current_product_parts = self._config.get("parts") 185 if current_product_parts: 186 all_parts.update(current_product_parts) 187 else: 188 all_parts.update(get_vendor_parts_list(self._config)) 189 all_parts.update(self._get_product_specific_parts()) 190 191 device_name = self._config.get('board') 192 if device_name: 193 all_parts.update(self._get_device_specific_parts()) 194 self._parts.update(all_parts) 195 196 # Update the _build_vars based on the product configuration in the vendor warehouse 197 def _update_build_vars(self): 198 config = self._config 199 build_vars = {} 200 if self._version == "1.0": 201 build_vars = {"os_level": 'large'} 202 else: 203 if self._version == "2.0": 204 build_vars['os_level'] = config.get("type", "standard") 205 device_name = config.get('product_device') 206 if device_name: 207 build_vars['device_name'] = device_name 208 else: 209 build_vars['device_name'] = '' 210 build_vars['product_company'] = config.get('product_company') 211 else: 212 build_vars['os_level'] = config.get('type', 'mini') 213 build_vars['device_name'] = config.get('board') 214 if config.get('product_company'): 215 build_vars['product_company'] = config.get( 216 'product_company') 217 elif os.path.dirname(self._config_file) != self._dirs.built_in_product_dir: 218 relpath = os.path.relpath( 219 self._config_file, self._dirs.vendor_dir) 220 build_vars['product_company'] = relpath.split('/')[0] 221 else: 222 build_vars['product_company'] = config.get( 223 'device_company') 224 build_vars['product_name'] = config.get('product_name') 225 if 'ext_root_proc_conf_path' in config: 226 ext_root_proc_conf_path = os.path.join( 227 self._dirs.source_root_dir, config.get('ext_root_proc_conf_path')) 228 if os.path.exists(ext_root_proc_conf_path): 229 build_vars['ext_root_proc_conf_path'] = ext_root_proc_conf_path 230 if 'ext_critical_proc_conf_path' in config: 231 ext_critical_proc_conf_path = os.path.join( 232 self._dirs.source_root_dir, config.get('ext_critical_proc_conf_path')) 233 if os.path.exists(ext_critical_proc_conf_path): 234 build_vars['ext_critical_proc_conf_path'] = ext_critical_proc_conf_path 235 if 'ext_sanitizer_check_list_path' in config: 236 ext_sanitizer_check_list_path = os.path.join( 237 self._dirs.source_root_dir, config.get('ext_sanitizer_check_list_path')) 238 if os.path.exists(ext_sanitizer_check_list_path): 239 build_vars['ext_sanitizer_check_list_path'] = ext_sanitizer_check_list_path 240 _global_ext_var_file = os.path.join( 241 self._dirs.source_root_dir, "out/products_ext", "global_ext_var_file.gni") 242 if os.path.exists(_global_ext_var_file): 243 build_vars['global_ext_var_file'] = _global_ext_var_file 244 if 'enable_ramdisk' in config: 245 build_vars['enable_ramdisk'] = config.get('enable_ramdisk') 246 if 'enable_absystem' in config: 247 build_vars['enable_absystem'] = config.get('enable_absystem') 248 if 'build_selinux' in config: 249 build_vars['build_selinux'] = config.get('build_selinux') 250 if 'build_seccomp' in config: 251 build_vars['build_seccomp'] = config.get('build_seccomp') 252 if 'support_jsapi' in config: 253 build_vars['support_jsapi'] = config.get('support_jsapi') 254 if 'chipprod_config_path' in config: 255 chipprod_config_path = os.path.join( 256 self._dirs.source_root_dir, config.get('chipprod_config_path')) 257 if os.path.exists(chipprod_config_path): 258 build_vars['chipprod_config_path'] = chipprod_config_path 259 if 'ext_sdk_config_file' in config: 260 ext_sdk_config_file = os.path.join( 261 self._dirs.source_root_dir, config.get('ext_sdk_config_file')) 262 if os.path.exists(ext_sdk_config_file): 263 build_vars['ext_sdk_config_file'] = ext_sdk_config_file 264 if 'ext_ndk_config_file' in config: 265 ext_ndk_config_file = os.path.join( 266 self._dirs.source_root_dir, config.get('ext_ndk_config_file')) 267 if os.path.exists(ext_ndk_config_file): 268 build_vars['ext_ndk_config_file'] = ext_ndk_config_file 269 if 'ext_sign_hap_py_path' in config: 270 path = os.path.join( 271 self._dirs.source_root_dir, config.get('ext_sign_hap_py_path')) 272 if os.path.exists(path): 273 build_vars['ext_sign_hap_py_path'] = path 274 275 build_vars.update(self._device_info) 276 if build_vars['os_level'] == 'mini' or build_vars['os_level'] == 'small': 277 toolchain_label = "" 278 else: 279 toolchain_label = '//build/toolchain/{0}:{0}_clang_{1}'.format( 280 self._device_info.get('target_os'), self._device_info.get('target_cpu')) 281 build_vars['product_toolchain_label'] = toolchain_label 282 self._build_vars = build_vars 283 284 # Remove excluded components 285 def _remove_excluded_components(self): 286 items_to_remove = [] 287 for part, val in self._parts.items(): 288 if "exclude" in val and val["exclude"] == "true": 289 items_to_remove.append(part) 290 for item in items_to_remove: 291 del self._parts[item] 292 293# get method 294 295 # Generate build_info needed for V2 configuration 296 def _get_device_info_v2(self, device_name, config_dir) -> dict: 297 device_config_file = os.path.join(config_dir, 298 '{}.json'.format(device_name)) 299 device_info = IoUtil.read_json_file(device_config_file) 300 if device_info and device_info.get('device_name') != device_name: 301 raise Exception("device name configuration incorrect in '{}'".format( 302 device_config_file)) 303 return device_info 304 305 # Generate build_info needed for V3 configuration 306 def _get_device_info_v3(self, config) -> dict: 307 # NOTE: 308 # Product_name, device_company are necessary for 309 # config.json, DON NOT use .get to replace [] 310 device_info = { 311 'device_name': config['board'], 312 'device_company': config['device_company'] 313 } 314 if config.get('target_os'): 315 device_info['target_os'] = config.get('target_os') 316 else: 317 device_info['target_os'] = 'ohos' 318 if config.get('target_cpu'): 319 device_info['target_cpu'] = config['target_cpu'] 320 else: 321 # Target cpu is used to set default toolchain for standard system. 322 LogUtil.hb_warning( 323 "The target_cpu needs to be specified, default target_cpu=arm") 324 device_info['target_cpu'] = 'arm' 325 if config.get('kernel_version'): 326 device_info['kernel_version'] = config.get('kernel_version') 327 if config.get('device_build_path'): 328 device_info['device_build_path'] = config.get('device_build_path') 329 else: 330 device_build_path = os.path.join(self._dirs.device_dir, 331 config['device_company'], 332 config['board']) 333 if not os.path.exists(device_build_path): 334 device_build_path = os.path.join(self._dirs.device_dir, 335 'board', 336 config['device_company'], 337 config['board']) 338 device_info['device_build_path'] = device_build_path 339 return device_info 340 341 def _get_device_specific_parts(self) -> dict: 342 info = {} 343 if self._device_info and self._device_info.get('device_build_path'): 344 subsystem_name = 'device_{}'.format(self._device_name) 345 part_name = subsystem_name 346 info['{}:{}'.format(subsystem_name, part_name)] = {} 347 return info 348 349 def _get_device_specific_subsystem(self) -> dict: 350 info = {} 351 subsystem_name = 'device_{}'.format(self._device_name) 352 if self._device_info and self._device_info.get('device_build_path'): 353 info[subsystem_name] = { 354 'name': subsystem_name, 355 'path': self._device_info.get('device_build_path') 356 } 357 return info 358 359 def _get_base_parts(self, base_config_dir, os_level) -> dict: 360 system_base_config_file = os.path.join(base_config_dir, 361 '{}_system.json'.format(os_level)) 362 if not os.path.exists(system_base_config_file): 363 raise Exception("product configuration '{}' doesn't exist.".format( 364 system_base_config_file)) 365 return IoUtil.read_json_file(system_base_config_file) 366 367 def _get_inherit_parts(self, inherit, source_root_dir) -> dict: 368 inherit_parts = {} 369 for _config in inherit: 370 _file = os.path.join(source_root_dir, _config) 371 _info = IoUtil.read_json_file(_file) 372 parts = _info.get('parts') 373 if parts: 374 inherit_parts.update(parts) 375 else: 376 inherit_parts.update(get_vendor_parts_list(_info)) 377 return inherit_parts 378 379 def _get_sys_relate_parts(self, system_component_info, _parts, source_root_dir) -> dict: 380 _info = IoUtil.read_json_file(os.path.join( 381 source_root_dir, system_component_info)) 382 ret = {} 383 parts = _info.get('parts') 384 if not parts: 385 parts = get_vendor_parts_list(_info) 386 for part, featrue in parts.items(): 387 if not _parts.get(part): 388 ret[part] = featrue 389 return ret 390 391 def _get_product_specific_parts(self) -> dict: 392 part_name = 'product_{}'.format(self._name) 393 subsystem_name = part_name 394 info = {} 395 info['{}:{}'.format(subsystem_name, part_name)] = {} 396 return info 397 398 def _get_product_specific_subsystem(self) -> dict: 399 info = {} 400 subsystem_name = 'product_{}'.format(self._name) 401 product_build_path = self._config.get('product_build_path') 402 if product_build_path: 403 info[subsystem_name] = { 404 'name': subsystem_name, 405 'path': product_build_path 406 } 407 return info 408 409 def _get_full_product_config(self) -> dict: 410 config = IoUtil.read_json_file(self._config_file) 411 if config.get("version") != '2.0': 412 if os.path.dirname(self._config_file) != self._dirs.built_in_product_dir \ 413 and not hasattr(self._config, 'product_build_path'): 414 config['product_build_path'] = os.path.relpath( 415 os.path.dirname(self._config_file), self._dirs.source_root_dir) 416 return config 417