• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
51class Dirs:
52
53    def __init__(self, config):
54        self.__post_init__(config)
55
56    def __post_init__(self, config):
57        self.source_root_dir = config.root_path
58        self.built_in_product_dir = config.built_in_product_path
59        self.productdefine_dir = os.path.join(
60            self.source_root_dir, 'productdefine/common')
61        self.built_in_base_dir = os.path.join(self.productdefine_dir, 'base')
62
63        # Configs of vendor specified products are stored in ${vendor_dir} directory.
64        self.vendor_dir = config.vendor_path
65        # Configs of device specified products are stored in ${device_dir} directory.
66        self.device_dir = os.path.join(config.root_path, 'device')
67
68        self.subsystem_config_json = os.path.join(
69            config.root_path, config.subsystem_config_json)
70        self.subsystem_config_overlay_json = os.path.join(config.product_path,
71                                                          'subsystem_config_overlay.json')
72        self.lite_components_dir = os.path.join(
73            config.root_path, 'build/lite/components')
74
75        self.preloader_output_dir = os.path.join(
76            config.root_path, 'out/preloader', config.product)
77
78
79class Product():
80
81    def __init__(self, config_dirs: Dirs, ohos_config: Config):
82        self._ohos_config = None
83        self._dirs = None
84        self._name = ""
85        self._config = {}
86        self._build_vars = {}
87        self._parts = {}
88        self._syscap_info = {}
89        self._device_name = ""
90        self._device_info = {}
91        self._config_file = ""
92        self._version = ''
93        self.__post_init__(config_dirs, ohos_config)
94
95    def __post_init__(self, config_dirs: Dirs, config: Config):
96        self._ohos_config = config
97        self._dirs = config_dirs
98        self._name = config.product
99        self._config_file = config.product_json
100        self._config = self._get_full_product_config()
101        self._version = self._config.get('version', '3.0')
102        self._do_parse()
103
104    # parse product configuration, then generate parts list and build vars
105    def _do_parse(self):
106        self._update_syscap_info()
107        self._update_device()
108        self._update_parts()
109        self._update_build_vars()
110        self._remove_excluded_components()
111
112# update and remove
113
114    # Update the syscap info
115    def _update_syscap_info(self):
116        product_name = self._config.get('product_name')
117        if product_name is None:
118            product_name = ""
119        os_level = self._config.get('type')
120        if os_level is None:
121            os_level = ""
122        api_version = self._config.get('api_version')
123        if api_version is None:
124            api_version = 0
125        manufacturer_id = self._config.get('manufacturer_id')
126        if manufacturer_id is None:
127            manufacturer_id = 0
128        self._syscap_info = {'product': product_name, 'api_version': api_version,
129                             'system_type': os_level, 'manufacturer_id': manufacturer_id}
130
131    # Update the _device_name and _device_info based on the product configuration in the vendor warehouse
132    def _update_device(self):
133        if self._version == "2.0":
134            device_name = self._config.get('product_device')
135            if device_name:
136                self._device_name = device_name
137                self._device_info = self._get_device_info_v2(
138                    device_name, self._dirs.built_in_device_dir)
139        else:
140            device_name = self._config.get('board')
141            if device_name:
142                self._device_name = device_name
143                self._device_info = self._get_device_info_v3(self._config)
144        if self._ohos_config.target_cpu:
145            self._device_info["target_cpu"] = self._ohos_config.target_cpu
146        if self._ohos_config.target_os:
147            self._device_info["target_os"] = self._ohos_config.target_os
148        if self._ohos_config.compile_config:
149            self._device_info[self._ohos_config["compile_config"]] = True
150
151    # Update the _parts based on the product configuration in the vendor warehouse
152    def _update_parts(self):
153        if self._version == "1.0":
154            _parts = {}
155            self._parts = _parts
156        else:
157            # 1. inherit parts information from base config
158            if self._version == "2.0":
159                os_level = self._config.get("type", "standard")
160            else:
161                os_level = self._config.get("type", "mini")
162            # 2. product config based on default minimum system
163            based_on_mininum_system = self._config.get(
164                'based_on_mininum_system')
165            if based_on_mininum_system == "true":
166                self._parts = self._get_base_parts(
167                    self._dirs.built_in_base_dir, os_level)
168            # 3. inherit parts information from inherit config
169            inherit = self._config.get('inherit')
170            if inherit:
171                self._parts.update(
172                    self._get_inherit_parts(inherit, self._dirs.source_root_dir))
173
174            # 4. chipset products relate system parts config
175            sys_info_path = self._config.get('system_component')
176            if sys_info_path:
177                sys_parts = self._get_sys_relate_parts(
178                    sys_info_path, self._parts, self._dirs.source_root_dir)
179                self._parts.update(sys_parts)
180            all_parts = {}
181            if self._version == "2.0":
182                current_product_parts = self._config.get("parts")
183                if current_product_parts:
184                    all_parts.update(current_product_parts)
185            else:
186                all_parts.update(get_vendor_parts_list(self._config))
187                all_parts.update(self._get_product_specific_parts())
188
189                device_name = self._config.get('board')
190                if device_name:
191                    all_parts.update(self._get_device_specific_parts())
192            self._parts.update(all_parts)
193
194    # Update the _build_vars based on the product configuration in the vendor warehouse
195    def _update_build_vars(self):
196        config = self._config
197        build_vars = {}
198        if self._version == "1.0":
199            build_vars = {"os_level": 'large'}
200        else:
201            if self._version == "2.0":
202                build_vars['os_level'] = config.get("type", "standard")
203                device_name = config.get('product_device')
204                if device_name:
205                    build_vars['device_name'] = device_name
206                else:
207                    build_vars['device_name'] = ''
208                build_vars['product_company'] = config.get('product_company')
209            else:
210                build_vars['os_level'] = config.get('type', 'mini')
211                build_vars['device_name'] = config.get('board')
212                if config.get('product_company'):
213                    build_vars['product_company'] = config.get(
214                        'product_company')
215                elif os.path.dirname(self._config_file) != self._dirs.built_in_product_dir:
216                    relpath = os.path.relpath(
217                        self._config_file, self._dirs.vendor_dir)
218                    build_vars['product_company'] = relpath.split('/')[0]
219                else:
220                    build_vars['product_company'] = config.get(
221                        'device_company')
222            build_vars['product_name'] = config.get('product_name')
223            if 'ext_root_proc_conf_path' in config:
224                ext_root_proc_conf_path = os.path.join(
225                    self._dirs.source_root_dir, config.get('ext_root_proc_conf_path'))
226                if os.path.exists(ext_root_proc_conf_path):
227                    build_vars['ext_root_proc_conf_path'] = ext_root_proc_conf_path
228            if 'ext_critical_proc_conf_path' in config:
229                ext_critical_proc_conf_path = os.path.join(
230                    self._dirs.source_root_dir, config.get('ext_critical_proc_conf_path'))
231                if os.path.exists(ext_critical_proc_conf_path):
232                    build_vars['ext_critical_proc_conf_path'] = ext_critical_proc_conf_path
233            if 'ext_sanitizer_check_list_path' in config:
234                ext_sanitizer_check_list_path = os.path.join(
235                    self._dirs.source_root_dir, config.get('ext_sanitizer_check_list_path'))
236                if os.path.exists(ext_sanitizer_check_list_path):
237                    build_vars['ext_sanitizer_check_list_path'] = ext_sanitizer_check_list_path
238            if 'enable_ramdisk' in config:
239                build_vars['enable_ramdisk'] = config.get('enable_ramdisk')
240            if 'enable_absystem' in config:
241                build_vars['enable_absystem'] = config.get('enable_absystem')
242            if 'build_selinux' in config:
243                build_vars['build_selinux'] = config.get('build_selinux')
244            if 'build_seccomp' in config:
245                build_vars['build_seccomp'] = config.get('build_seccomp')
246            if 'support_jsapi' in config:
247                build_vars['support_jsapi'] = config.get('support_jsapi')
248            if 'chipprod_config_path' in config:
249                chipprod_config_path = os.path.join(
250                    self._dirs.source_root_dir, config.get('chipprod_config_path'))
251                if os.path.exists(chipprod_config_path):
252                    build_vars['chipprod_config_path'] = chipprod_config_path
253            if 'ext_sdk_config_file' in config:
254                ext_sdk_config_file = os.path.join(
255                    self._dirs.source_root_dir, config.get('ext_sdk_config_file'))
256                if os.path.exists(ext_sdk_config_file):
257                    build_vars['ext_sdk_config_file'] = ext_sdk_config_file
258            if 'ext_ndk_config_file' in config:
259                ext_ndk_config_file = os.path.join(
260                    self._dirs.source_root_dir, config.get('ext_ndk_config_file'))
261                if os.path.exists(ext_ndk_config_file):
262                    build_vars['ext_ndk_config_file'] = ext_ndk_config_file
263            if 'ext_sign_hap_py_path' in config:
264                path = os.path.join(
265                    self._dirs.source_root_dir, config.get('ext_sign_hap_py_path'))
266                if os.path.exists(path):
267                    build_vars['ext_sign_hap_py_path'] = path
268
269        build_vars.update(self._device_info)
270        if build_vars['os_level'] == 'mini' or build_vars['os_level'] == 'small':
271            toolchain_label = ""
272        else:
273            toolchain_label = '//build/toolchain/{0}:{0}_clang_{1}'.format(
274                self._device_info.get('target_os'), self._device_info.get('target_cpu'))
275        build_vars['product_toolchain_label'] = toolchain_label
276        self._build_vars = build_vars
277
278    # Remove excluded components
279    def _remove_excluded_components(self):
280        items_to_remove = []
281        for part, val in self._parts.items():
282            if "exclude" in val and val["exclude"] == "true":
283                items_to_remove.append(part)
284        for item in items_to_remove:
285            del self._parts[item]
286
287# get method
288
289    # Generate build_info needed for V2 configuration
290    def _get_device_info_v2(self, device_name, config_dir) -> dict:
291        device_config_file = os.path.join(config_dir,
292                                          '{}.json'.format(device_name))
293        device_info = IoUtil.read_json_file(device_config_file)
294        if device_info and device_info.get('device_name') != device_name:
295            raise Exception("device name configuration incorrect in '{}'".format(
296                device_config_file))
297        return device_info
298
299    # Generate build_info needed for V3 configuration
300    def _get_device_info_v3(self, config) -> dict:
301        # NOTE:
302        # Product_name, device_company are necessary for
303        # config.json, DON NOT use .get to replace []
304        device_info = {
305            'device_name': config['board'],
306            'device_company': config['device_company']
307        }
308        if config.get('target_os'):
309            device_info['target_os'] = config.get('target_os')
310        else:
311            device_info['target_os'] = 'ohos'
312        if config.get('target_cpu'):
313            device_info['target_cpu'] = config['target_cpu']
314        else:
315            # Target cpu is used to set default toolchain for standard system.
316            LogUtil.hb_warning(
317                "The target_cpu needs to be specified, default target_cpu=arm")
318            device_info['target_cpu'] = 'arm'
319        if config.get('kernel_version'):
320            device_info['kernel_version'] = config.get('kernel_version')
321        if config.get('device_build_path'):
322            device_info['device_build_path'] = config.get('device_build_path')
323        else:
324            device_build_path = os.path.join(self._dirs.device_dir,
325                                             config['device_company'],
326                                             config['board'])
327            if not os.path.exists(device_build_path):
328                device_build_path = os.path.join(self._dirs.device_dir,
329                                                 'board',
330                                                 config['device_company'],
331                                                 config['board'])
332            device_info['device_build_path'] = device_build_path
333        return device_info
334
335    def _get_device_specific_parts(self) -> dict:
336        info = {}
337        if self._device_info and self._device_info.get('device_build_path'):
338            subsystem_name = 'device_{}'.format(self._device_name)
339            part_name = subsystem_name
340            info['{}:{}'.format(subsystem_name, part_name)] = {}
341        return info
342
343    def _get_device_specific_subsystem(self) -> dict:
344        info = {}
345        subsystem_name = 'device_{}'.format(self._device_name)
346        if self._device_info and self._device_info.get('device_build_path'):
347            info[subsystem_name] = {
348                'name': subsystem_name,
349                'path': self._device_info.get('device_build_path')
350            }
351        return info
352
353    def _get_base_parts(self, base_config_dir, os_level) -> dict:
354        system_base_config_file = os.path.join(base_config_dir,
355                                               '{}_system.json'.format(os_level))
356        if not os.path.exists(system_base_config_file):
357            raise Exception("product configuration '{}' doesn't exist.".format(
358                system_base_config_file))
359        return IoUtil.read_json_file(system_base_config_file)
360
361    def _get_inherit_parts(self, inherit, source_root_dir) -> dict:
362        inherit_parts = {}
363        for _config in inherit:
364            _file = os.path.join(source_root_dir, _config)
365            _info = IoUtil.read_json_file(_file)
366            parts = _info.get('parts')
367            if parts:
368                inherit_parts.update(parts)
369            else:
370                inherit_parts.update(get_vendor_parts_list(_info))
371        return inherit_parts
372
373    def _get_sys_relate_parts(self, system_component_info, _parts, source_root_dir) -> dict:
374        _info = IoUtil.read_json_file(os.path.join(
375            source_root_dir, system_component_info))
376        ret = {}
377        parts = _info.get('parts')
378        if not parts:
379            parts = get_vendor_parts_list(_info)
380        for part, featrue in parts.items():
381            if not _parts.get(part):
382                ret[part] = featrue
383        return ret
384
385    def _get_product_specific_parts(self) -> dict:
386        part_name = 'product_{}'.format(self._name)
387        subsystem_name = part_name
388        info = {}
389        info['{}:{}'.format(subsystem_name, part_name)] = {}
390        return info
391
392    def _get_product_specific_subsystem(self) -> dict:
393        info = {}
394        subsystem_name = 'product_{}'.format(self._name)
395        product_build_path = self._config.get('product_build_path')
396        if product_build_path:
397            info[subsystem_name] = {
398                'name': subsystem_name,
399                'path': product_build_path
400            }
401        return info
402
403    def _get_full_product_config(self) -> dict:
404        config = IoUtil.read_json_file(self._config_file)
405        if config.get("version") != '2.0':
406            if os.path.dirname(self._config_file) != self._dirs.built_in_product_dir \
407                    and not hasattr(self._config, 'product_build_path'):
408                config['product_build_path'] = os.path.relpath(
409                    os.path.dirname(self._config_file), self._dirs.source_root_dir)
410        return config
411