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
16import os
17import sys
18
19from containers.status import throw_exception
20from util.log_util import LogUtil
21from resources.config import Config
22from exceptions.ohos_exception import OHOSException
23from scripts.util.file_utils import read_json_file, write_json_file, \
24    write_file  # noqa: E402, E501  pylint: disable=C0413, E0611
25from . import load_bundle_file
26
27IMPORT_LIST = """
28# import("//build/ohos.gni")
29# import("//build/ohos_var.gni")
30import("//build/ohos/ohos_part.gni")
31import("//build/ohos/ohos_kits.gni")
32import("//build/ohos/ohos_test.gni")
33"""
34
35PART_TEMPLATE = """
36ohos_part("{}") {{
37  subsystem_name = "{}"
38  module_list = [
39    {}
40  ]
41  origin_name = "{}"
42  variant = "{}"
43}}"""
44
45INNER_KITS_TEMPLATE = """
46ohos_inner_kits("{0}_inner_kits") {{
47  sdk_libs = [
48{1}
49  ]
50  part_name = "{2}"
51  origin_name = "{3}"
52  variant = "{4}"
53}}"""
54
55SYSTEM_KITS_TEMPLATE = """
56ohos_system_kits("{0}_system_kits") {{
57  sdk_libs = [
58{1}
59  ]
60  part_name = "{0}"
61  origin_name = "{2}"
62  variant = "{3}"
63}}"""
64
65TEST_TEMPLATE = """
66ohos_part_test("{0}_test") {{
67  testonly = true
68  test_packages = [
69    {1}
70  ]
71  deps = [":{0}"]
72  part_name = "{0}"
73  subsystem_name = "{2}"
74}}"""
75
76
77def _normalize(label, path):
78    if not label.startswith('//'):
79        label = '//{}/{}'.format(path, label)
80    return label
81
82
83@throw_exception
84def get_syscap_from_bundle(bundle_file):
85    if not os.path.exists(bundle_file):
86        raise OHOSException(
87            "config file '{}' doesn't exist.".format(bundle_file), "2014")
88    bundle_config = read_json_file(bundle_file)
89    if bundle_config is None:
90        raise OHOSException(
91            "read file '{}' failed.".format(bundle_file), "2014")
92    part_name = bundle_config.get('component').get('name')
93    part_syscap = bundle_config.get('component').get('syscap')
94    return part_name, part_syscap
95
96
97def read_build_file(ohos_build_file):
98    if not os.path.exists(ohos_build_file):
99        raise OHOSException(
100            "config file '{}' doesn't exist.".format(ohos_build_file), "2014")
101    subsystem_config = read_json_file(ohos_build_file)
102    if subsystem_config is None:
103        raise OHOSException(
104            "read file '{}' failed.".format(ohos_build_file), "2014")
105    return subsystem_config
106
107
108class PartObject(object):
109    """"part object info, description part variant."""
110
111    def __init__(self, part_name, variant_name, part_config, toolchain,
112                 subsystem_name, target_arch, overrided_components):
113        self._origin_name = part_name
114        if variant_name != 'phone':
115            _real_name = '{}_{}'.format(part_name, variant_name)
116        else:
117            _real_name = part_name
118        self._part_name = _real_name
119        self._variant_name = variant_name
120        self._subsystem_name = subsystem_name
121        self._feature_list = []
122        self._toolchain = toolchain
123        self._inner_kits_info = {}
124        self._components_info = {}
125        self._kits = []
126        self._target_arch = target_arch
127        self._system_capabilities = []
128        self._overrided_components = overrided_components
129        self._parsing_config(self._part_name, part_config, subsystem_name)
130
131    @classmethod
132    def _parsing_kits_lib(cls, kit_lib, is_inner_kits=False):
133        lib_config = []
134        lib_type = kit_lib.get('type')
135        if lib_type is None:
136            lib_type = 'so' if is_inner_kits else 'jar'
137        label = kit_lib.get('name')
138        if label is None:
139            raise Exception("kits lib config incorrect, required for name.")
140        lib_config.append('      type = "{}"'.format(lib_type))
141        lib_config.append('      name = "{}"'.format(label))
142        if lib_type == 'so' and 'header' in kit_lib:
143            header = kit_lib.get('header')
144            header_files = header.get('header_files')
145            lib_config.append('      header = {')
146            lib_config.append('        header_files = [')
147            for h_file in header_files:
148                lib_config.append('          "{}",'.format(h_file))
149            lib_config.append('        ]')
150            header_base = header.get('header_base')
151            lib_config.append('        header_base = "{}"'.format(header_base))
152            lib_config.append('      }')
153        if is_inner_kits is False and 'javadoc' in kit_lib:
154            javadoc_val = kit_lib.get('javadoc')
155            lib_config.append('      javadoc = {')
156            resource_dir = javadoc_val.get('resource_dir')
157            lib_config.append(
158                '        resource_dir = "{}"'.format(resource_dir))
159            lib_config.append('      }')
160        return lib_config
161
162    @throw_exception
163    def _parsing_inner_kits(self, part_name, inner_kits_info, build_gn_content,
164                            target_arch):
165        inner_kits_libs_gn = []
166        for inner_kits_lib in inner_kits_info:
167            header = inner_kits_lib.get('header')
168            if header is None:
169                continue
170            inner_kits_libs_gn.append('    {')
171            inner_kits_libs_gn.extend(
172                self._parsing_kits_lib(inner_kits_lib, True))
173            inner_kits_libs_gn.append('    },')
174
175        overrided_part_name, overrided_origin_name = self._overrided_part_name()
176
177        inner_kits_libs_gn_line = '\n'.join(inner_kits_libs_gn)
178        inner_kits_def = INNER_KITS_TEMPLATE.format(part_name,
179                                                    inner_kits_libs_gn_line,
180                                                    overrided_part_name,
181                                                    overrided_origin_name,
182                                                    self._variant_name)
183        build_gn_content.append(inner_kits_def)
184        # output inner kits info to resolve external deps
185        _libs_info = {}
186        for inner_kits_lib in inner_kits_info:
187            info, lib_name, lib_type = self._parsing_base_info(inner_kits_lib, part_name)
188            self._components_info[lib_name] = info
189            # header files
190            header = inner_kits_lib.get('header')
191            if header is None:
192                continue
193            if lib_type == 'so':
194                header_base = header.get('header_base')
195                if header_base is None:
196                    raise OHOSException(
197                        "header base not configuration, part_name = '{}'".
198                        format(part_name), "2014")
199                info['header_base'] = header_base
200                info['header_files'] = header.get('header_files')
201            _libs_info[lib_name] = info
202        self._inner_kits_info = _libs_info
203
204    def part_name(self):
205        """part name."""
206        return self._part_name
207
208    def part_variant(self):
209        """part variant."""
210        return self._variant_name
211
212    def toolchain(self):
213        """current part variant toolchain."""
214        return self._toolchain
215
216    def part_inner_kits(self):
217        """part inner kits."""
218        return self._inner_kits_info
219
220    def part_component_info(self):
221        """part component info."""
222        return self._components_info
223
224    def part_kits(self):
225        """part kits."""
226        return self._kits
227
228    def write_build_gn(self, config_output_dir):
229        """output build gn."""
230        part_gn_file = os.path.join(config_output_dir, self._part_name,
231                                    'BUILD.gn')
232        write_file(part_gn_file, '\n'.join(self._build_gn_content))
233
234    def get_target_label(self, config_output_relpath):
235        """target label."""
236        if config_output_relpath.startswith('/'):
237            raise OHOSException(
238                "args config output relative path is incorrect.", "2003")
239        if self._toolchain == '':
240            return "//{0}/{1}:{1}".format(config_output_relpath,
241                                          self._part_name)
242        else:
243            return "//{0}/{1}:{1}({2})".format(config_output_relpath,
244                                               self._part_name,
245                                               self._toolchain)
246
247    def part_group_targets(self, config_output_relpath):
248        """part group target."""
249        if config_output_relpath.startswith('/'):
250            raise OHOSException(
251                "args config output relative path is incorrect.", "2003")
252        _labels = {}
253        _labels['part'] = self.get_target_label(config_output_relpath)
254        for group_label in self._part_target_list:
255            if group_label == 'phony':
256                _labels[group_label] = "//{0}/{1}:{1}_{2}".format(
257                    config_output_relpath, self._part_name, group_label)
258                continue
259            if self._toolchain == '':
260                _labels[group_label] = "//{0}/{1}:{1}_{2}".format(
261                    config_output_relpath, self._part_name, group_label)
262            else:
263                _labels[group_label] = "//{0}/{1}:{1}_{2}({3})".format(
264                    config_output_relpath, self._part_name, group_label,
265                    self._toolchain)
266        return _labels
267
268    def part_info(self):
269        """part info."""
270        _info = {}
271        _info['part_name'] = self._part_name
272        _info['origin_part_name'] = self._origin_name
273        _info['toolchain_label'] = self._toolchain
274        _info['variant_name'] = self._variant_name
275        _info['subsystem_name'] = self._subsystem_name
276        _info['system_capabilities'] = self._system_capabilities
277
278        if self._feature_list:
279            _info['feature_list'] = self._feature_list
280        if self._variant_name != 'phone':
281            toolchain_name = self._toolchain.split(':')[1]
282            _build_out_dir = toolchain_name
283        else:
284            _build_out_dir = '.'
285        _info['build_out_dir'] = _build_out_dir
286        return _info
287
288    def _overrided_part_name(self):
289        overrided_components = self._overrided_components
290        full_part_name = f"{self._subsystem_name}:{self._origin_name}"
291        overrided_map = overrided_components.get(full_part_name)
292        if overrided_map:
293            # origin_name is same as part_name in variant phone
294            overrided_origin_name = overrided_map.get("partName")
295            overrided_part_name = overrided_origin_name
296        else:
297            overrided_part_name = self._part_name
298            overrided_origin_name = self._origin_name
299
300        return overrided_part_name, overrided_origin_name
301
302    def _parsing_base_info(self, inner_kits_lib, part_name):
303        info = {'part_name': part_name}
304        label = inner_kits_lib.get('name')
305        lib_name = label.split(':')[1]
306        info['label'] = label
307        info['name'] = lib_name
308        if inner_kits_lib.get('visibility') is not None:
309            info['visibility'] = inner_kits_lib.get('visibility')
310        lib_type = inner_kits_lib.get('type')
311        if lib_type is None:
312            lib_type = 'so'
313        info['type'] = lib_type
314        prebuilt = inner_kits_lib.get('prebuilt_enable')
315        if prebuilt:
316            info['prebuilt_enable'] = prebuilt
317            prebuilt_source_libs = inner_kits_lib.get('prebuilt_source')
318            prebuilt_source = prebuilt_source_libs.get(target_arch)
319            info['prebuilt_source'] = prebuilt_source
320        else:
321            info['prebuilt_enable'] = False
322        return info, lib_name, lib_type
323
324    def _parsing_system_kits(self, part_name, system_kits_info,
325                             build_gn_content):
326        system_kits_libs_gn = []
327        kits = []
328        for _kits_lib in system_kits_info:
329            system_kits_libs_gn.append('    {')
330            system_kits_libs_gn.extend(self._parsing_kits_lib(
331                _kits_lib, False))
332            kits.append('"{}"'.format(_kits_lib.get('name')))
333            system_kits_libs_gn.append('    },')
334        _kits_libs_gn_line = '\n'.join(system_kits_libs_gn)
335        system_kits_def = SYSTEM_KITS_TEMPLATE.format(part_name,
336                                                      _kits_libs_gn_line,
337                                                      self._origin_name,
338                                                      self._variant_name)
339        build_gn_content.append(system_kits_def)
340        self._kits = kits
341
342    def _parsing_config(self, part_name, part_config, subsystem_name):
343        self._part_target_list = []
344        build_gn_content = []
345        build_gn_content.append(IMPORT_LIST)
346
347        # ohos part
348        if 'module_list' not in part_config:
349            raise OHOSException(
350                "ohos.build incorrect, part name: '{}'".format(part_name), "2014")
351        module_list = part_config.get('module_list')
352        if len(module_list) == 0:
353            module_list_line = ''
354        else:
355            module_list_line = '"{}",'.format('",\n    "'.join(module_list))
356        parts_definition = PART_TEMPLATE.format(part_name, subsystem_name,
357                                                module_list_line,
358                                                self._origin_name,
359                                                self._variant_name)
360        build_gn_content.append(parts_definition)
361
362        # part inner kits
363        if part_config.get('inner_kits'):
364            self._part_target_list.append('inner_kits')
365            inner_kits_info = part_config.get('inner_kits')
366            self._parsing_inner_kits(part_name, inner_kits_info,
367                                     build_gn_content, self._target_arch)
368        # part system kits
369        if part_config.get('system_kits'):
370            self._part_target_list.append('system_kits')
371            system_kits_info = part_config.get('system_kits')
372            self._parsing_system_kits(part_name, system_kits_info,
373                                      build_gn_content)
374        # part test list
375        if part_config.get('test_list'):
376            self._part_target_list.append('test')
377            test_list = part_config.get('test_list')
378            test_list_line = '"{}",'.format('",\n    "'.join(test_list))
379            test_def = TEST_TEMPLATE.format(part_name, test_list_line,
380                                            subsystem_name)
381            build_gn_content.append(test_def)
382        self._build_gn_content = build_gn_content
383        # feature
384        if part_config.get('feature_list'):
385            self._feature_list = part_config.get('feature_list')
386            # check feature
387            for _feature_name in self._feature_list:
388                if not _feature_name.startswith('{}_'.format(
389                        self._origin_name)):
390                    raise OHOSException(
391                        "part feature list config incorrect,"
392                        " part_name='{}', feature_name='{}'".format(
393                            self._origin_name, _feature_name), "2014")
394
395        # system_capabilities is a list attribute of a part in ohos.build
396        if part_config.get('system_capabilities'):
397            self._system_capabilities = part_config.get('system_capabilities')
398
399
400class LoadBuildConfig(object):
401    """load build config file and parse configuration info."""
402
403    def __init__(self, source_root_dir, subsystem_build_info,
404                 config_output_dir, variant_toolchains, subsystem_name,
405                 target_arch, ignored_subsystems, exclusion_modules_config_file,
406                 load_test_config, overrided_components, bundle_subsystem_allow_list):
407        self._source_root_dir = source_root_dir
408        self._build_info = subsystem_build_info
409        self._config_output_relpath = config_output_dir
410        self._is_load = False
411        self._parts_variants = {}
412        self._part_list = {}
413        self._part_targets_label = {}
414        self._variant_toolchains = variant_toolchains
415        self._subsystem_name = subsystem_name
416        self._target_arch = target_arch
417        self._ignored_subsystems = ignored_subsystems
418        self._parts_info_dict = {}
419        self._phony_targets = {}
420        self._parts_path_dict = {}
421        self._part_hisysevent_config = {}
422        self._parts_module_list = {}
423        self._parts_deps = {}
424        self._exclusion_modules_config_file = exclusion_modules_config_file
425        self._load_test_config = load_test_config
426        self._overrided_components = overrided_components
427        self._bundle_subsystem_allow_list = bundle_subsystem_allow_list
428
429    @throw_exception
430    def _parsing_config(self, parts_config):
431        _parts_info_dict = {}
432        _parts_deps = {}
433        _variant_phony_targets = {}
434        for part_name, value in parts_config.items():
435            if 'variants' in value:
436                variants = value.get('variants')
437                if len(variants) == 0:
438                    variants = ['phone']
439            else:
440                variants = ['phone']
441            _build_target = {}
442            _targets_label = {}
443            _parts_info = []
444            for variant in variants:
445                toolchain = self._variant_toolchains.get(variant)
446                if toolchain is None:
447                    continue
448                part_obj = PartObject(part_name, variant, value, toolchain,
449                                      self._subsystem_name, self._target_arch, self._overrided_components)
450                real_part_name = part_obj.part_name()
451                self._part_list[real_part_name] = part_obj
452
453                subsystem_config_dir = os.path.join(
454                    self._config_output_relpath, self._subsystem_name)
455                part_obj.write_build_gn(
456                    os.path.join(self._source_root_dir, subsystem_config_dir))
457
458                _target_label = part_obj.get_target_label(subsystem_config_dir)
459                _build_target[variant] = _target_label
460                _targets_label[real_part_name] = part_obj.part_group_targets(
461                    subsystem_config_dir)
462                _parts_info.append(part_obj.part_info())
463                if variant != 'phone':
464                    _variant_phony_targets[real_part_name] = _target_label
465            self._part_targets_label.update(_targets_label)
466            self._parts_variants[part_name] = _build_target
467            if 'hisysevent_config' in value:
468                _config_files = value.get('hisysevent_config')
469                for _config_file in _config_files:
470                    if not _config_file.startswith('//'):
471                        raise OHOSException(
472                            "part '{}' hisysevent config incorrest.".format(
473                                part_name), "2014")
474                self._part_hisysevent_config[part_name] = _config_files
475            _parts_info_dict[part_name] = _parts_info
476            _parts_deps[part_name] = value.get('part_deps')
477        self._parts_info_dict = _parts_info_dict
478        self._phony_targets = _variant_phony_targets
479        self._parts_deps = _parts_deps
480
481    def parse_syscap_info(self):
482        _build_files = self._build_info.get('build_files')
483        subsystem_syscap = []
484        for _build_file in _build_files:
485            if _build_file.endswith('bundle.json'):
486                part_name, part_syscap = get_syscap_from_bundle(_build_file)
487                subsystem_syscap.append(
488                    {'component': part_name, 'syscap': part_syscap})
489        return subsystem_syscap
490
491    def parse(self):
492        """parse part info from build config file."""
493        if self._is_load:
494            return
495        subsystem_config, parts_path_dict = self._merge_build_config()
496        parts_config = subsystem_config.get('parts')
497        self._parts_module_list.update(parts_config)
498        self._parsing_config(parts_config)
499        self._parts_path_dict = parts_path_dict
500        self._is_load = True
501
502    def parts_variants(self):
503        """parts varinats info."""
504        self.parse()
505        return self._parts_variants
506
507    def parts_inner_kits_info(self):
508        """parts inner kits info."""
509        self.parse()
510        _parts_inner_kits = {}
511        for part_obj in self._part_list.values():
512            _parts_inner_kits[
513                part_obj.part_name()] = part_obj.part_inner_kits()
514        return _parts_inner_kits
515
516    def parts_component_info(self):
517        """parts component info."""
518        self.parse()
519        _parts_component_info = {}
520        for part_obj in self._part_list.values():
521            _parts_component_info[
522                part_obj.part_name()] = part_obj.part_component_info()
523        return _parts_component_info
524
525    def parts_build_targets(self):
526        """parts build target label."""
527        self.parse()
528        return self._part_targets_label
529
530    def parts_name_list(self):
531        """parts name list."""
532        self.parse()
533        return list(self._part_list.keys())
534
535    def parts_info(self):
536        """parts info."""
537        self.parse()
538        return self._parts_info_dict
539
540    def parts_phony_target(self):
541        """parts phony target info"""
542        self.parse()
543        return self._phony_targets
544
545    def parts_kits_info(self):
546        """parts kits info."""
547        self.parse()
548        _parts_kits = {}
549        for part_obj in self._part_list.values():
550            _parts_kits[part_obj.part_name()] = part_obj.part_kits()
551        return _parts_kits
552
553    def parts_path_info(self):
554        """parts to path info."""
555        self.parse()
556        return self._parts_path_dict
557
558    def parts_hisysevent_config(self):
559        self.parse()
560        return self._part_hisysevent_config
561
562    def parts_modules_info(self):
563        self.parse()
564        return self._parts_module_list
565
566    def parts_deps(self):
567        self.parse()
568        return self._parts_deps
569
570    def parts_info_filter(self, save_part):
571        if save_part is None:
572            raise Exception
573        self._parts_variants = {
574            key: value for key, value in self._parts_variants.items() if key in save_part}
575        self._part_list = {
576            key: value for key, value in self._part_list.items() if key in save_part}
577        self._part_targets_label = {
578            key: value for key, value in self._part_targets_label.items() if key in save_part}
579        self._parts_info_dict = {
580            key: value for key, value in self._parts_info_dict.items() if key in save_part}
581        self._phony_targets = {
582            key: value for key, value in self._phony_targets.items() if key in save_part}
583        self._parts_path_dict = {
584            key: value for key, value in self._parts_path_dict.items() if key in save_part}
585        self._part_hisysevent_config = {
586            key: value for key, value in self._part_hisysevent_config.items() if key in save_part}
587        self._parts_module_list = {
588            key: value for key, value in self._parts_module_list.items() if key in save_part}
589        self._parts_deps = {
590            key: value for key, value in self._parts_deps.items() if key in save_part}
591
592    def _merge_build_config(self):
593        _build_files = self._build_info.get('build_files')
594        is_thirdparty_subsystem = False
595        if _build_files[0].startswith(self._source_root_dir + 'third_party'):
596            is_thirdparty_subsystem = True
597        subsystem_name = None
598        parts_info = {}
599        parts_path_dict = {}
600        for _build_file in _build_files:
601            if _build_file.endswith('bundle.json'):
602                bundle_part_obj = load_bundle_file.BundlePartObj(
603                    _build_file, self._exclusion_modules_config_file,
604                    self._load_test_config)
605                _parts_config = bundle_part_obj.to_ohos_build()
606            else:
607                _parts_config = read_build_file(_build_file)
608
609            _subsystem_name = _parts_config.get('subsystem')
610            if not is_thirdparty_subsystem and subsystem_name and _subsystem_name != subsystem_name:
611                raise OHOSException(
612                    "subsystem name config incorrect in '{}'.".format(
613                        _build_file), "2014")
614            if _subsystem_name != self._subsystem_name:
615                is_allow = False
616                for file_path in self._bundle_subsystem_allow_list:
617                    if _build_file.endswith(file_path):
618                        is_allow = True
619                        break
620                if is_allow:
621                    print("warning: subsystem name config incorrect in '{}', build file subsystem name is {},"
622                          "configured subsystem name is {}.".format(
623                        _build_file, _subsystem_name, self._subsystem_name))
624                else:
625                    raise OHOSException("subsystem name config incorrect in '{}', build file subsystem name is {},"
626                                        "configured subsystem name is {}.".format(
627                        _build_file, _subsystem_name, self._subsystem_name), 2014)
628
629            subsystem_name = _subsystem_name
630            _curr_parts_info = _parts_config.get('parts')
631            for _pname in _curr_parts_info.keys():
632                parts_path_dict[_pname] = os.path.relpath(
633                    os.path.dirname(_build_file), self._source_root_dir)
634            parts_info.update(_curr_parts_info)
635        subsystem_config = {}
636        subsystem_config['subsystem'] = subsystem_name
637        subsystem_config['parts'] = parts_info
638        return subsystem_config, parts_path_dict
639
640
641def compare_subsystem_and_component(subsystem_name, components_name, subsystem_compoents_whitelist_info,
642                                    part_subsystem_component_info, parts_config_path, subsystem_components_list):
643    name = ""
644    message = ""
645    if components_name in list(subsystem_compoents_whitelist_info.keys()):
646        return
647    overrided_components_name = '{}_{}'.format(components_name, 'override')
648    if components_name in list(part_subsystem_component_info.keys()) \
649            or overrided_components_name in list(part_subsystem_component_info.keys()):
650        if subsystem_name in list(part_subsystem_component_info.values()):
651            return
652        if subsystem_name == components_name:
653            return
654        name = subsystem_name
655        message = "find subsystem {} failed, please check it in {}.".format(subsystem_name, parts_config_path)
656    else:
657        name = components_name
658        message = "find component {} failed, please check it in {}.".format(components_name, parts_config_path)
659    if name in subsystem_components_list:
660        print(f"Warning: {message}")
661    else:
662        raise Exception(message)
663
664
665def check_subsystem_and_component(parts_info_output_path, skip_partlist_check):
666    config = Config()
667    parts_config_path = os.path.join(config.root_path, "out/preloader", config.product,
668                                     "parts.json")
669    part_subsystem_file = os.path.join(parts_info_output_path,
670                                       "part_subsystem.json")
671    part_subsystem_component_info = read_json_file(part_subsystem_file)
672
673    subsystem_compoents_whitelist_file = os.path.join(config.root_path,
674                                                      "build/subsystem_compoents_whitelist.json")
675    subsystem_compoents_whitelist_info = read_json_file(subsystem_compoents_whitelist_file)
676
677    compile_standard_whitelist_file = os.path.join(config.root_path, "out/preloader", config.product,
678                                                   "compile_standard_whitelist.json")
679    compile_standard_whitelist_info = read_json_file(compile_standard_whitelist_file)
680    subsystem_components_list = compile_standard_whitelist_info.get("subsystem_components", [])
681
682    if os.path.isfile(parts_config_path):
683        parts_config_info = read_json_file(parts_config_path)
684        parts_info = parts_config_info.get("parts")
685
686        for subsystem_part in parts_info:
687            subsystem_part_list = subsystem_part.split(':')
688            subsystem_name = subsystem_part_list[0]
689            components_name = subsystem_part_list[1]
690
691            if subsystem_name is None or components_name is None:
692                print("Warning: subsystem_name or components_name is empty, please check it in {}.".format(
693                    parts_config_path))
694                continue
695            if not skip_partlist_check:
696                compare_subsystem_and_component(subsystem_name, components_name, subsystem_compoents_whitelist_info,
697                                                part_subsystem_component_info, parts_config_path,
698                                                subsystem_components_list)
699
700
701def _output_all_components_info(parts_config_dict, parts_info_output_path):
702    if 'parts_component_info' not in parts_config_dict:
703        return
704    parts_component_info = parts_config_dict.get('parts_component_info')
705    if 'parts_info' not in parts_config_dict:
706        return
707    parts_info = parts_config_dict.get('parts_info')
708    if 'parts_path_info' not in parts_config_dict:
709        return
710    parts_path_info = parts_config_dict.get('parts_path_info')
711
712    components = {}
713
714    for name, val in parts_component_info.items():
715        component = {}
716        component["innerapis"] = []
717        component["variants"] = []
718        if name in parts_path_info:
719            component["path"] = parts_path_info[name]
720        else:
721            print("Warning: component %s has no path info." % name)
722
723        if name in parts_info:
724            for item in parts_info[name]:
725                component["subsystem"] = item.get("subsystem_name")
726                break
727        else:
728            print("Warning: component %s has no subsystem info." % name)
729
730        if val:
731            for k, v in val.items():
732                innerapi = {"name": k, "label": v["label"]}
733                if "visibility" in v:
734                    innerapi["visibility"] = v["visibility"]
735                component["innerapis"].append(innerapi)
736
737        components[name] = component
738
739    _component_file = os.path.join(parts_info_output_path,
740                                   "components.json")
741    write_json_file(_component_file, components)
742
743
744def _output_parts_info(parts_config_dict,
745                       config_output_path, skip_partlist_check):
746    parts_info_output_path = os.path.join(config_output_path, "parts_info")
747    # parts_info.json
748    process_parts_info(parts_config_dict, parts_info_output_path, skip_partlist_check)
749
750    # subsystem_parts.json
751    process_subsystem_parts(config_output_path, parts_config_dict, parts_info_output_path)
752
753    # _parts_modules_info
754    process_parts_modules_info(parts_config_dict, parts_info_output_path)
755
756    # paths_path_info.json
757    process_parts_path_info(parts_config_dict, parts_info_output_path)
758
759    other_parts = ["parts_variants", "parts_inner_kits_info", "parts_targets",
760                   "phony_target", "hisysevent_config", "parts_deps"]
761
762    for parts in other_parts:
763        process_other_parts(parts, parts_config_dict, parts_info_output_path)
764
765    check_subsystem_and_component(parts_info_output_path, skip_partlist_check)
766
767    _output_all_components_info(parts_config_dict, parts_info_output_path)
768
769
770def process_other_parts(part_name, parts_config_dict, parts_info_output_path):
771    if part_name in parts_config_dict:
772        parts_info = parts_config_dict.get(part_name)
773        parts_info_file = os.path.join(parts_info_output_path,
774                                       part_name + ".json")
775        if part_name == 'hisysevent_config':
776            parts_info_file = os.path.join(parts_info_output_path,
777                                           'hisysevent_configs' + ".json")
778        if part_name == 'parts_inner_kits_info':
779            parts_info_file = os.path.join(parts_info_output_path,
780                                           'inner_kits_info' + ".json")
781        write_json_file(parts_info_file, parts_info)
782        LogUtil.hb_info("generate '{}' info to '{}'".format(part_name,
783                                                            parts_info_file), mode=Config.log_mode)
784
785
786def process_parts_modules_info(parts_config_dict, parts_info_output_path):
787    if 'parts_modules_info' in parts_config_dict:
788        parts_modules_info = parts_config_dict.get('parts_modules_info')
789        _output_info = {}
790        _all_p_info = []
791        for key, value in parts_modules_info.items():
792            _p_info = {}
793            _p_info['part_name'] = key
794            _module_list = value.get('module_list')
795            _p_info['module_list'] = _module_list
796            _all_p_info.append(_p_info)
797        _output_info['parts'] = _all_p_info
798        parts_modules_info_file = os.path.join(parts_info_output_path,
799                                               'parts_modules_info.json')
800        write_json_file(parts_modules_info_file, _output_info)
801        LogUtil.hb_info("generate parts modules info to '{}'".format(
802            parts_modules_info_file), mode=Config.log_mode)
803
804
805def process_parts_path_info(parts_config_dict, parts_info_output_path):
806    if 'parts_path_info' in parts_config_dict:
807        parts_path_info = parts_config_dict.get('parts_path_info')
808        parts_path_info_file = os.path.join(parts_info_output_path,
809                                            'parts_path_info.json')
810        write_json_file(parts_path_info_file, parts_path_info)
811        LogUtil.hb_info(
812            "generate parts path info to '{}'".format(parts_path_info_file), mode=Config.log_mode)
813        path_to_parts = {}
814        for _key, _val in parts_path_info.items():
815            _p_list = path_to_parts.get(_val, [])
816            _p_list.append(_key)
817            path_to_parts[_val] = _p_list
818        path_to_parts_file = os.path.join(parts_info_output_path,
819                                          'path_to_parts.json')
820        write_json_file(path_to_parts_file, path_to_parts)
821        LogUtil.hb_info(
822            "generate path to parts to '{}'".format(path_to_parts_file), mode=Config.log_mode)
823
824
825def process_subsystem_parts(config_output_path, parts_config_dict, parts_info_output_path):
826    if 'subsystem_parts' in parts_config_dict:
827        subsystem_parts = parts_config_dict.get('subsystem_parts')
828        subsystem_parts_file = os.path.join(parts_info_output_path,
829                                            "subsystem_parts.json")
830        write_json_file(subsystem_parts_file, subsystem_parts)
831        LogUtil.hb_info(
832            "generate ubsystem-parts of parts-info to '{}'".format(
833                subsystem_parts_file), mode=Config.log_mode)
834
835        # adapter mini system
836        for _sub_name, _p_list in subsystem_parts.items():
837            _output_info = {}
838            _output_info['parts'] = _p_list
839            _sub_info_output_file = os.path.join(config_output_path,
840                                                 'mini_adapter',
841                                                 '{}.json'.format(_sub_name))
842            write_json_file(_sub_info_output_file, _output_info)
843        LogUtil.hb_info(
844            "generate mini adapter info to '{}/mini_adapter/'".format(
845                config_output_path), mode=Config.log_mode)
846
847
848def process_parts_info(parts_config_dict, parts_info_output_path, skip_partlist_check):
849    if 'parts_info' in parts_config_dict:
850        parts_info = parts_config_dict.get('parts_info')
851        parts_info_file = os.path.join(parts_info_output_path,
852                                       "parts_info.json")
853        write_json_file(parts_info_file, parts_info)
854        LogUtil.hb_info("generate parts info to '{}'".format(parts_info_file), mode=Config.log_mode)
855        _part_subsystem_dict = {}
856        for key, value in parts_info.items():
857            for _info in value:
858                _sub_name = _info.get('subsystem_name')
859                _part_subsystem_dict[key] = _sub_name
860                break
861        _part_subsystem_file = os.path.join(parts_info_output_path,
862                                            "part_subsystem.json")
863        write_json_file(_part_subsystem_file, _part_subsystem_dict)
864        LogUtil.hb_info(
865            "generate part-subsystem of parts-info to '{}'".format(
866                _part_subsystem_file), mode=Config.log_mode)
867
868
869def get_parts_info(source_root_dir,
870                   config_output_relpath,
871                   subsystem_info,
872                   variant_toolchains,
873                   target_arch,
874                   ignored_subsystems,
875                   exclusion_modules_config_file,
876                   load_test_config,
877                   overrided_components,
878                   bundle_subsystem_allow_list,
879                   skip_partlist_check,
880                   build_xts=False):
881    """parts info,
882    get info from build config file.
883    """
884    parts_variants = {}
885    parts_inner_kits_info = {}
886    parts_component_info = {}
887    parts_kits_info = {}
888    parts_targets = {}
889    parts_info = {}
890    subsystem_parts = {}
891    _phony_target = {}
892    _parts_path_info = {}
893    _parts_hisysevent_config = {}
894    _parts_modules_info = {}
895    _parts_deps = {}
896    system_syscap = []
897    for subsystem_name, build_config_info in subsystem_info.items():
898        if not len(build_config_info.get("build_files")):
899            continue
900        build_loader = LoadBuildConfig(source_root_dir, build_config_info,
901                                       config_output_relpath,
902                                       variant_toolchains, subsystem_name,
903                                       target_arch, ignored_subsystems,
904                                       exclusion_modules_config_file,
905                                       load_test_config, overrided_components,
906                                       bundle_subsystem_allow_list)
907        # xts subsystem special handling, device_attest and
908        # device_attest_lite parts need to be compiled into the version image, other parts are not.
909        # parts_modules_info needs to be parse before filting.
910        if subsystem_name == 'xts' and build_xts is False:
911            xts_device_attest_name = ['device_attest_lite', 'device_attest']
912            build_loader.parse()
913            build_loader.parts_info_filter(xts_device_attest_name)
914        _parts_variants = build_loader.parts_variants()
915        parts_variants.update(_parts_variants)
916        _inner_kits_info = build_loader.parts_inner_kits_info()
917        parts_inner_kits_info.update(_inner_kits_info)
918        _parts_component_info = build_loader.parts_component_info()
919        parts_component_info.update(_parts_component_info)
920        parts_kits_info.update(build_loader.parts_kits_info())
921        _parts_targets = build_loader.parts_build_targets()
922        parts_targets.update(_parts_targets)
923        subsystem_parts[subsystem_name] = build_loader.parts_name_list()
924        parts_info.update(build_loader.parts_info())
925        _phony_target.update(build_loader.parts_phony_target())
926        _parts_path_info.update(build_loader.parts_path_info())
927        _parts_hisysevent_config.update(build_loader.parts_hisysevent_config())
928        _parts_modules_info.update(build_loader.parts_modules_info())
929        _parts_deps.update(build_loader.parts_deps())
930        system_syscap.extend(build_loader.parse_syscap_info())
931    LogUtil.hb_info(
932        "generate all parts build gn file to '{}/{}'".format(
933            source_root_dir, config_output_relpath), mode=Config.log_mode)
934    parts_config_dict = {}
935    parts_config_dict['parts_info'] = parts_info
936    parts_config_dict['subsystem_parts'] = subsystem_parts
937    parts_config_dict['parts_variants'] = parts_variants
938    parts_config_dict['parts_inner_kits_info'] = parts_inner_kits_info
939    parts_config_dict['parts_component_info'] = parts_component_info
940    parts_config_dict['parts_kits_info'] = parts_kits_info
941    parts_config_dict['parts_targets'] = parts_targets
942    parts_config_dict['phony_target'] = _phony_target
943    parts_config_dict['parts_path_info'] = _parts_path_info
944    parts_config_dict['hisysevent_config'] = _parts_hisysevent_config
945    parts_config_dict['parts_modules_info'] = _parts_modules_info
946    parts_config_dict['parts_deps'] = _parts_deps
947    _output_parts_info(parts_config_dict,
948                       os.path.join(source_root_dir, config_output_relpath), skip_partlist_check)
949    parts_config_dict['syscap_info'] = system_syscap
950    LogUtil.hb_info('all parts scan completed')
951    return parts_config_dict
952