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 sys
17import os
18from util.log_util import LogUtil
19from resources.config import Config
20from exceptions.ohos_exception import OHOSException
21from scripts.util.file_utils import read_json_file, write_json_file  # noqa: E402
22from containers.status import throw_exception
23from . import merge_platform_build
24
25
26class PlatformsLoader:
27    def __init__(self, platforms_config_file, source_root_dir, root_build_dir,
28                 target_arch, scalable_build):
29        self._platforms_config_file = platforms_config_file
30        self._source_root_dir = source_root_dir
31        self._root_build_dir = root_build_dir
32        self._platforms_info = {}
33        self._all_parts = {}
34        self._all_stubs = {}
35        self._is_load = False
36        self._target_arch = target_arch
37        self._scalable_build = scalable_build
38
39    @staticmethod
40    def _load_platform_config(platform_config_file):
41        if not os.path.exists(platform_config_file):
42            raise Exception(
43                "config file '{}' doesn't exist.".format(platform_config_file))
44        _platform_config = read_json_file(platform_config_file)
45        if _platform_config is None:
46            raise Exception(
47                "read file '{}' failed.".format(platform_config_file))
48        if 'parts' in _platform_config:
49            parts = _platform_config.get('parts')
50        else:
51            parts = []
52        if 'stub_parts' in _platform_config:
53            stub_parts = _platform_config.get('stub_parts')
54        else:
55            stub_parts = []
56        return parts, stub_parts
57
58    @throw_exception
59    def _read_platforms_config(self):
60        if not os.path.exists(self._platforms_config_file):
61            raise OHOSException("config file '{}' doesn't exist.".format(
62                self._platforms_config_file), "2012")
63        config_info = read_json_file(self._platforms_config_file)
64        if config_info is None:
65            raise OHOSException("read file '{}' failed.".format(
66                self._platforms_config_file), "2012")
67        config_version = config_info.get('version')
68        platforms_info = config_info.get('platforms')
69
70        platforms_config = {}
71        for _name, _platform_infos in platforms_info.items():
72            if config_version >= 2:
73                _info = _platform_infos
74                target_os = _info.get('target_os')
75                target_cpu = _info.get('target_cpu')
76                if target_os is None or target_cpu is None:
77                    error_info = "platform '{}' config incorrect,"\
78                        "target_os or target_cpu is None."
79                    raise OHOSException(error_info.format(_name), "2012")
80                arch = "{}_{}".format(target_os, target_cpu)
81                if arch in platforms_config:
82                    _infos = platforms_config.get(arch)
83                else:
84                    _infos = {}
85                _infos[_name] = _info
86                platforms_config[arch] = _infos
87            else:
88                for _info in _platform_infos:
89                    target_os = _info.get('target_os')
90                    target_cpu = _info.get('target_cpu')
91                    if target_os is None or target_cpu is None:
92                        error_info = "platform '{}' config incorrect,"\
93                            "target_os or target_cpu is None."
94                        raise OHOSException(error_info.format(_name), "2012")
95                    arch = "{}_{}".format(target_os, target_cpu)
96                    if arch in platforms_config:
97                        _infos = platforms_config.get(arch)
98                    else:
99                        _infos = {}
100                    _infos[_name] = _info
101                    platforms_config[arch] = _infos
102        return platforms_config
103
104    @throw_exception
105    def _loading(self):
106        if self._is_load:
107            return
108        all_platforms_config = self._read_platforms_config()
109        _platforms_info = all_platforms_config.get(self._target_arch)
110        config_base_dir = os.path.dirname(self._platforms_config_file)
111        all_parts = {}
112        all_stubs = {}
113        if _platforms_info is None:
114            raise OHOSException(
115                'this product do not support thish arch', '2001')
116        for _platform_name, _info in _platforms_info.items():
117            if self._scalable_build is False:
118                _file_name = _info.get('parts_config')
119                if _file_name is None:
120                    _file_name = os.path.join(
121                        'target_platforms', "{}.build".format(_platform_name))
122                _platform_config_file = os.path.join(config_base_dir,
123                                                     _file_name)
124            else:
125                required_build_file = os.path.join(
126                    config_base_dir, _info.get('base_parts_config'))
127                optional_build_file = os.path.join(
128                    config_base_dir, _info.get('optional_parts_config'))
129                stub_build_file = os.path.join(config_base_dir,
130                                               _info.get('stub_parts_config'))
131                _platform_config_file = '{}.build'.format(_platform_name)
132
133                merge_platform_build.do_merge(required_build_file,
134                                              optional_build_file,
135                                              stub_build_file,
136                                              _platform_config_file)
137
138            parts, stubs = self._load_platform_config(_platform_config_file)
139            all_parts[_platform_name] = parts
140            all_stubs[_platform_name] = stubs
141
142        self._platforms_info = _platforms_info
143        self._all_parts = all_parts
144        self._all_stubs = all_stubs
145        self._is_load = True
146
147    def get_all_parts(self):
148        self._loading()
149        all_parts = {}
150        for _platform, _parts in self._all_parts.items():
151            part_list = []
152            for part_def in _parts:
153                _part_name = part_def.split(':')[1]
154                part_list.append(_part_name)
155            all_parts[_platform] = part_list
156        return all_parts
157
158    def get_all_stubs(self):
159        self._loading()
160        all_parts = {}
161        for _platform, _parts in self._all_stubs.items():
162            part_list = []
163            for part_def in _parts:
164                _part_name = part_def.split(':')[1]
165                part_list.append(_part_name)
166            all_parts[_platform] = part_list
167        return all_parts
168
169    def platforms_toolchain(self):
170        self._loading()
171        platform_toolchain = {}
172        toolchain_platform = {}
173        for key, val in self._platforms_info.items():
174            _toolchain = val.get('toolchain')
175            platform_toolchain[key] = _toolchain
176            toolchain_platform[_toolchain] = key
177        _result = {
178            "platform_toolchain": platform_toolchain,
179            "toolchain_platform": toolchain_platform
180        }
181        return _result
182
183
184def get_platforms_info(platforms_config_file, source_root_dir, root_build_dir,
185                       target_arch, config_output_relpath, scalable_build):
186    platform_loader = PlatformsLoader(platforms_config_file, source_root_dir,
187                                      root_build_dir, target_arch,
188                                      scalable_build)
189
190    platforms_info_output_dir = 'platforms_info'
191    all_parts = platform_loader.get_all_parts()
192    all_parts_file = os.path.join(source_root_dir, config_output_relpath,
193                                  platforms_info_output_dir, "all_parts.json")
194    write_json_file(all_parts_file, all_parts)
195    LogUtil.hb_info(
196        "generate all parts of platforms info to '{}'".format(all_parts_file), mode=Config.log_mode)
197
198    # variant to toolchain and toolchain to variant
199    toolchain_to_variant_dict = platform_loader.platforms_toolchain()
200    toolchain_variant_info_file = os.path.join(source_root_dir,
201                                               config_output_relpath,
202                                               platforms_info_output_dir,
203                                               "toolchain_to_variant.json")
204    write_json_file(toolchain_variant_info_file,
205                    toolchain_to_variant_dict,
206                    check_changes=True)
207    LogUtil.hb_info("generate toolchain to variant of platforms info to '{}'".format(
208        toolchain_variant_info_file), mode=Config.log_mode)
209
210    result = {}
211    result['all_parts'] = all_parts
212    result['all_stubs'] = platform_loader.get_all_stubs()
213    result['variant_toolchain_info'] = toolchain_to_variant_dict
214    return result
215