1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2021-2023 Huawei Device Co., Ltd.
5#
6# HDF is dual licensed: you can use it either under the terms of
7# the GPL, or the BSD license, at your option.
8# See the LICENSE file in the root of this repository for complete details.
9
10import argparse
11import json
12import os
13import re
14import sys
15
16
17class TokenType(object):
18    UNKNOWN = 0
19    COMMENT = 1
20    PACKAGE = 2
21    IMPORT = 3
22    INTERFACE = 4
23    CALLBACK = 5
24    ID = 6
25    END_OF_FILE = 7
26
27
28class Token(object):
29    def __init__(self, file_name, token_type, value):
30        self.token_type = token_type
31        self.value = value
32        self.row = 1
33        self.col = 1
34        self.file_name = file_name
35
36    def clean(self):
37        self.token_type = TokenType.UNKNOWN
38        self.value = ""
39        self.row = 1
40        self.col = 1
41
42    def dump(self):
43        return "<{}:{}:{}: {},'{}'>".format(self.file_name, self.row, self.col,
44                                            self.token_type, self.value)
45
46    def info(self):
47        return "{}:{}:{}".format(self.file_name, self.row, self.col)
48
49
50class Char(object):
51    def __init__(self, is_eof, char):
52        self.is_eof = is_eof
53        self.char = char
54
55    def dump(self):
56        return "{%s, %s}" % (self.is_eof, self.char)
57
58
59class Lexer(object):
60    _key_words = {
61        "package": TokenType.PACKAGE,
62        "import": TokenType.IMPORT,
63        "interface": TokenType.INTERFACE,
64        "callback": TokenType.CALLBACK,
65    }
66
67    def __init__(self, idl_file_path):
68        self.have_peek = False
69        with open(idl_file_path, 'r') as idl_file:
70            file_info = idl_file.read()
71        self.data = file_info
72        self.data_len = len(self.data)
73        self.read_index = 0
74        self.cur_token = Token(os.path.basename(idl_file_path),
75                               TokenType.UNKNOWN, "")
76        self.cur_row = 1
77        self.cur_col = 1
78
79    def peek_char(self, peek_count=0):
80        index = self.read_index + peek_count
81        if index >= self.data_len:
82            return Char(True, '0')
83        return Char(False, self.data[index])
84
85    def get_char(self):
86        if self.read_index >= self.data_len:
87            return Char(True, '0')
88        read_index = self.read_index
89        self.read_index += 1
90        if self.data[read_index] == '\n':
91            self.cur_row += 1
92            self.cur_col = 1
93        else:
94            self.cur_col += 1
95        return Char(False, self.data[read_index])
96
97    def peek_token(self):
98        if not self.have_peek:
99            self.read_token()
100            self.have_peek = True
101        return self.cur_token
102
103    def get_token(self):
104        if not self.have_peek:
105            self.read_token()
106        self.have_peek = False
107        return self.cur_token
108
109    def read_token(self):
110        self.cur_token.clean()
111        while not self.peek_char().is_eof:
112            new_char = self.peek_char()
113            if new_char.char.isspace():
114                self.get_char()
115                continue
116            self.cur_token.row = self.cur_row
117            self.cur_token.col = self.cur_col
118            if new_char.char.isalpha() or new_char.char == '_':
119                self.read_id()
120                return
121            if new_char.char == '/':
122                self.read_comment()
123                return
124            self.cur_token.value = new_char.char
125            self.cur_token.token_type = TokenType.UNKNOWN
126            self.get_char()
127            return
128        self.cur_token.token_type = TokenType.END_OF_FILE
129
130    def read_id(self):
131        token_value = []
132        token_value.append(self.get_char().char)
133        while not self.peek_char().is_eof:
134            new_char = self.peek_char()
135            if new_char.char.isalpha() or new_char.char.isdigit(
136            ) or new_char.char == '_' or new_char.char == '.':
137                token_value.append(new_char.char)
138                self.get_char()
139                continue
140            break
141        key = "".join(token_value)
142        if key in self._key_words.keys():
143            self.cur_token.token_type = self._key_words[key]
144        else:
145            self.cur_token.token_type = TokenType.ID
146        self.cur_token.value = key
147
148    def read_comment(self):
149        token_value = []
150        token_value.append(self.get_char().char)
151        new_char = self.peek_char()
152        if not new_char.is_eof:
153            if new_char.char == '/':
154                self.read_line_comment(token_value)
155                return
156            elif new_char.char == '*':
157                self.read_block_comment(token_value)
158                return
159        self.cur_token.token_type = TokenType.UNKNOWN
160        self.cur_token.value = "".join(token_value)
161
162    def read_line_comment(self, token_value):
163        token_value.append(self.get_char().char)
164        while not self.peek_char().is_eof:
165            new_char = self.get_char()
166            if new_char.char == '\n':
167                break
168            token_value.append(new_char.char)
169        self.cur_token.token_type = TokenType.COMMENT
170        self.cur_token.value = "".join(token_value)
171
172    def read_block_comment(self, token_value):
173        # read *
174        token_value.append(self.get_char().char)
175        while not self.peek_char().is_eof:
176            new_char = self.get_char()
177            token_value.append(new_char.char)
178            if new_char.char == '*' and self.peek_char().char == '/':
179                token_value.append(self.get_char().char)
180                break
181        value = "".join(token_value)
182        if value.endswith("*/"):
183            self.cur_token.token_type = TokenType.COMMENT
184        else:
185            self.cur_token.token_type = TokenType.UNKNOWN
186        self.cur_token.value = value
187
188
189# module info of all idl
190class ModuleInfo(object):
191    package = ""
192    version = ""
193    include_dirs = set()
194    out_dir = ""
195    sources = []
196    proxy_sources = []
197    stub_sources = []
198    proxy_deps = []
199    stub_deps = []
200    header_deps = []
201
202    @staticmethod
203    def json_info():
204        include_dirs_ret = sorted(list(ModuleInfo.include_dirs))
205        ModuleInfo.sources.sort()
206        ModuleInfo.proxy_sources.sort()
207        ModuleInfo.stub_sources.sort()
208
209        result = {
210            "package": ModuleInfo.package,
211            "version": ModuleInfo.version,
212            "include_dirs": include_dirs_ret,
213            "out_dir": ModuleInfo.out_dir,
214            "sources": ModuleInfo.sources,
215            "proxy_sources": ModuleInfo.proxy_sources,
216            "stub_sources": ModuleInfo.stub_sources,
217            "proxy_deps": ModuleInfo.proxy_deps,
218            "stub_deps": ModuleInfo.stub_deps,
219            "header_deps": ModuleInfo.header_deps,
220        }
221        return json.dumps(result)
222
223
224class Option(object):
225    system = "full"
226    mode = "ipc"
227    language = "cpp"
228    gen_dir = ""
229    root_package = ""
230    root_path = ""
231    idl_sources = []
232    imports = []
233
234    @staticmethod
235    def load(opt_args):
236        Option.system = opt_args.system
237        Option.mode = opt_args.mode
238        Option.language = opt_args.language
239
240        if opt_args.out == "":
241            raise Exception(
242                "the gen_dir '{}' is empty, please check input".format(
243                    opt_args.out))
244        else:
245            Option.gen_dir = opt_args.out
246
247        map_result = opt_args.root.split(":")
248        if len(map_result) != 2:
249            raise Exception(
250                "the package path '{}' is valid, please check input".format(
251                    opt_args.root))
252        else:
253            Option.root_package = map_result[0]
254            Option.root_path = map_result[1]
255
256        if len(opt_args.file) == 0:
257            raise Exception("the idl sources is empty, please check input")
258        else:
259            Option.idl_sources = opt_args.file
260
261        if opt_args.imports is not None:
262            Option.imports = opt_args.imports
263
264    @staticmethod
265    def dump():
266        result = {
267            "system": Option.system,
268            "kernel": Option.kernel,
269            "mode": Option.mode,
270            "language": Option.language,
271            "gen_dir": Option.gen_dir,
272            "root_package": Option.root_package,
273            "root_path": Option.root_path,
274            "idl_sources": Option.idl_sources
275        }
276        return json.dumps(result)
277
278
279class IdlType(object):
280    INTERFACE = 1
281    CALL_INTERFACE = 2
282    CALLBACK = 3
283    TYPES = 4
284
285
286# file detail of idl file
287class IdlDetail(object):
288    def __init__(self, path):
289        self.package = ""
290        self.idl_type = IdlType.TYPES
291        self.imports = []
292        self.file_path = path
293
294        self.file_name = os.path.basename(self.file_path)
295        self.name = self.file_name.split('.')[0]
296
297    # package + file name, like 'ohos.hdi.foo.v1_0.IFoo'
298    def full_name(self):
299        return "{}.{}".format(self.package, self.name)
300
301    def dump(self):
302        result = {
303            "package": self.package,
304            "type": self.idl_type,
305            "imports": self.imports,
306            "path": self.file_path
307        }
308        return json.dumps(result)
309
310
311class IdlParser(object):
312    def parse(self, ):
313        all_idl_details = {}
314        if Option.language == "c":
315            self.parse_c_idl_files(all_idl_details)
316            self.parse_deps(all_idl_details)
317            self.parse_module_info(all_idl_details)
318            return
319
320        for idl_file in Option.idl_sources:
321            idl_detail = self.parse_one(idl_file)
322            all_idl_details[idl_detail.full_name()] = idl_detail
323        self.parse_deps(all_idl_details)
324        self.parse_module_info(all_idl_details)
325
326    def parse_one(self, file_path):
327        cur_idl_detail = IdlDetail(file_path)
328        lex = Lexer(file_path)
329        while lex.peek_token().token_type != TokenType.END_OF_FILE:
330            cur_token_type = lex.peek_token().token_type
331            if cur_token_type == TokenType.PACKAGE:
332                self.parse_package(lex, cur_idl_detail)
333            elif cur_token_type == TokenType.IMPORT:
334                self.parse_import(lex, cur_idl_detail)
335            elif cur_token_type == TokenType.CALLBACK:
336                cur_idl_detail.idl_type = IdlType.CALLBACK
337                lex.get_token()
338            elif cur_token_type == TokenType.INTERFACE:
339                self.parse_interface(lex, cur_idl_detail)
340            else:
341                lex.get_token()
342        return cur_idl_detail
343
344    def parse_c_idl_files(self, all_idl_details):
345        idl_sources_set = set()
346        idl_queue = []
347        for idl_file in Option.idl_sources:
348            idl_queue.append(idl_file)
349        while len(idl_queue) > 0:
350            cur_idl_file = idl_queue.pop(0)
351            if cur_idl_file in idl_sources_set:
352                continue
353            idl_sources_set.add(cur_idl_file)
354            self.parse_c_idl_files_import(cur_idl_file, idl_queue)
355        for idl_file in idl_sources_set:
356            idl_detail = self.parse_one(idl_file)
357            all_idl_details[idl_detail.full_name()] = idl_detail
358        self.merged_idl_details(all_idl_details)
359
360    def parse_c_idl_files_import(self, file_path, idl_queue):
361        lex = Lexer(file_path)
362        while lex.peek_token().token_type != TokenType.END_OF_FILE:
363            cur_token_type = lex.peek_token().token_type
364            if cur_token_type == TokenType.IMPORT:
365                lex.get_token()
366                token = lex.peek_token()
367                if lex.peek_token().token_type != TokenType.ID:
368                    raise Exception("{}: expected package name before '{}'".format(
369                        token.info(), token.value))
370                idl_queue.append(
371                    CodeGen.get_package_path(token.value) + ".idl")
372            lex.get_token()
373
374    def merged_idl_details(self, all_idl_details):
375        merged_details = {}
376        source_idl_detail = self.parse_one(Option.idl_sources[0])
377        for _, idl_detail in all_idl_details.items():
378            idl_detail.package = source_idl_detail.package
379            idl_detail.version = source_idl_detail.version
380        for _, idl_detail in all_idl_details.items():
381            if idl_detail.full_name() not in merged_details:
382                imports = []
383                for import_name in idl_detail.imports:
384                    import_idl = all_idl_details[import_name]
385                    if import_idl.full_name() in imports:
386                        continue
387                    if import_idl.full_name() != idl_detail.full_name():
388                        imports.append(import_idl.full_name())
389                idl_detail.imports = imports
390                merged_details[idl_detail.full_name()] = idl_detail
391            else:
392                for import_name in idl_detail.imports:
393                    import_idl = all_idl_details[import_name]
394                    merged_detail = merged_details[idl_detail.full_name()]
395                    if import_idl.full_name() in merged_detail.imports:
396                        continue
397                    if import_idl.full_name() != idl_detail.full_name():
398                        merged_detail.imports.append(import_idl.full_name())
399        all_idl_details.clear()
400        for key, value in merged_details.items():
401            all_idl_details[key] = value
402
403    def parse_package(self, lex, cur_idl_detail):
404        lex.get_token()  # package token
405        token = lex.peek_token()
406        if token.token_type != TokenType.ID:
407            raise Exception("{}: expected package name before '{}'".format(
408                token.info(), token.value))
409        token = lex.get_token()
410        if not self.parse_version(token.value, cur_idl_detail):
411            raise Exception("{}: failed to parse package name '{}'".format(
412                token.info(), token.value))
413
414    def parse_version(self, package_name, cur_idl_detail):
415        result = re.findall(r'\w+(?:\.\w+)*\.[V|v](\d+)_(\d+)', package_name)
416        if len(result) > 0:
417            cur_idl_detail.package = package_name
418            major_version = result[0][0]
419            minor_version = result[0][1]
420            cur_idl_detail.version = "{}.{}".format(major_version, minor_version)
421            return True
422        return False
423
424    def parse_import(self, lex, cur_idl_detail):
425        lex.get_token()  # import token
426        if lex.peek_token().token_type != TokenType.ID:
427            token = lex.peek_token()
428            raise Exception("{}: expected package name before '{}'".format(
429                token.info(), token.value))
430        cur_idl_detail.imports.append(lex.get_token().value)
431
432    def parse_interface(self, lex, cur_idl_detail):
433        lex.get_token()  # interface token
434        if lex.peek_token().token_type != TokenType.ID:
435            token = lex.peek_token()
436            raise Exception("{}: expected interface name before '{}'".format(
437                token.info(), token.value))
438        token = lex.get_token()
439        interface_name = token.value
440        if interface_name != cur_idl_detail.name:
441            raise Exception(
442                "{}: interface name '{}' does not match file name '{}'".format(
443                    token.info(), interface_name, cur_idl_detail.file_name))
444        if cur_idl_detail.idl_type != IdlType.CALLBACK:
445            cur_idl_detail.idl_type = IdlType.INTERFACE
446
447    def parse_deps(self, all_idl_details):
448        for detail_name, idl_detail in all_idl_details.items():
449            self.query_and_update_idl_type(idl_detail, all_idl_details)
450
451    # update interface idl file type if the file import by other idl file
452    def query_and_update_idl_type(self, idl_detail, all_idl_details):
453        for other_name, other_detail in all_idl_details.items():
454            if idl_detail.full_name() == other_name:
455                continue
456            if self.imported_by_other_idl(idl_detail, other_detail) and idl_detail.idl_type == IdlType.INTERFACE:
457                idl_detail.idl_type = IdlType.CALL_INTERFACE
458                break
459
460    def imported_by_other_idl(self, idl_detail, other_detail):
461        for import_name in other_detail.imports:
462            if idl_detail.full_name() == import_name:
463                return True
464        return False
465
466    def parse_module_info(self, all_idl_details):
467        generator = CodeGenFactory.create_code_generate()
468        if generator is None:
469            return
470        ModuleInfo.out_dir = Option.gen_dir
471        self.parse_sources(all_idl_details, generator)
472        ModuleInfo.proxy_deps, ModuleInfo.stub_deps, ModuleInfo.header_deps = CodeGen.get_lib_deps(Option.imports)
473
474    def parse_sources(self, all_idl_details, generator):
475        ModuleInfo.include_dirs.add(Option.gen_dir)
476        for idl_detail in all_idl_details.values():
477            ModuleInfo.package = idl_detail.package
478            ModuleInfo.version = idl_detail.version
479            ModuleInfo.include_dirs.add(
480                generator.parse_include_dirs(idl_detail.package))
481
482            sources, proxy_sources, sub_sources = generator.gen_code(
483                idl_detail)
484            ModuleInfo.sources.extend(sources)
485            ModuleInfo.proxy_sources.extend(proxy_sources)
486            ModuleInfo.stub_sources.extend(sub_sources)
487
488
489# generate code file info of hdi
490class CodeGen(object):
491
492    def gen_code(self, idl_detail):
493        idl_detail
494        return [], [], []
495
496    # package is 'ohos.hdi.foo.v1_0'
497    # -r ohos.hdi:./interface
498    # sub_package is foo.v1_0
499    @staticmethod
500    def get_sub_package(package):
501        if package.startswith(Option.root_package):
502            root_package_len = len(Option.root_package)
503            return package[root_package_len + 1:]
504        return package
505
506    @staticmethod
507    def get_package_path(package):
508        package_path = ""
509        if package.startswith(Option.root_package):
510            root_package_len = len(Option.root_package)
511            sub_package = package[root_package_len:]
512            sub_package_path = sub_package.replace(".", os.sep)
513            package_path = "{}{}".format(Option.root_path, sub_package_path)
514        else:
515            raise Exception("find root package '{}' failed in '{}'".format(
516                Option.root_package, package))
517
518        return package_path
519
520    @staticmethod
521    def get_version(package):
522        major_version = 0
523        minor_version = 0
524        result = re.findall(r'\w+(?:\.\w+)*\.[V|v](\d+)_(\d+)', package)
525        if len(result) > 0:
526            major_version = result[0][0]
527            minor_version = result[0][1]
528        return major_version, minor_version
529
530    # transalte package name to include directory
531    @staticmethod
532    def parse_include_dirs(package):
533        sub_package = CodeGen.get_sub_package(package)
534        last_point_index = sub_package.rfind('.')
535        package_without_version = sub_package[:last_point_index]
536        package_dir_without_version = package_without_version.replace(
537            '.', os.sep)
538        return os.path.join(Option.gen_dir, package_dir_without_version)
539
540    # translate package name to directory
541    @staticmethod
542    def get_source_file_dir(package):
543        sub_package = CodeGen.get_sub_package(package)
544        sub_package_dir = "{}{}".format(sub_package.replace('.', os.sep),
545                                        os.sep)
546        return os.path.join(Option.gen_dir, sub_package_dir)
547
548    # translate idl file name to c/c++ file name
549    @staticmethod
550    def translate_file_name(file_name):
551        under_line = '_'
552        result = []
553        name_len = len(file_name)
554        for index in range(name_len):
555            cur_char = file_name[index]
556            if cur_char.isupper():
557                if index > 1:
558                    result.append(under_line)
559                result.append(cur_char.lower())
560            else:
561                result.append(cur_char)
562        return "".join(result)
563
564    @staticmethod
565    def translate_proxy_name(base_name):
566        temp_name = "{}Proxy".format(base_name)
567        return CodeGen.translate_file_name(temp_name)
568
569    @staticmethod
570    def translate_stub_name(base_name):
571        temp_name = "{}Stub".format(base_name)
572        return CodeGen.translate_file_name(temp_name)
573
574    @staticmethod
575    def translate_service_name(base_name):
576        temp_name = "{}Service".format(base_name)
577        return CodeGen.translate_file_name(temp_name)
578
579    @staticmethod
580    def translate_driver_name(base_name):
581        temp_name = "{}Driver".format(base_name)
582        return CodeGen.translate_file_name(temp_name)
583
584    @staticmethod
585    def get_type_names(name):
586        base_name = CodeGen.translate_file_name(name)
587        return base_name
588
589    # translate idl file name to c/c++ file name
590    # for example, IFoo -> ifoo, foo_proxy, foo_stub, foo_service, foo_driver, foo
591    @staticmethod
592    def get_file_names(idl_detail):
593        interface_name = ""
594        proxy_name = ""
595        stub_name = ""
596        service_name = ""
597        driver_name = ""
598        types_name = ""
599
600        if idl_detail.idl_type == IdlType.TYPES:
601            types_name = CodeGen.get_type_names(idl_detail.name)
602            return interface_name, proxy_name, stub_name, service_name, driver_name, types_name
603
604        base_name = idl_detail.name[1:] if idl_detail.name.startswith(
605            "I") else idl_detail.name
606        interface_name = CodeGen.translate_file_name(idl_detail.name)
607        proxy_name = CodeGen.translate_proxy_name(base_name)
608        stub_name = CodeGen.translate_stub_name(base_name)
609        service_name = CodeGen.translate_service_name(base_name)
610        driver_name = CodeGen.translate_driver_name(base_name)
611        if idl_detail.idl_type == IdlType.INTERFACE:
612            driver_name = CodeGen.translate_driver_name(base_name)
613        return interface_name, proxy_name, stub_name, service_name, driver_name, types_name
614
615    @staticmethod
616    def header_file(file_dir, name):
617        return os.path.join(file_dir, "{}.h".format(name))
618
619    @staticmethod
620    def c_source_file(file_dir, name):
621        return os.path.join(file_dir, "{}.c".format(name))
622
623    @staticmethod
624    def cpp_source_file(file_dir, name):
625        return os.path.join(file_dir, "{}.cpp".format(name))
626
627    @staticmethod
628    def get_import(imp):
629        package = ""
630        module_name = ""
631        imp_result = imp.split(":")
632        if len(imp_result) == 2:
633            package = imp_result[0]
634            module_name = imp_result[1]
635        return package, module_name
636
637    # get lib deps from imports
638    @staticmethod
639    def get_lib_deps(imports):
640        proxy_deps = []
641        stub_deps = []
642        header_deps = []
643        for imps in imports:
644            package, module_name = CodeGen.get_import(imps)
645            package_path = CodeGen.get_package_path(package)
646            major_version, minor_version = CodeGen.get_version(package)
647            proxy_lib_name = "lib{}_proxy_{}.{}".format(module_name, major_version, minor_version)
648            stub_lib_name = "lib{}_stub_{}.{}".format(module_name, major_version, minor_version)
649            header_config = "{}_idl_headers_{}.{}".format(module_name, major_version, minor_version)
650            proxy_lib_dep = "{}:{}".format(package_path, proxy_lib_name)
651            stub_lib_dep = "{}:{}".format(package_path, stub_lib_name)
652            header_dep = "{}:{}".format(package_path, header_config)
653            proxy_deps.append(proxy_lib_dep)
654            stub_deps.append(stub_lib_dep)
655            header_deps.append(header_dep)
656        return proxy_deps, stub_deps, header_deps
657
658
659class LowCCodeGen(CodeGen):
660    def gen_code(self, idl_detail):
661        file_dir = self.get_source_file_dir(idl_detail.package)
662        sources = []
663        interface_name, _, _, service_name, driver_name, types_name = self.get_file_names(idl_detail)
664        if idl_detail.idl_type == IdlType.TYPES:
665            header_file = self.header_file(file_dir, types_name)
666            sources.append(header_file)
667        if idl_detail.idl_type == IdlType.INTERFACE:
668            header_file = self.header_file(file_dir, interface_name)
669            service_header_file = self.header_file(file_dir, service_name)
670            service_source_file = self.c_source_file(file_dir, service_name)
671            driver_source_file = self.c_source_file(file_dir, driver_name)
672            sources.extend([header_file, service_header_file, service_source_file, driver_source_file])
673        return sources, [], []
674
675
676class LowCppCodeGen(CodeGen):
677    def gen_code(self, idl_detail):
678        file_dir = self.get_source_file_dir(idl_detail.package)
679        sources = []
680        interface_name, _, _, service_name, driver_name, types_name = self.get_file_names(idl_detail)
681        if idl_detail.idl_type == IdlType.TYPES:
682            header_file = self.header_file(file_dir, types_name)
683            sources.append(header_file)
684        if idl_detail.idl_type == IdlType.INTERFACE:
685            header_file = self.header_file(file_dir, interface_name)
686            service_header_file = self.header_file(file_dir, service_name)
687            service_source_file = self.cpp_source_file(file_dir, service_name)
688            driver_source_file = self.cpp_source_file(file_dir, driver_name)
689            sources.extend([header_file, service_header_file, service_source_file, driver_source_file])
690        return sources, [], []
691
692
693# generate kernel c code file info of hdi
694class KernelCodeGen(CodeGen):
695    def gen_code(self, idl_detail):
696        file_dir = self.get_source_file_dir(idl_detail.package)
697        sources = []
698        proxy_sources = []
699        stub_sources = []
700        interface_name, proxy_name, stub_name, service_name, driver_name, types_name = self.get_file_names(
701            idl_detail)
702        if idl_detail.idl_type == IdlType.TYPES:
703            header_file = self.header_file(file_dir, types_name)
704            source_file = self.c_source_file(file_dir, types_name)
705            sources.extend([header_file, source_file])
706            proxy_sources.append(source_file)
707            stub_sources.append(source_file)
708            return sources, proxy_sources, stub_sources
709
710        if idl_detail.idl_type == IdlType.INTERFACE:
711            iface_header_file = self.header_file(file_dir, interface_name)
712            proxy_source_file = self.c_source_file(file_dir, proxy_name)
713            stub_header_file = self.header_file(file_dir, stub_name)
714            stub_source_file = self.c_source_file(file_dir, stub_name)
715            service_header_file = self.header_file(file_dir, service_name)
716            service_source_file = self.c_source_file(file_dir, service_name)
717            driver_source_file = self.c_source_file(file_dir, driver_name)
718            sources.extend([
719                iface_header_file, proxy_source_file, stub_header_file,
720                stub_source_file, service_header_file, service_source_file,
721                driver_source_file
722            ])
723            proxy_sources.append(proxy_source_file)
724            stub_sources.append(stub_source_file)
725        return sources, proxy_sources, stub_sources
726
727
728# generate passthrough c code file info of hdi
729class PassthroughCCodeGen(CodeGen):
730
731    def gen_code(self, idl_detail):
732        file_dir = self.get_source_file_dir(idl_detail.package)
733        sources = []
734        proxy_sources = []
735        stub_sources = []
736        interface_name, proxy_name, _, service_name, _, types_name = self.get_file_names(
737            idl_detail)
738
739        iface_header_file = self.header_file(file_dir, interface_name)
740        proxy_source_file = self.c_source_file(file_dir, proxy_name)
741        service_header_file = self.header_file(file_dir, service_name)
742        service_source_file = self.c_source_file(file_dir, service_name)
743        types_header_file = self.header_file(file_dir, types_name)
744
745        if idl_detail.idl_type == IdlType.INTERFACE:
746            sources.extend(
747                [iface_header_file, proxy_source_file, service_source_file])
748            proxy_sources.append(proxy_source_file)
749        elif idl_detail.idl_type == IdlType.CALL_INTERFACE:
750            sources.extend(
751                [iface_header_file, service_header_file, service_source_file])
752        elif idl_detail.idl_type == IdlType.CALLBACK:
753            sources.extend(
754                [iface_header_file, service_header_file, service_source_file])
755        else:
756            sources.append(types_header_file)
757        return sources, proxy_sources, stub_sources
758
759
760# generate passthrough cpp code file info of hdi
761class PassthroughCppCodeGen(CodeGen):
762
763    def gen_code(self, idl_detail):
764        file_dir = self.get_source_file_dir(idl_detail.package)
765        sources = []
766        proxy_sources = []
767        stub_sources = []
768        interface_name, proxy_name, _, service_name, _, types_name = self.get_file_names(
769            idl_detail)
770        iface_header_file = self.header_file(file_dir, interface_name)
771        proxy_source_file = self.cpp_source_file(file_dir, proxy_name)
772        service_header_file = self.header_file(file_dir, service_name)
773        service_source_file = self.cpp_source_file(file_dir, service_name)
774        types_header_file = self.header_file(file_dir, types_name)
775
776        if idl_detail.idl_type == IdlType.INTERFACE:
777            sources.extend([
778                iface_header_file, proxy_source_file, service_header_file,
779                service_source_file
780            ])
781            proxy_sources.append(proxy_source_file)
782        elif idl_detail.idl_type == IdlType.CALL_INTERFACE:
783            sources.extend(
784                [iface_header_file, service_header_file, service_source_file])
785        elif idl_detail.idl_type == IdlType.CALLBACK:
786            sources.extend(
787                [iface_header_file, service_header_file, service_source_file])
788        else:
789            sources.append(types_header_file)
790        return sources, proxy_sources, stub_sources
791
792
793# generate ipc c code file information of hdi
794class IpcCCodeGen(CodeGen):
795
796    def gen_code(self, idl_detail):
797        file_dir = self.get_source_file_dir(idl_detail.package)
798        sources = []
799        proxy_sources = []
800        stub_sources = []
801        interface_name, proxy_name, stub_name, service_name, driver_name, types_name = self.get_file_names(
802            idl_detail)
803        iface_header_file = self.header_file(file_dir, interface_name)
804        proxy_source_file = self.c_source_file(file_dir, proxy_name)
805        stub_header_file = self.header_file(file_dir, stub_name)
806        stub_source_file = self.c_source_file(file_dir, stub_name)
807        service_header_file = self.header_file(file_dir, service_name)
808        service_source_file = self.c_source_file(file_dir, service_name)
809        driver_source_file = self.c_source_file(file_dir, driver_name)
810        types_header_file = self.header_file(file_dir, types_name)
811        types_source_file = self.c_source_file(file_dir, types_name)
812
813        if idl_detail.idl_type == IdlType.INTERFACE:
814            sources.extend([
815                iface_header_file, proxy_source_file, stub_header_file,
816                stub_source_file, service_source_file, driver_source_file
817            ])
818            proxy_sources.append(proxy_source_file)
819            stub_sources.append(stub_source_file)
820        elif idl_detail.idl_type == IdlType.CALL_INTERFACE:
821            sources.extend([
822                iface_header_file, proxy_source_file, stub_header_file,
823                stub_source_file, service_header_file, service_source_file
824            ])
825            proxy_sources.append(proxy_source_file)
826            stub_sources.append(stub_source_file)
827        elif idl_detail.idl_type == IdlType.CALLBACK:
828            sources.extend([
829                iface_header_file, proxy_source_file, stub_header_file,
830                stub_source_file, service_header_file, service_source_file
831            ])
832            proxy_sources.append(stub_source_file)
833            stub_sources.append(proxy_source_file)
834        else:
835            sources.extend([types_header_file, types_source_file])
836            proxy_sources.append(types_source_file)
837            stub_sources.append(types_source_file)
838        return sources, proxy_sources, stub_sources
839
840
841# generate ipc cpp code file information of hdi
842class IpcCppCodeGen(CodeGen):
843
844    def gen_code(self, idl_detail):
845        file_dir = self.get_source_file_dir(idl_detail.package)
846        sources = []
847        proxy_sources = []
848        stub_sources = []
849        interface_name, proxy_name, stub_name, service_name, driver_name, types_name = self.get_file_names(
850            idl_detail)
851        iface_header_file = self.header_file(file_dir, interface_name)
852        proxy_header_file = self.header_file(file_dir, proxy_name)
853        proxy_source_file = self.cpp_source_file(file_dir, proxy_name)
854        stub_header_file = self.header_file(file_dir, stub_name)
855        stub_source_file = self.cpp_source_file(file_dir, stub_name)
856        service_header_file = self.header_file(file_dir, service_name)
857        service_source_file = self.cpp_source_file(file_dir, service_name)
858        driver_source_file = self.cpp_source_file(file_dir, driver_name)
859        types_header_file = self.header_file(file_dir, types_name)
860        types_source_file = self.cpp_source_file(file_dir, types_name)
861
862        if idl_detail.idl_type == IdlType.INTERFACE:
863            sources.extend([
864                iface_header_file, proxy_header_file, proxy_source_file,
865                stub_header_file, stub_source_file, service_header_file,
866                service_source_file, driver_source_file
867            ])
868            proxy_sources.append(proxy_source_file)
869            stub_sources.append(stub_source_file)
870        elif idl_detail.idl_type == IdlType.CALL_INTERFACE:
871            sources.extend([
872                iface_header_file, proxy_header_file, proxy_source_file,
873                stub_header_file, stub_source_file, service_header_file,
874                service_source_file
875            ])
876            proxy_sources.append(proxy_source_file)
877            stub_sources.append(stub_source_file)
878        elif idl_detail.idl_type == IdlType.CALLBACK:
879            sources.extend([
880                iface_header_file, proxy_header_file, proxy_source_file,
881                stub_header_file, stub_source_file, service_header_file,
882                service_source_file
883            ])
884            proxy_sources.append(stub_source_file)
885            stub_sources.append(proxy_source_file)
886        else:
887            sources.extend([types_header_file, types_source_file])
888            proxy_sources.append(types_source_file)
889            stub_sources.append(types_source_file)
890        return sources, proxy_sources, stub_sources
891
892
893class CodeGenFactory(object):
894    action_config = {
895        "mini": {
896            "low": {
897                "c": LowCCodeGen(),
898                "cpp": LowCppCodeGen()
899            }
900        },
901        "lite": {
902            "kernel": {
903                "c": KernelCodeGen()
904            },
905            "passthrough": {
906                "c": PassthroughCCodeGen(),
907                "cpp": PassthroughCppCodeGen()
908            }
909        },
910        "full": {
911            "kernel": {
912                "c": KernelCodeGen()
913            },
914            "passthrough": {
915                "c": PassthroughCCodeGen(),
916                "cpp": PassthroughCppCodeGen()
917            },
918            "ipc": {
919                "c": IpcCCodeGen(),
920                "cpp": IpcCppCodeGen()
921            }
922        }
923    }
924
925    @staticmethod
926    def create_code_generate():
927        if Option.system not in CodeGenFactory.action_config:
928            raise Exception("the '{}' system is not supported".format(
929                Option.system))
930        system_action = CodeGenFactory.action_config.get(
931            Option.system)
932        if Option.mode not in system_action:
933            raise Exception(
934                "the '{}' mode is not supported by '{}' system".format(
935                    Option.mode, Option.system))
936        mode_action = system_action.get(Option.mode)
937        if Option.language not in mode_action:
938            raise Exception(
939                "the '{}' language is not support by '{}' mode of '{}' system"
940                .format(Option.language, Option.mode, Option.system))
941        return mode_action.get(Option.language)
942
943
944def check_python_version():
945    if sys.version_info < (3, 0):
946        raise Exception("Please run with python version >= 3.0")
947
948
949if __name__ == "__main__":
950    check_python_version()
951    option_parser = argparse.ArgumentParser(
952        description="Tools for generating compilation infomation of idl files",
953    )
954
955    option_parser.add_argument("-s",
956                               "--system",
957                               choices=["mini", "lite", "full"],
958                               default="full",
959                               help="system: mini, lite, full")
960
961    option_parser.add_argument(
962        "-m",
963        "--mode",
964        choices=["ipc", "passthrough", "kernel", "low"],
965        default="ipc",
966        help="generate code of ipc, passthrough or kernel mode")
967
968    option_parser.add_argument("-l",
969                               "--language",
970                               required=True,
971                               choices=["c", "cpp"],
972                               help="language of generating code")
973
974    option_parser.add_argument("-o",
975                               "--out",
976                               required=True,
977                               default=".",
978                               help="direstory of generate file")
979
980    option_parser.add_argument("-r",
981                               "--root",
982                               required=True,
983                               help="mapping path: <root package>:<path>")
984
985    option_parser.add_argument("-f",
986                               "--file",
987                               required=True,
988                               action="append",
989                               help="the idl file")
990
991    option_parser.add_argument("--imports",
992                               action="append",
993                               help="the imports")
994
995    Option.load(option_parser.parse_args())
996    idl_parser = IdlParser()
997    idl_parser.parse()
998    sys.stdout.write(ModuleInfo.json_info())
999    sys.stdout.flush()
1000