1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2021 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 argparse
17import sys
18import os
19import pathlib
20import re
21from convert_permissions import convert_permissions
22
23sys.path.append(
24    os.path.dirname(
25        os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
26from scripts.util.file_utils import write_file, read_json_file, \
27    write_json_file  # noqa: E402
28from scripts.util import build_utils  # noqa: E402
29
30# Import jinja2 from third_party/jinja2
31sys.path.insert(1, os.path.join(os.path.abspath(
32                   os.path.join(os.path.dirname(__file__),
33                   '..', '..', '..')), 'third_party'))
34from jinja2 import Template  # noqa: E402  # pylint: disable=F0401
35
36KEYS = ['target_os', 'install_dir', 'module_label', 'build_only']
37
38
39class SdkTargets:
40
41    def __init__(self, os_type):
42        self.os_type = os_type
43        self.targets = []
44
45    def add_target(self, target):
46        if target not in self.targets:
47            self.targets.append(target)
48
49    def get_targets(self):
50        return self.targets
51
52
53def check_keys(keys):
54    for key in keys:
55        if key not in KEYS:
56            raise Exception(
57                'Error: failed to parse ohos sdk description file, missing {}.'
58                .format(key))
59
60
61def get_sdk_type(path_name: str):
62    p = pathlib.Path(path_name)
63    if path_name.startswith('/'):
64        top_dir = p.parts[1]
65    else:
66        top_dir = p.parts[0]
67    return top_dir
68
69
70def add_target(item: dict, target: str, sdk_systems: list):
71    for _os in sdk_systems:
72        if _os == 'linux' or _os == 'Linux':
73            item.get('targets').get('linux').add_target('"%s",' % target)
74        elif _os == 'windows' or _os == 'Windows':
75            item.get('targets').get('windows').add_target('"%s",' % target)
76        elif _os == 'darwin' or _os == 'Darwin':
77            item.get('targets').get('darwin').add_target('"%s",' % target)
78        elif _os == 'ohos' or _os == 'Ohos':
79            item.get('targets').get('ohos').add_target('"%s",' % target)
80
81
82def write_sdk_build_gni(sdk_targets: list, build_only_targets: list, gni: str):
83    template = Template(
84        """#Generated code, DONOT modify it.
85            ohos_sdk_modules = {
86                {% for item in sdk_targets %}
87
88                    {% set sdk_type = item.get('type') %}
89                    {% set targets = item.get('targets') %}
90                    {% set systems = targets.keys() %}
91                    {% set _sdk_type = sdk_type.replace('-', '_') %}
92
93                        {{ _sdk_type }} = {
94                            {% for os in systems %}
95                                {{ os }} = [
96                                    {% for t in targets.get(os).get_targets() %}
97                                        {{ t }}
98                                    {% endfor %}
99                                ]
100                            {% endfor %}
101                        }
102                {% endfor %}
103
104                extras = [
105                {% for t in build_only_targets%}
106                    "{{ t }}",
107                {% endfor %}
108                ]
109            }
110        """,  # noqa E501
111        trim_blocks=True,
112        lstrip_blocks=True)
113
114    contents = template.render(
115        sdk_targets=sdk_targets, build_only_targets=build_only_targets)
116    write_file(gni, contents)
117
118
119def get_build_gn(label: str):
120    match = re.search(r"(.*?):(.*?)", label)
121    if match:
122        gn = '{}/BUILD.gn'.format(match.group(1))
123        if gn.startswith("//"):
124            return gn[len("//"):]
125        else:
126            return gn
127    else:
128        raise Exception("failed to get BUILD.gn of {}".format(label))
129
130
131def variant_to_product(variant: dict, options: dict):
132    relations = read_json_file(options.variant_to_product)
133    if variant in relations.keys():
134        return relations.get(variant)
135    else:
136        raise Exception('Error: failed to read {} in {}'.format(
137            variant, options.variant_to_product))
138
139
140def expand_platform_targets(options, label: str, install_dir: str):
141    base = options.base_platform
142    platforms = options.platforms
143    variant = list(set(platforms) - set([base]))
144
145    if label.find('${base}') != -1:
146        return [label.replace('${base}', base)], [install_dir]
147    elif label.find('${platforms}') != -1:
148        return [label.replace('${platforms}', p) for p in platforms], [
149            install_dir.replace('${platforms}',
150                                variant_to_product(c, options))
151            for c in platforms
152        ]
153    elif label.find('${variant}') != -1:
154        return [label.replace('${variant}', c) for c in variant], [
155            install_dir.replace('${variant}', variant_to_product(c, options))
156            for c in variant
157        ]
158    else:
159        return [label], [install_dir]
160
161
162def add_sdk_targets(sdk_type, sdk_targets):
163    sdk_targets.append({
164        'type': sdk_type,
165        'targets': {
166            'linux': SdkTargets('linux'),
167            'windows': SdkTargets('windows'),
168            'darwin': SdkTargets('darwin'),
169            'ohos': SdkTargets('ohos')
170        }
171    })
172
173
174def parse_description_file(options):
175    data = read_json_file(options.sdk_description_file)
176    if data is None:
177        raise Exception("read file '{}' failed.".format(
178            options.sdk_description_file))
179
180    module_install_infos = []
181    sdk_types = []
182    sdk_targets = []
183    build_only_targets = []
184
185    for d in data:
186        check_keys(d.keys())
187
188        label = d.get('module_label')
189        install_dir = d.get('install_dir')
190        build_only = d.get('build_only')
191
192        # skip labels that we cannot find.
193        rebased_build_gn = build_utils.rebase_path(
194            get_build_gn(label), current_base=options.source_root_dir)
195        if not os.path.exists(rebased_build_gn):
196            continue
197
198        if build_only:
199            build_only_targets.append(label)
200            continue
201
202        module_labels, install_dirs = expand_platform_targets(
203            options, label, install_dir)
204        target_os = d.get('target_os')
205
206        sdk_type = get_sdk_type(install_dir)
207
208        if sdk_type not in sdk_types:
209            add_sdk_targets(sdk_type, sdk_targets)
210            sdk_types.append(sdk_type)
211
212        for item in sdk_targets:
213            if item['type'] == sdk_type:
214                for m in module_labels:
215                    add_target(item, m, target_os)
216
217        for label, install_dir in zip(module_labels, install_dirs):
218            install_info = {
219                'label': label,
220                'install_dir': install_dir
221            }
222            module_install_infos.append(install_info)
223
224    return {
225        "sdk_targets": sdk_targets,
226        "install_infos": module_install_infos,
227        "sdk_types": sdk_types,
228        "build_only_targets": build_only_targets
229    }
230
231
232def main():
233    parser = argparse.ArgumentParser()
234    parser.add_argument('--sdk-description-file', required=True)
235    parser.add_argument('--sdk-install-info-file', required=True)
236    parser.add_argument('--sdk-modules-gni', required=True)
237    parser.add_argument('--sdk-types-file', required=True)
238    parser.add_argument('--base-platform', required=True)
239    parser.add_argument('--platforms', action='append', required=True)
240    parser.add_argument('--source-root-dir', required=True)
241    parser.add_argument('--variant-to-product', required=True)
242    parser.add_argument('--node-js', required=True)
243
244    options = parser.parse_args()
245
246    convert_permissions(options.source_root_dir, options.node_js)
247
248    data = parse_description_file(options)
249
250    write_sdk_build_gni(
251        data.get('sdk_targets'), data.get('build_only_targets'),
252        options.sdk_modules_gni)
253    write_json_file(options.sdk_install_info_file, data.get('install_infos'))
254    with open(options.sdk_types_file, 'w') as f:
255        f.write('\n'.join(data.get('sdk_types')))
256
257
258if __name__ == '__main__':
259    sys.exit(main())
260