• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3#   Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
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
16"""
17* Description: Settings parsers.
18* Create: 2020-1-2
19"""
20
21import os
22import json
23
24from build_utils import color_red
25from build_utils import color_end
26
27__all__ = ["MconfParser", "BuildConfParser"]
28
29def nv_repeat_check(pairs):
30    key_list = []
31    for key_temp in pairs:
32        if key_temp[0] not in key_list:
33            key_list.append(key_temp[0])
34        else:
35            raise Exception("nv items(%s) repeat"%key_temp[0])
36    pairs = dict(pairs)
37    return pairs
38
39class ParserError(Exception):
40    """
41    Parse errors, highlight in red.
42    """
43    def __init__(self, err):
44        emsg = "%s%s%s"%(color_red(), err, color_end())
45        Exception.__init__(self, emsg)
46    pass
47
48"""
49Json format config file parser
50"""
51class BuildConfParser:
52    def __init__(self, conf_path):
53        if not os.path.isfile(conf_path):
54            raise ParserError("Configration file %s NOT found!"%conf_path)
55
56        with open(conf_path, 'r', encoding='utf-8') as conf:
57            try:
58                myread = conf.read()
59                self.conf_data = json.loads(myread)
60                self.nvconf_data = json.loads(myread, object_pairs_hook = nv_repeat_check)
61            except Exception as err:
62                msg = "%s\nParsing file:%s"%(err, conf_path)
63                raise ParserError(msg)
64
65        self.conf_data = self._parse(self.conf_data)
66        self.nvconf_data = self._parse(self.nvconf_data)
67
68    def get_conf_data(self):
69        return self.conf_data
70
71    def get_nvconf_data(self):
72        return self.nvconf_data
73
74    def get(self, option):
75        return self.conf_data.get(option)
76
77    def _parse(self, data):
78        """
79        parse the python sentence starts with ###
80        """
81        for key, value in data.items():
82            if isinstance(value, dict):
83                # Recursion
84                value = self._parse(value)
85            if isinstance(value, list):
86                # Recursion
87                data[key] = self._parse_list(value)
88            if isinstance(value, int):
89                data[key] = value
90            if isinstance(value, str) and value.startswith('###'):
91                value = self._exec(value)
92                data[key] = value
93        return data
94
95    def _parse_list(self, values):
96        new_list = []
97        for val in values:
98            if type(val) is str and val.startswith('###'):
99                value = self._exec(val)
100                new_list.append(value)
101            elif isinstance(val, dict):
102                new_list.append(self._parse(val))
103            else:
104                new_list.append(val)
105        return new_list
106
107    def _exec(self, code):
108        """
109        Execute the simple python sentence.
110        For the security reason, only allows 'os.path.join' to be input, as a path string
111        to support multiple platforms.
112        If it needs to support more python features, please use compile and eval, but careful about
113        the security issues.
114        """
115        start = code.find("os.path.join")
116        if start < 0:
117            raise ParserError("The input doesn't support!")
118        lpt = code.find("(")
119        if lpt < 0 or lpt < start:
120            raise ParserError("The input doesn't support!")
121        rpt = code.find(")")
122        if rpt < 0 or rpt < lpt:
123            raise ParserError("The input doesn't support!")
124        path_parts = code[lpt + 1:rpt].split(",")
125        ret = ""
126        for part in path_parts:
127            ret = os.path.join(ret, part.lstrip(" '\"").rstrip(" '\""))
128        return ret
129
130"""
131Menuconfig format config file parser
132"""
133class MconfParser:
134    def __init__(self, conf_path):
135        if not os.path.isfile(conf_path):
136            raise ParserError("Configration file %s NOT found!"%conf_path)
137
138        with open(conf_path, 'r', encoding='utf-8') as conf:
139            self.conf_data = conf.readlines()
140
141        self.conf_data = self._parse(self.conf_data)
142
143    def get(self, option):
144        data = self.conf_data.get(option)
145        if data is None:
146            # Option not found be treated as false.
147            return 'n'
148        # strip " when met string values.
149        return data.replace('"', '')
150
151    def _parse(self, data):
152        settings = {}
153        for option in data:
154            if self._option_is_valid(option) is True:
155                key, value = self._parse_option(option)
156                settings[key] = value.strip().replace('\n', '').replace('\r', '')
157        return settings
158
159    def _option_is_valid(self, option):
160        option = option.strip()
161        if (option is None) or (option == '') or (option.startswith('#') is True):
162            # skip blanks and comments.
163            return False
164        return True
165
166    def _parse_option(self, option):
167        cfg = option.split('=')
168        if len(cfg) == 2:
169            # like "KEY=value", length always be 2. return in KEY, value
170            return cfg[0], cfg[1]
171        else:
172            raise ParserError("Unknow format of the option:%s"%option)
173
174def test():
175    """
176    Test only.
177    """
178    parser = BuildConfParser("build/config/riscv32_toolchain.json")
179    print(parser.get('TargetFolder'))
180    mparser = MconfParser("build/config/settings.json")
181    print(mparser.get('CONFIG_TARGET_SOFT_VER'))
182
183if __name__ == "__main__":
184    test()
185
186