|
|
@@ -0,0 +1,185 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+#
|
|
|
+# Long-running server process uses stdin & stdout to communicate JSON
|
|
|
+# with a caller
|
|
|
+#
|
|
|
+from __future__ import print_function
|
|
|
+import argparse
|
|
|
+import json
|
|
|
+import kconfiglib
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import confgen
|
|
|
+from confgen import FatalError, __version__
|
|
|
+
|
|
|
+def main():
|
|
|
+ parser = argparse.ArgumentParser(description='confserver.py v%s - Config Generation Tool' % __version__, prog=os.path.basename(sys.argv[0]))
|
|
|
+
|
|
|
+ parser.add_argument('--config',
|
|
|
+ help='Project configuration settings',
|
|
|
+ required=True)
|
|
|
+
|
|
|
+ parser.add_argument('--kconfig',
|
|
|
+ help='KConfig file with config item definitions',
|
|
|
+ required=True)
|
|
|
+
|
|
|
+ parser.add_argument('--env', action='append', default=[],
|
|
|
+ help='Environment to set when evaluating the config file', metavar='NAME=VAL')
|
|
|
+
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ try:
|
|
|
+ args.env = [ (name,value) for (name,value) in ( e.split("=",1) for e in args.env) ]
|
|
|
+ except ValueError:
|
|
|
+ print("--env arguments must each contain =. To unset an environment variable, use 'ENV='")
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ for name, value in args.env:
|
|
|
+ os.environ[name] = value
|
|
|
+
|
|
|
+ print("Server running, waiting for requests on stdin...", file=sys.stderr)
|
|
|
+ run_server(args.kconfig, args.config)
|
|
|
+
|
|
|
+
|
|
|
+def run_server(kconfig, sdkconfig):
|
|
|
+ config = kconfiglib.Kconfig(kconfig)
|
|
|
+ config.load_config(sdkconfig)
|
|
|
+
|
|
|
+ config_dict = confgen.get_json_values(config)
|
|
|
+ ranges_dict = get_ranges(config)
|
|
|
+ json.dump({"version": 1, "values" : config_dict, "ranges" : ranges_dict}, sys.stdout)
|
|
|
+ print("\n")
|
|
|
+
|
|
|
+ while True:
|
|
|
+ line = sys.stdin.readline()
|
|
|
+ if not line:
|
|
|
+ break
|
|
|
+ req = json.loads(line)
|
|
|
+ before = confgen.get_json_values(config)
|
|
|
+ before_ranges = get_ranges(config)
|
|
|
+
|
|
|
+ if "load" in req: # if we're loading a different sdkconfig, response should have all items in it
|
|
|
+ before = {}
|
|
|
+ before_ranges = {}
|
|
|
+
|
|
|
+ # if no new filename is supplied, use existing sdkconfig path, otherwise update the path
|
|
|
+ if req["load"] is None:
|
|
|
+ req["load"] = sdkconfig
|
|
|
+ else:
|
|
|
+ sdkconfig = req["load"]
|
|
|
+
|
|
|
+ if "save" in req:
|
|
|
+ if req["save"] is None:
|
|
|
+ req["save"] = sdkconfig
|
|
|
+ else:
|
|
|
+ sdkconfig = req["save"]
|
|
|
+
|
|
|
+ error = handle_request(config, req)
|
|
|
+
|
|
|
+ after = confgen.get_json_values(config)
|
|
|
+ after_ranges = get_ranges(config)
|
|
|
+
|
|
|
+ values_diff = diff(before, after)
|
|
|
+ ranges_diff = diff(before_ranges, after_ranges)
|
|
|
+ response = {"version" : 1, "values" : values_diff, "ranges" : ranges_diff}
|
|
|
+ if error:
|
|
|
+ for e in error:
|
|
|
+ print("Error: %s" % e, file=sys.stderr)
|
|
|
+ response["error"] = error
|
|
|
+ json.dump(response, sys.stdout)
|
|
|
+ print("\n")
|
|
|
+
|
|
|
+
|
|
|
+def handle_request(config, req):
|
|
|
+ if not "version" in req:
|
|
|
+ return [ "All requests must have a 'version'" ]
|
|
|
+ if int(req["version"]) != 1:
|
|
|
+ return [ "Only version 1 requests supported" ]
|
|
|
+
|
|
|
+ error = []
|
|
|
+
|
|
|
+ if "load" in req:
|
|
|
+ print("Loading config from %s..." % req["load"], file=sys.stderr)
|
|
|
+ try:
|
|
|
+ config.load_config(req["load"])
|
|
|
+ except Exception as e:
|
|
|
+ error += [ "Failed to load from %s: %s" % (req["load"], e) ]
|
|
|
+
|
|
|
+ if "set" in req:
|
|
|
+ handle_set(config, error, req["set"])
|
|
|
+
|
|
|
+ if "save" in req:
|
|
|
+ try:
|
|
|
+ print("Saving config to %s..." % req["save"], file=sys.stderr)
|
|
|
+ confgen.write_config(config, req["save"])
|
|
|
+ except Exception as e:
|
|
|
+ error += [ "Failed to save to %s: %s" % (req["save"], e) ]
|
|
|
+
|
|
|
+ return error
|
|
|
+
|
|
|
+def handle_set(config, error, to_set):
|
|
|
+ missing = [ k for k in to_set if not k in config.syms ]
|
|
|
+ if missing:
|
|
|
+ error.append("The following config symbol(s) were not found: %s" % (", ".join(missing)))
|
|
|
+ # replace name keys with the full config symbol for each key:
|
|
|
+ to_set = dict((config.syms[k],v) for (k,v) in to_set.items() if not k in missing)
|
|
|
+
|
|
|
+ # Work through the list of values to set, noting that
|
|
|
+ # some may not be immediately applicable (maybe they depend
|
|
|
+ # on another value which is being set). Therefore, defer
|
|
|
+ # knowing if any value is unsettable until then end
|
|
|
+
|
|
|
+ while len(to_set):
|
|
|
+ set_pass = [ (k,v) for (k,v) in to_set.items() if k.visibility ]
|
|
|
+ if not set_pass:
|
|
|
+ break # no visible keys left
|
|
|
+ for (sym,val) in set_pass:
|
|
|
+ if sym.type in (kconfiglib.BOOL, kconfiglib.TRISTATE):
|
|
|
+ if val == True:
|
|
|
+ sym.set_value(2)
|
|
|
+ elif val == False:
|
|
|
+ sym.set_value(0)
|
|
|
+ else:
|
|
|
+ error.append("Boolean symbol %s only accepts true/false values" % sym.name)
|
|
|
+ else:
|
|
|
+ sym.set_value(str(val))
|
|
|
+ print("Set %s" % sym.name)
|
|
|
+ del to_set[sym]
|
|
|
+
|
|
|
+ if len(to_set):
|
|
|
+ error.append("The following config symbol(s) were not visible so were not updated: %s" % (", ".join(s.name for s in to_set)))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+def diff(before, after):
|
|
|
+ """
|
|
|
+ Return a dictionary with the difference between 'before' and 'after' (either with the new value if changed,
|
|
|
+ or None as the value if a key in 'before' is missing in 'after'
|
|
|
+ """
|
|
|
+ diff = dict((k,v) for (k,v) in after.items() if before.get(k, None) != v)
|
|
|
+ hidden = dict((k,None) for k in before if k not in after)
|
|
|
+ diff.update(hidden)
|
|
|
+ return diff
|
|
|
+
|
|
|
+
|
|
|
+def get_ranges(config):
|
|
|
+ ranges_dict = {}
|
|
|
+ def handle_node(node):
|
|
|
+ sym = node.item
|
|
|
+ if not isinstance(sym, kconfiglib.Symbol):
|
|
|
+ return
|
|
|
+ active_range = sym.active_range
|
|
|
+ if active_range[0] is not None:
|
|
|
+ ranges_dict[sym.name] = active_range
|
|
|
+
|
|
|
+ config.walk_menu(handle_node)
|
|
|
+ return ranges_dict
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ try:
|
|
|
+ main()
|
|
|
+ except FatalError as e:
|
|
|
+ print("A fatal error occurred: %s" % e, file=sys.stderr)
|
|
|
+ sys.exit(2)
|
|
|
+
|