• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
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.
15import logging
16import os
17import json
18import argparse
19import stat
20
21
22def get_args():
23    parser = argparse.ArgumentParser(add_help=True)
24    parser.add_argument(
25        "-p",
26        "--project_path",
27        default=r"",
28        type=str,
29        help="root path of project. default: ./",
30    )
31    args = parser.parse_args()
32    return args
33
34
35def dict_to_json(output_path: str, syscaps_dict: dict):
36    """
37    output diff product syscaps json to output path
38    :param output_path:
39    :param syscaps_dict:
40    :return:
41    """
42    print("start generate syscap json...")
43    flags = os.O_WRONLY | os.O_CREAT
44    modes = stat.S_IWUSR | stat.S_IRUSR
45    for product_name, syscaps_list in syscaps_dict.items():
46        filename = os.path.join(output_path, f'{product_name}.json')
47        with os.fdopen(os.open(filename, flags, modes), 'w') as f:
48            json.dump({'SysCaps': syscaps_list}, f)
49    print("end...")
50
51
52def check_syscap(syscap_str: str):
53    syscap = syscap_str.split(' = ')
54    if syscap[-1].lower() == 'false':
55        return False
56    else:
57        return syscap[0]
58
59
60def bundle_syscap_list_handler(bundle_syscap_list: list, component_syscaps_list: list):
61    """
62    check syscap
63    :param bundle_syscap_list:
64    :param component_syscaps_list:
65    :return:
66    """
67    for component_syscap in component_syscaps_list:
68        component_syscap = check_syscap(component_syscap)
69        if component_syscap:
70            bundle_syscap_list.append(component_syscap)
71    return bundle_syscap_list
72
73
74def read_json_file(bundle_json_path: str):
75    bundle_syscap_list = list()
76    error_list = dict()
77    try:
78        with open(bundle_json_path, 'r', encoding='utf-8') as f:
79            bundle_data = json.load(f)
80            component_data = bundle_data.get("component")
81            component_syscaps_list = component_data.get("syscap")
82            if component_syscaps_list:
83                bundle_syscap_list = bundle_syscap_list_handler(bundle_syscap_list, component_syscaps_list)
84    except FileNotFoundError as e:
85        error_list[bundle_json_path] = str(e)
86    except Exception as e:
87        error_list[bundle_json_path] = str(e)
88    return bundle_syscap_list, error_list
89
90
91def get_all_components_path(components_file_path: str):
92    try:
93        with open(components_file_path, 'r', encoding='utf-8') as f:
94            path_dict = json.load(f)
95        return path_dict
96    except FileNotFoundError:
97        logging.error(r"PATH ERROR")
98        return {}
99
100
101def path_component_to_bundle(path: str) -> str:
102    bundle_json_path = os.path.join(path, 'bundle.json')
103    return bundle_json_path
104
105
106def handle_bundle_json_file(component_path_dict: dict):
107    """
108    from product required part bundle.json to all products parts list
109    :param component_path_dict:
110    :return: all products parts list
111    """
112    print("start collect syscap path...")
113    syscap_dict = dict()
114    errors_list = list()
115    for product_name, path_list in component_path_dict.items():
116        bundles_list = list()
117        for path in path_list:
118            bundle_json_path = path_component_to_bundle(path)
119            bundle_syscap_list, error_list = read_json_file(bundle_json_path)
120            bundles_list.extend(bundle_syscap_list)
121            errors_list.extend(error_list)
122        syscap_dict.update({product_name: bundles_list})
123    return syscap_dict, errors_list
124
125
126def format_component_path(component_path: str):
127    sep_list = ['\\', '/']
128    sep_list.remove(os.sep)
129    component_path = component_path.replace(sep_list[0], os.sep)
130    return component_path
131
132
133def traversal_path(parts_path_info: dict, project_path: str, product_define_dict):
134    component_path_dict = dict()
135    for product_name, component_name_list in product_define_dict.items():
136        component_paths = list()
137        for component_name in component_name_list:
138            component_relpath = parts_path_info.get(component_name)
139            if component_relpath:
140                component_path = os.path.join(project_path, component_relpath)
141                component_path = format_component_path(component_path)
142                component_paths.append(component_path)
143            else:
144                logging.error(f'can\'t find component_name : {component_name}')
145        component_path_dict.update({product_name: component_paths})
146    return component_path_dict
147
148
149def collect_all_product_component_syscap_dict(parts_path_info: dict, project_path: str, product_define_dict):
150    """
151    get all syscap to dict
152    :param parts_path_info:
153    :param project_path:
154    :param product_define_dict:
155    :return:
156    """
157    if parts_path_info:
158        print("start collect component path...")
159        component_path_dict = traversal_path(parts_path_info, project_path, product_define_dict)
160        syscap_dict, errors_list = handle_bundle_json_file(component_path_dict)
161        return syscap_dict, errors_list
162    else:
163        return 0, 0
164
165
166def get_subsystem_info(subsystem_config_file, source_root_dir):
167    """
168    get subsystem name and subsystem path from oh/build/subsystem_config.json
169    :param subsystem_config_file: subsystem_config_file path
170    :param source_root_dir: oh project path
171    :return: subsystem name and subsystem path
172    """
173    subsystem_configs = scan(subsystem_config_file, source_root_dir)
174    _all_components_path = []
175    for _, value in subsystem_configs.get('subsystem').items():
176        for i in value.get('build_files'):
177            _all_components_path.append(i)
178    return subsystem_configs.get('subsystem')
179
180
181def _check_path_prefix(paths):
182    allow_path_prefix = ['vendor', 'device']
183    result = list(
184        filter(lambda x: x is False,
185               map(lambda p: p.split('/')[0] in allow_path_prefix, paths)))
186    return len(result) <= 1
187
188
189def traversal_files(subsystem_path, _files):
190    for item in os.scandir(subsystem_path):
191        if is_symlik(item.path):
192            continue
193        elif item.is_file() and item.name == 'ohos.build':
194            _files.append(item.path)
195        elif item.is_file() and item.name == 'bundle.json':
196            _files.append(item.path)
197        elif item.is_dir():
198            traversal_files(item, _files)
199    return _files
200
201
202def get_file_type(file_path):
203    if os.path.islink(file_path):
204        return 'symlink'
205    elif os.path.isfile(file_path):
206        return 'file'
207    elif os.path.isdir(file_path):
208        return 'directory'
209    else:
210        return 'unknown'
211
212
213def is_symlik(file_path):
214    file_type = get_file_type(file_path)
215    if file_type == 'symlink':
216        link_target = os.readlink(file_path)
217        return link_target != file_type
218    return False
219
220
221def _scan_build_file(subsystem_path):
222    _files = []
223    _bundle_files = []
224    try:
225        _files = traversal_files(subsystem_path, _files)
226    except FileNotFoundError:
227        print(f"read file {subsystem_path} failed.")
228    return _files
229
230
231def scan(subsystem_config_file, source_root_dir):
232    subsystem_infos = _read_config(subsystem_config_file)
233    _default_subsystem = {"build": "build"}
234    subsystem_infos.update(_default_subsystem)
235    no_src_subsystem = {}
236    _build_configs = {}
237    for key, val in subsystem_infos.items():
238        _all_build_config_files = []
239        if not isinstance(val, list):
240            val = [val]
241        else:
242            if not _check_path_prefix(val):
243                raise Exception("subsystem '{}' path configuration is incorrect.".format(key), "2013")
244        _info = {'path': val}
245        for _path in val:
246            _subsystem_path = os.path.join(source_root_dir, _path)
247            _build_config_files = _scan_build_file(_subsystem_path)
248            _all_build_config_files.extend(_build_config_files)
249        if _all_build_config_files:
250            _info['build_files'] = _all_build_config_files
251            _build_configs[key] = _info
252        else:
253            no_src_subsystem[key] = val
254
255    scan_result = {
256        'source_path': source_root_dir,
257        'subsystem': _build_configs,
258    }
259    print('subsystem config scan completed')
260    return scan_result
261
262
263def _read_config(subsystem_config_file):
264    if not os.path.exists(subsystem_config_file):
265        raise Exception("config file '{}' doesn't exist.".format(subsystem_config_file), "2013")
266    subsystem_config = _read_json_file(subsystem_config_file)
267    if subsystem_config is None:
268        raise Exception("read file '{}' failed.".format(subsystem_config_file), "2013")
269
270    subsystem_info = {}
271    for key, val in subsystem_config.items():
272        if 'path' not in val:
273            raise Exception("subsystem '{}' not config path.".format(key), "2013")
274        subsystem_info[key] = val.get('path')
275    return subsystem_info
276
277
278def read_build_file(ohos_build_file):
279    if not os.path.exists(ohos_build_file):
280        raise Exception("config file '{}' doesn't exist.".format(ohos_build_file), "2014")
281    subsystem_config = _read_json_file(ohos_build_file)
282    if not subsystem_config:
283        raise Exception("read file '{}' failed.".format(ohos_build_file), "2014")
284    return subsystem_config
285
286
287class BundlePartObj(object):
288    def __init__(self, bundle_config_file):
289        self._build_config_file = bundle_config_file
290        self._loading_config()
291
292    def _loading_config(self):
293        if not os.path.exists(self._build_config_file):
294            raise Exception("file '{}' doesn't exist.".format(
295                self._build_config_file), "2011")
296        self.bundle_info = _read_json_file(self._build_config_file)
297        if not self.bundle_info:
298            raise Exception("read file '{}' failed.".format(
299                self._build_config_file), "2011")
300
301    def to_ohos_build(self):
302        _component_info = self.bundle_info.get('component')
303        _subsystem_name = _component_info.get('subsystem')
304        _part_name = _component_info.get('name')
305        _bundle_build = _component_info.get('build')
306        _ohos_build_info = dict()
307        _ohos_build_info['subsystem'] = _subsystem_name
308        _part_info = {}
309        module_list = []
310        if _component_info.get('build').__contains__('sub_component'):
311            _part_info['module_list'] = _component_info.get('build').get(
312                'sub_component')
313        elif _component_info.get('build').__contains__('modules'):
314            _part_info['module_list'] = _component_info.get(
315                'build').get('modules')
316        elif _component_info.get('build').__contains__('group_type'):
317            _module_groups = _component_info.get('build').get('group_type')
318            for _group_type, _module_list in _module_groups.items():
319                _key = '{}:{}'.format(_subsystem_name, _part_name)
320            _part_info['module_list'] = module_list
321        if 'inner_kits' in _bundle_build:
322            _part_info['inner_kits'] = _bundle_build.get('inner_kits')
323        elif 'inner_api' in _bundle_build:
324            _part_info['inner_kits'] = _bundle_build.get('inner_api')
325        if 'features' in _component_info:
326            _part_info['feature_list'] = _component_info.get('features')
327        if 'syscap' in _component_info:
328            _part_info['system_capabilities'] = _component_info.get('syscap')
329        if 'hisysevent_config' in _component_info:
330            _part_info['hisysevent_config'] = _component_info.get(
331                'hisysevent_config')
332        _part_info['part_deps'] = _component_info.get('deps', {})
333        _part_info['part_deps']['build_config_file'] = self._build_config_file
334        _ohos_build_info['parts'] = {_part_name: _part_info}
335        return _ohos_build_info
336
337
338class LoadBuildConfig(object):
339    """load build config file and parse configuration info."""
340
341    def __init__(self, source_root_dir, subsystem_build_info, subsystem_name):
342        self._source_root_dir = source_root_dir
343        self._build_info = subsystem_build_info
344        self._is_load = False
345        self._parts_variants = {}
346        self._part_list = {}
347        self._part_targets_label = {}
348        self._subsystem_name = subsystem_name
349        self._parts_info_dict = {}
350        self._phony_targets = {}
351        self._parts_path_dict = {}
352        self._part_hisysevent_config = {}
353        self._parts_module_list = {}
354        self._parts_deps = {}
355
356    def _merge_build_config(self):
357        _build_files = self._build_info.get('build_files')
358        is_thirdparty_subsystem = False
359        if _build_files[0].startswith(self._source_root_dir + 'third_party'):
360            is_thirdparty_subsystem = True
361        subsystem_name = None
362        parts_info = {}
363        parts_path_dict = {}
364        for _build_file in _build_files:
365            if _build_file.endswith('bundle.json'):
366                bundle_part_obj = BundlePartObj(_build_file)
367                _parts_config = bundle_part_obj.to_ohos_build()
368            else:
369                _parts_config = read_build_file(_build_file)
370            _subsystem_name = _parts_config.get('subsystem')
371            if not is_thirdparty_subsystem and subsystem_name and _subsystem_name != subsystem_name:
372                raise Exception(
373                    "subsystem name config incorrect in '{}'.".format(
374                        _build_file), "2014")
375            subsystem_name = _subsystem_name
376            _curr_parts_info = _parts_config.get('parts')
377            for _pname in _curr_parts_info.keys():
378                parts_path_dict[_pname] = os.path.relpath(
379                    os.path.dirname(_build_file), self._source_root_dir)
380            parts_info.update(_curr_parts_info)
381        subsystem_config = dict()
382        subsystem_config['subsystem'] = subsystem_name
383        subsystem_config['parts'] = parts_info
384        return subsystem_config, parts_path_dict
385
386    def parse(self):
387        """parse part info from build config file."""
388        if self._is_load:
389            return
390        subsystem_config, parts_path_dict = self._merge_build_config()
391        parts_config = subsystem_config.get('parts')
392        self._parts_module_list.update(parts_config)
393        self._parts_path_dict = parts_path_dict
394        self._is_load = True
395
396    def parts_path_info(self):
397        """parts to path info."""
398        self.parse()
399        return self._parts_path_dict
400
401    def parts_info_filter(self, save_part):
402        if save_part is None:
403            raise Exception
404        self._parts_info_dict = {
405            key: value for key, value in self._parts_info_dict.items() if key in save_part}
406
407
408def get_parts_info(source_root_dir, subsystem_info, build_xts=False):
409    """
410    get parts path info from subsystem info
411    :param source_root_dir: oh project path
412    :param subsystem_info:
413    :param build_xts:
414    :return: parts path info
415    """
416    _phony_target = {}
417    _parts_path_info = {}
418    _parts_hisysevent_config = {}
419    _parts_modules_info = {}
420    _parts_deps = {}
421    for subsystem_name, build_config_info in subsystem_info.items():
422        if not len(build_config_info.get("build_files")):
423            continue
424        build_loader = LoadBuildConfig(source_root_dir, build_config_info, subsystem_name)
425        if subsystem_name == 'xts' and build_xts is False:
426            xts_device_attest_name = ['device_attest_lite', 'device_attest']
427            build_loader.parse()
428            build_loader.parts_info_filter(xts_device_attest_name)
429        _parts_path_info.update(build_loader.parts_path_info())
430    return _parts_path_info
431
432
433def _read_json_file(input_file):
434    if not os.path.exists(input_file):
435        print("file '{}' doesn't exist.".format(input_file))
436        return {}
437    try:
438        with open(input_file, 'r') as input_f:
439            data = json.load(input_f)
440        return data
441    except json.decoder.JSONDecodeError:
442        print("The file '{}' format is incorrect.".format(input_file))
443        raise
444    except Exception:
445        print("read file '{}' failed.".format(input_file))
446        raise
447
448
449def get_product_define_path(source_root_dir):
450    return os.path.join(source_root_dir, 'productdefine', 'common', 'inherit')
451
452
453def components_list_handler(product_file_json):
454    components_list = list()
455    for subsystems in product_file_json.get('subsystems'):
456        for components in subsystems.get('components'):
457            components_list.append(components.get('component'))
458
459    return components_list
460
461
462def product_component_handler(product_file, product_file_path):
463    all_components_dict = dict()
464    components_list = list()
465    try:
466        with open(product_file_path, 'r', encoding='utf-8') as f:
467            product_file_json = json.load(f)
468            components_list = components_list_handler(product_file_json)
469    except FileNotFoundError:
470        print(f"read file {product_file_path} failed.")
471    all_components_dict.update({product_file.split('.')[0]: components_list})
472    return all_components_dict
473
474
475def collect_all_product_component(product_file_dict: dict):
476    all_components_dict = dict()
477    for product_file, product_file_path in product_file_dict.items():
478        product_components_dict = product_component_handler(product_file, product_file_path)
479        all_components_dict.update(product_components_dict)
480    return all_components_dict
481
482
483def get_product_define_dict(source_root_dir):
484    product_define_path = get_product_define_path(source_root_dir)
485    product_file_dict = dict()
486    for file in os.scandir(product_define_path):
487        if file.name.split('.')[-1] == 'json':
488            product_file_dict.update({file.name: os.path.join(product_define_path, file.name)})
489    product_define_dict = collect_all_product_component(product_file_dict)
490    return product_define_dict
491
492
493def output_path_handler(project_path):
494    output_path = os.path.join(project_path, 'interface', 'sdk-js', 'api', 'device-define-common')
495    folder = os.path.exists(output_path)
496    # 多线程创建文件夹问题
497    if not folder:
498        os.makedirs(output_path, exist_ok=True)
499    return output_path
500
501
502def project_path_handler(project_path):
503    if not project_path:
504        project_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
505    return project_path
506
507
508def main():
509    logging.basicConfig(level=logging.INFO)
510    args = get_args()
511    project_path = args.project_path
512    project_path = project_path_handler(project_path)
513    output_path = output_path_handler(project_path)
514    subsystem_config_file = os.path.join(project_path, 'build', 'subsystem_config.json')
515    product_define_dict = get_product_define_dict(project_path)
516    _subsystem_info = get_subsystem_info(subsystem_config_file, project_path)
517    _parts_path_info = get_parts_info(project_path, _subsystem_info)
518    syscap_dict, errors_list = collect_all_product_component_syscap_dict(_parts_path_info, project_path,
519                                                                         product_define_dict)
520    if syscap_dict:
521        dict_to_json(output_path, syscap_dict)
522
523
524if __name__ == "__main__":
525    main()
526