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