1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2021-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 argparse
17import json
18import os
19import sys
20
21from collections import OrderedDict
22from os.path import join
23from os.path import realpath
24
25
26DEBUG = False
27
28
29def read_json_file(input_file):
30    if not os.path.exists(input_file):
31        if DEBUG:
32            print("file '{}' doesn't exist.".format(input_file))
33        return None
34
35    data = None
36    with open(input_file, 'r') as input_f:
37        data = json.load(input_f, object_pairs_hook=OrderedDict)
38    return data
39
40
41def write_json_file(output_file, content):
42    file_dir = os.path.dirname(os.path.abspath(output_file))
43    if not os.path.exists(file_dir):
44        os.makedirs(file_dir, exist_ok=True)
45    with open(output_file, 'w') as output_f:
46        json.dump(content, output_f)
47
48
49def get_gn_build_content(plugins, plugin_so):
50    so_items = []
51    items = []
52    build_path = os.path.split(realpath(__file__))[0]
53    root_path = realpath(join(build_path, ".."))
54    ut_items = []
55    mst_items = []
56    for plugin_info in plugins:
57        for plugin, info in plugin_info.items():
58            gn_path = realpath(join(root_path, info['path'], "BUILD.gn"))
59            if not os.path.exists(gn_path):
60                print('%s %s' % (gn_path, 'is not exist.'))
61                break
62            if 'loadType' in info:
63                if info['loadType'] == 'dynamic':
64                    target = 'lib%s' % (plugin.lower())
65                    so_items.append('\"%s:%s\",\n' % (info['path'], target))
66            else:
67                items.append('\"%s:%s\",\n' % (info['path'], info['name']))
68            ut_items.append('\"%s:unittest\",\n' % (info['path']))
69            mst_items.append('\"%s:moduletest\",\n' % (info['path']))
70    dynamic_info = 'plugin_dynamic_deps = [\n%s\n]\n' % (''.join(so_items))
71    static_info = ('plugin_static_deps = [\n%s\n]\n' % (''.join(items)))
72    ut_deps = ('plugin_ut_deps = [\n%s\n]\n' % (''.join(list(set(ut_items)))))
73    mst_deps = ('plugin_mst_deps = [\n%s\n]' % (''.join(list(set(mst_items)))))
74    return '%s%s%s%s' % (dynamic_info, static_info, ut_deps, mst_deps)
75
76
77def write_build_file(output_file, plugins, plugin_so, cnt_type):
78    build_info = ""
79    if cnt_type == "gn":
80        build_info = get_gn_build_content(plugins, plugin_so)
81    else:
82        return
83    out_dir = os.path.dirname(output_file)
84    if os.path.isdir(out_dir) is False:
85        os.makedirs(out_dir, exist_ok=True)
86    with open(output_file, 'w') as file_id:
87        if DEBUG:
88            print(build_info)
89        file_id.write(build_info)
90
91
92def write_config_file(output_file, plugins, pipelines, pipelinegroups, is_so):
93    items = []
94    # plugins
95    items.append('plugins:%d \n' % (len(plugins)))
96    for plugin_info in plugins:
97        for plugin, info in plugin_info.items():
98            items.append('%s[' % (plugin))
99            if "threadtype" in info:
100                items.append('%s:%s' %
101                      (info['threadtype'], info['threadname']))
102                if info['threadtype'] == "pool":
103                    items.append(':%d' % (info['threadnumber']))
104            loadtime = 0
105            if "loadtime" in info:
106                loadtime = info['loadtime']
107            load_type = "static"
108            if "loadType" in info:
109                load_type = info['loadType']
110            items.append(']:%d %s\n' % (loadtime, load_type))
111    if len(pipelines) > 0:
112        items.append('pipelines:%d\n' % (len(pipelines)))
113        for pipeline, plugin_list in pipelines.items():
114            items.append('%s:' % (pipeline))
115            for plugin in plugin_list:
116                items.append('%s ' % (plugin))
117            items.append('\n')
118    if len(pipelinegroups) > 0:
119        items.append('pipelinegroups:%d\n' % (len(pipelinegroups)))
120        for pipelinegroup in pipelinegroups:
121            for plugin, pipeline_list in pipelinegroup.items():
122                items.append('%s:' % (plugin))
123                for pipeline in pipeline_list:
124                    items.append('%s ' % (pipeline))
125            items.append('\n')
126    config_info = ''.join(items)
127    out_dir = os.path.dirname(output_file)
128    if os.path.isdir(out_dir) is False:
129        os.makedirs(out_dir, exist_ok=True)
130    with open(output_file, 'w') as file_id:
131        file_id.write(config_info)
132
133
134def check_plugin_config(config_data):
135    if "plugins" not in config_data:
136        return False
137    if "rules" not in config_data:
138        return False
139    return True
140
141
142class ArgsPara():
143    def __init__(self, target_os, product, platform, arch, ram, rom):
144        self.target_os = target_os
145        self.product = product
146        self.platform = platform
147        self.arch = arch
148        self.ram = self.get_memory_size(ram)
149        self.rom = self.get_memory_size(rom)
150
151
152    def get_memory_size(self, memory_detail):
153        # get memory
154        if isinstance(memory_detail, int) is True:
155            return memory_detail
156        if memory_detail.isdigit() is True:
157            return int(memory_detail)
158        size_str = memory_detail[0:-1]
159        size_unit = memory_detail[-1].lower()
160        if size_str.isdigit() is False:
161            return -1
162        size_int = int(size_str)
163        if size_unit == "k":
164            return 1024 * size_int
165        if size_unit == "m":
166            return 1024 * 1024 * size_int
167        if size_unit == "g":
168            return 1024 * 1024 * 1024 * size_int
169        return -1
170
171
172    def is_less(self, args_para):
173        if self.target_os != "double"and args_para.target_os != "double":
174            if self.target_os != args_para.target_os:
175                return False
176        if self.product != args_para.product:
177            return False
178        if self.platform != args_para.platform:
179            return False
180        if self.arch != args_para.arch:
181            return False
182        if self.ram > args_para.ram:
183            return False
184        if self.rom > args_para.rom:
185            return False
186        return True
187
188
189    def set_para(self, name, value):
190        if name == "target_os":
191            self.target_os = value
192        if name == "product":
193            self.product = value
194        if name == "platform":
195            self.platform = value
196        if name == "arch":
197            self.arch = value
198        if name == "ram":
199            self.ram = self.get_memory_size(value)
200        if name == "rom":
201            self.rom = self.get_memory_size(value)
202        return ""
203
204
205def is_build_rule(rule_list, args_para):
206    args_list = ["target_os", "product", "platform", "arch", "ram", "rom"]
207    for rule in rule_list:
208        config_args = ArgsPara(args_para.target_os,
209                               args_para.product,
210                               args_para.platform,
211                               args_para.arch,
212                               args_para.ram,
213                               args_para.rom)
214        for arg in args_list:
215            if arg in rule:
216                config_args.set_para(arg, rule[arg])
217        if config_args.is_less(args_para) is True:
218            return True
219    return False
220
221
222def add_plugin(plugins, loadorder, threads, config_plugins):
223    thread_list = {}
224    for name, value in threads.items():
225        if name == "singledthread":
226            for plugin, thread_name in value.items():
227                if plugin not in config_plugins:
228                    raise Exception("[{}] not exist in plugins".format(plugin))
229                thread_list.update({plugin: {'threadtype': 'thread',
230                                            'threadname': thread_name}})
231        if name == "sharedthread":
232            for thread_name, plugin_list in value.items():
233                for plugin in plugin_list:
234                    if plugin not in config_plugins:
235                        raise Exception("[{}] not in plugins".format(plugin))
236                    thread_list.update({plugin: {'threadtype': 'thread',
237                                                'threadname': thread_name}})
238        if name == "threadpool":
239            number = 4
240            if "number" in value:
241                number = value['number']
242            if "poolinfo" not in value:
243                raise Exception("[{}] not exist in threadpool".format(value))
244            poolinfo = value['poolinfo']
245            threadpool_name = "pool"
246            if "poolname" in poolinfo:
247                threadpool_name = poolinfo['poolname']
248            if "plugins" not in poolinfo:
249                raise Exception("[{}] not exist in poolinfo".format(poolinfo))
250
251            for plugin in poolinfo['plugins']:
252                if plugin not in config_plugins:
253                    raise Exception("[{}] notexist in plugins".format(plugin))
254                thread_list.update({plugin: {'threadtype': 'pool',
255                                            'threadname': threadpool_name,
256                                            'threadnumber': number}})
257    for plugin, value in loadorder.items():
258        plugin_info = {}
259        if plugin not in config_plugins:
260            raise Exception("[{}] not exist in plugins".format(plugin))
261        plugin_info.update(value)
262        plugin_info.update(config_plugins[plugin])
263        if plugin in thread_list:
264            plugin_info.update(thread_list[plugin])
265        plugins.append({plugin: plugin_info})
266
267
268def add_pipelines(pipelines, config_pipelines, config_plugins):
269    for name, value in config_pipelines.items():
270        for plugin in value:
271            if plugin not in config_plugins:
272                raise Exception("[{}]  not exist in pipeline".format(value))
273        pipelines.update({name: value})
274
275
276def add_pipeline_groups(pipelinegrps, cfg_pipelinegrps, pipelines, plugins):
277    for plugin, pipeline_list in cfg_pipelinegrps.items():
278        found_plugin = False
279        for item in plugins:
280            if plugin in item:
281                found_plugin = True
282                break
283        if found_plugin is False:
284            raise Exception("[{}] not exist in plugins".format(plugin))
285        for pipeline in pipeline_list:
286            if pipeline not in pipelines:
287                raise Exception("[{}] not exist in pipeline".format(pipelines))
288        pipelinegrps.append({plugin: pipeline_list})
289
290
291def generate_plugin(input_file, output_build_file, output_config_file,
292                    plugin_so, build_file_type, args_para):
293    data = read_json_file(input_file)
294    if data is None:
295        raise Exception("file [{}] does not exist.".format(input_file))
296    #parse plugins
297    #parse thread
298    if DEBUG:
299        print(data)
300    check_rslt = check_plugin_config(data)
301    if check_rslt is not True:
302        raise Exception("file [{}] content is error".format(input_file))
303    plugins = []
304    pipelines = {}
305    pipelinegroups = []
306    config_plugins = data['plugins']
307    for rule_item in data['rules']:
308        if 'rule' not in rule_item:
309            raise Exception("[{}] invalid, no rule item.".format(rule_item))
310        if 'info' not in rule_item:
311            raise Exception("[{}] invalid, no info item.".format(rule_item))
312        rule_list = rule_item['rule']
313        check_rslt = is_build_rule(rule_list, args_para)
314        if check_rslt is False:
315            continue
316        info = rule_item['info']
317        if 'loadorder' not in info:
318            raise Exception("[{}] invalid, no loadorder item.".format(info))
319        threads = {}
320        if 'threads' in info:
321            threads = info['threads']
322        add_plugin(plugins, info['loadorder'], threads, config_plugins)
323        if 'pipelines' in info:
324            add_pipelines(pipelines, info['pipelines'], config_plugins)
325        if 'pipelinegroups' in info:
326            add_pipeline_groups(pipelinegroups, info['pipelinegroups'],
327                                pipelines, plugins)
328        break
329    if DEBUG:
330        print("xxxxxxxxx plugins xxxxxxxxxxxx")
331        print(plugins)
332        print("xxxxxxxxx pipelines xxxxxxxxxxxx")
333        print(pipelines)
334        print("xxxxxxxxx pipelinegroups xxxxxxxxxxxx")
335        print(pipelinegroups)
336    write_config_file(output_config_file, plugins, pipelines,
337                      pipelinegroups, plugin_so)
338    write_build_file(output_build_file, plugins, plugin_so, build_file_type)
339
340
341def main():
342    parser = argparse.ArgumentParser()
343    parser.add_argument('--input-file', help='plugin build config json',
344                        required=True)
345    parser.add_argument('--plugin-config-file', help='plugin config json file',
346                        required=True)
347    parser.add_argument('--plugin-build-file', help='plugin config json file',
348                        required=True)
349    parser.add_argument('--target_os', help='target os', required=True)
350    parser.add_argument('--double_framework', help='double os framework',
351                        required=True)
352    parser.add_argument('--target_platform', help='product type',
353                        required=True)
354    parser.add_argument('--target_cpu', help='arm or arm64', required=True)
355    parser.add_argument('--plugin_so', help='build library', required=True)
356    parser.add_argument('--plugin_target_platform', help='hisi,qcom, mtk,etc.',
357                        required=True)
358    parser.add_argument('--plugin_target_ram', help='ram', required=True)
359    parser.add_argument('--plugin_target_rom', help='rom', required=True)
360    parser.add_argument('--build-file-type', help='build file type',
361                        required=False, default='gn')
362    args = parser.parse_args()
363    target_os = args.target_os
364
365    if args.double_framework == "true":
366        target_os = "double"
367    plugin_so = False
368    if args.plugin_so == "true":
369        plugin_so = True
370    args_para = ArgsPara(target_os,
371                         args.target_platform,
372                         args.plugin_target_platform,
373                         args.target_cpu,
374                         args.plugin_target_ram,
375                         args.plugin_target_rom)
376    if args_para.rom == -1 or args_para.ram == -1:
377        raise Exception("input ram=%s,rom=%s invalid".
378                  format(args.plugin_target_ram, args.plugin_target_rom))
379    generate_plugin(args.input_file, args.plugin_build_file,
380                    args.plugin_config_file, plugin_so,
381                    args.build_file_type, args_para)
382    return 0
383
384
385if __name__ == '__main__':
386    sys.exit(main())
387