3 # Long-running server process uses stdin & stdout to communicate JSON
6 from __future__ import print_function
13 from confgen import FatalError, __version__
16 parser = argparse.ArgumentParser(description='confserver.py v%s - Config Generation Tool' % __version__, prog=os.path.basename(sys.argv[0]))
18 parser.add_argument('--config',
19 help='Project configuration settings',
22 parser.add_argument('--kconfig',
23 help='KConfig file with config item definitions',
26 parser.add_argument('--env', action='append', default=[],
27 help='Environment to set when evaluating the config file', metavar='NAME=VAL')
29 args = parser.parse_args()
32 args.env = [ (name,value) for (name,value) in ( e.split("=",1) for e in args.env) ]
34 print("--env arguments must each contain =. To unset an environment variable, use 'ENV='")
37 for name, value in args.env:
38 os.environ[name] = value
40 print("Server running, waiting for requests on stdin...", file=sys.stderr)
41 run_server(args.kconfig, args.config)
44 def run_server(kconfig, sdkconfig):
45 config = kconfiglib.Kconfig(kconfig)
46 config.load_config(sdkconfig)
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)
54 line = sys.stdin.readline()
57 req = json.loads(line)
58 before = confgen.get_json_values(config)
59 before_ranges = get_ranges(config)
61 if "load" in req: # if we're loading a different sdkconfig, response should have all items in it
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
69 sdkconfig = req["load"]
72 if req["save"] is None:
73 req["save"] = sdkconfig
75 sdkconfig = req["save"]
77 error = handle_request(config, req)
79 after = confgen.get_json_values(config)
80 after_ranges = get_ranges(config)
82 values_diff = diff(before, after)
83 ranges_diff = diff(before_ranges, after_ranges)
84 response = {"version" : 1, "values" : values_diff, "ranges" : ranges_diff}
87 print("Error: %s" % e, file=sys.stderr)
88 response["error"] = error
89 json.dump(response, sys.stdout)
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" ]
102 print("Loading config from %s..." % req["load"], file=sys.stderr)
104 config.load_config(req["load"])
105 except Exception as e:
106 error += [ "Failed to load from %s: %s" % (req["load"], e) ]
109 handle_set(config, error, req["set"])
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) ]
120 def handle_set(config, error, to_set):
121 missing = [ k for k in to_set if not k in config.syms ]
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)
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
133 set_pass = [ (k,v) for (k,v) in to_set.items() if k.visibility ]
135 break # no visible keys left
136 for (sym,val) in set_pass:
137 if sym.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
143 error.append("Boolean symbol %s only accepts true/false values" % sym.name)
145 sym.set_value(str(val))
146 print("Set %s" % sym.name)
150 error.append("The following config symbol(s) were not visible so were not updated: %s" % (", ".join(s.name for s in to_set)))
154 def diff(before, after):
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'
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)
165 def get_ranges(config):
167 def handle_node(node):
169 if not isinstance(sym, kconfiglib.Symbol):
171 active_range = sym.active_range
172 if active_range[0] is not None:
173 ranges_dict[sym.name] = active_range
175 config.walk_menu(handle_node)
179 if __name__ == '__main__':
182 except FatalError as e:
183 print("A fatal error occurred: %s" % e, file=sys.stderr)