]> granicus.if.org Git - esp-idf/blob - tools/kconfig_new/confserver.py
Merge branch 'bugfix/use_component_srcs' into 'master'
[esp-idf] / tools / kconfig_new / confserver.py
1 #!/usr/bin/env python
2 #
3 # Long-running server process uses stdin & stdout to communicate JSON
4 # with a caller
5 #
6 from __future__ import print_function
7 import argparse
8 import json
9 import kconfiglib
10 import os
11 import sys
12 import confgen
13 from confgen import FatalError, __version__
14
15 def main():
16     parser = argparse.ArgumentParser(description='confserver.py v%s - Config Generation Tool' % __version__, prog=os.path.basename(sys.argv[0]))
17
18     parser.add_argument('--config',
19                        help='Project configuration settings',
20                        required=True)
21
22     parser.add_argument('--kconfig',
23                         help='KConfig file with config item definitions',
24                         required=True)
25
26     parser.add_argument('--env', action='append', default=[],
27                         help='Environment to set when evaluating the config file', metavar='NAME=VAL')
28
29     args = parser.parse_args()
30
31     try:
32        args.env = [ (name,value) for (name,value) in ( e.split("=",1) for e in args.env) ]
33     except ValueError:
34        print("--env arguments must each contain =. To unset an environment variable, use 'ENV='")
35        sys.exit(1)
36
37     for name, value in args.env:
38         os.environ[name] = value
39
40     print("Server running, waiting for requests on stdin...", file=sys.stderr)
41     run_server(args.kconfig, args.config)
42
43
44 def run_server(kconfig, sdkconfig):
45     config = kconfiglib.Kconfig(kconfig)
46     config.load_config(sdkconfig)
47
48     config_dict = confgen.get_json_values(config)
49     ranges_dict = get_ranges(config)
50     json.dump({"version": 1, "values" : config_dict, "ranges" : ranges_dict}, sys.stdout)
51     print("\n")
52
53     while True:
54         line = sys.stdin.readline()
55         if not line:
56             break
57         req = json.loads(line)
58         before = confgen.get_json_values(config)
59         before_ranges = get_ranges(config)
60
61         if "load" in req:  # if we're loading a different sdkconfig, response should have all items in it
62             before = {}
63             before_ranges = {}
64
65             # if no new filename is supplied, use existing sdkconfig path, otherwise update the path
66             if req["load"] is None:
67                 req["load"] = sdkconfig
68             else:
69                 sdkconfig = req["load"]
70
71         if "save" in req:
72             if req["save"] is None:
73                 req["save"] = sdkconfig
74             else:
75                 sdkconfig = req["save"]
76
77         error = handle_request(config, req)
78
79         after = confgen.get_json_values(config)
80         after_ranges = get_ranges(config)
81
82         values_diff = diff(before, after)
83         ranges_diff = diff(before_ranges, after_ranges)
84         response = {"version" : 1, "values" : values_diff, "ranges" : ranges_diff}
85         if error:
86             for e in error:
87                 print("Error: %s" % e, file=sys.stderr)
88             response["error"] = error
89         json.dump(response, sys.stdout)
90         print("\n")
91
92
93 def handle_request(config, req):
94     if not "version" in req:
95         return [ "All requests must have a 'version'" ]
96     if int(req["version"]) != 1:
97         return [ "Only version 1 requests supported" ]
98
99     error = []
100
101     if "load" in req:
102         print("Loading config from %s..." % req["load"], file=sys.stderr)
103         try:
104             config.load_config(req["load"])
105         except Exception as e:
106             error += [ "Failed to load from %s: %s" % (req["load"], e) ]
107
108     if "set" in req:
109         handle_set(config, error, req["set"])
110
111     if "save" in req:
112         try:
113             print("Saving config to %s..." % req["save"], file=sys.stderr)
114             confgen.write_config(config, req["save"])
115         except Exception as e:
116             error += [ "Failed to save to %s: %s" % (req["save"], e) ]
117
118     return error
119
120 def handle_set(config, error, to_set):
121     missing = [ k for k in to_set if not k in config.syms ]
122     if missing:
123         error.append("The following config symbol(s) were not found: %s" % (", ".join(missing)))
124     # replace name keys with the full config symbol for each key:
125     to_set = dict((config.syms[k],v) for (k,v) in to_set.items() if not k in missing)
126
127     # Work through the list of values to set, noting that
128     # some may not be immediately applicable (maybe they depend
129     # on another value which is being set). Therefore, defer
130     # knowing if any value is unsettable until then end
131
132     while len(to_set):
133         set_pass = [ (k,v) for (k,v) in to_set.items() if k.visibility ]
134         if not set_pass:
135             break  # no visible keys left
136         for (sym,val) in set_pass:
137             if sym.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
138                 if val == True:
139                     sym.set_value(2)
140                 elif val == False:
141                     sym.set_value(0)
142                 else:
143                     error.append("Boolean symbol %s only accepts true/false values" % sym.name)
144             else:
145                 sym.set_value(str(val))
146             print("Set %s" % sym.name)
147             del to_set[sym]
148
149     if len(to_set):
150         error.append("The following config symbol(s) were not visible so were not updated: %s" % (", ".join(s.name for s in to_set)))
151
152
153
154 def diff(before, after):
155     """
156     Return a dictionary with the difference between 'before' and 'after' (either with the new value if changed,
157     or None as the value if a key in 'before' is missing in 'after'
158     """
159     diff = dict((k,v) for (k,v) in after.items() if before.get(k, None) != v)
160     hidden = dict((k,None) for k in before if k not in after)
161     diff.update(hidden)
162     return diff
163
164
165 def get_ranges(config):
166     ranges_dict = {}
167     def handle_node(node):
168         sym = node.item
169         if not isinstance(sym, kconfiglib.Symbol):
170             return
171         active_range = sym.active_range
172         if active_range[0] is not None:
173             ranges_dict[sym.name] = active_range
174
175     config.walk_menu(handle_node)
176     return ranges_dict
177
178
179 if __name__ == '__main__':
180     try:
181         main()
182     except FatalError as e:
183         print("A fatal error occurred: %s" % e, file=sys.stderr)
184         sys.exit(2)
185