From df68c7ad2e4b570bc28d1fc02da9ba86b5486c6c Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Wed, 24 Aug 2022 22:04:32 +0800 Subject: [PATCH] config_tools: support dynamic enum names in XML schema The dynamic enum mechanism today only allows specifying the enum values using XPATH. While this is sufficient from functionality point of view, it may not provide the best experience as users have to understand the raw data used internally. The typical way to present more informational labels of enum values to users is enum names which cannot be supported by the current XML schema to JSONSchema converter. This patch allows the XML schema to specify dynamic enum names by adding an `acrn:option-names` attribute to an element. The attribute is interpreted as an XPATH which evaluates to a sequence of the same length of `acrn-options`. The element at index i in that sequence is considered the enum name of the enum value at index i of the results of `acrn:options`. This mechanism is first applied to the `pcpu_id` element to indicate whether a physical CPU is P-core or E-core. Tracked-On: #8050 Signed-off-by: Junjie Mao --- .../configurator/pyodide/loadBoard.py | 22 +++++++++++-------- .../scenario_config/jsonschema/converter.py | 17 ++++++++------ misc/config_tools/schema/VMtypes.xsd | 6 ++++- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/misc/config_tools/configurator/pyodide/loadBoard.py b/misc/config_tools/configurator/pyodide/loadBoard.py index 9f925915a..6917bf652 100644 --- a/misc/config_tools/configurator/pyodide/loadBoard.py +++ b/misc/config_tools/configurator/pyodide/loadBoard.py @@ -22,15 +22,16 @@ def get_dynamic_scenario(board): """ board_xml = etree.fromstring(board) - def get_enum(source, options, obj_type): + def get_enum(source, options, option_names, obj_type): elements = [str(x) for x in elementpath.select(source, options) if x] - elements = list(set(elements)) + element_names = [str(x) for x in elementpath.select(source, option_names) if x] + elements = list(set(zip(elements, element_names))) if not elements: - elements = [''] + elements = [('', '')] # TODO: Add more converters if needed enum_type_convert = {'integer': lambda x: int(x) if x else 0} if obj_type in enum_type_convert.keys(): - elements = [enum_type_convert[obj_type](x) for x in elements] + elements = [(enum_type_convert[obj_type](x[0]), x[1]) for x in elements] return elements def dynamic_enum(**enum_setting): @@ -40,13 +41,14 @@ def get_dynamic_scenario(board): for key in ['function', 'source'] ] # value from given - selector, sorted_func, obj_type = [enum_setting[key] for key in ['selector', 'sorted', 'type']] + selector, name_selector, sorted_func, obj_type = [enum_setting[key] for key in ['selector', 'name-selector', 'sorted', 'type']] # get enum data - enum = function(source, selector, obj_type) + enum = function(source, selector, name_selector, obj_type) if sorted_func: - enum = sorted(enum, key=eval(sorted_func)) - return enum + fn = eval(sorted_func) + enum = sorted(enum, key=lambda x: fn(x[0])) + return zip(*enum) def dynamic_enum_apply(obj): # get json schema enum obj @@ -56,7 +58,9 @@ def get_dynamic_scenario(board): if enum_setting['type'] == 'dynamicEnum': enum_setting['type'] = obj.get('type', '') # replace json schema obj enum field data - obj['enum'] = dynamic_enum(**enum_setting) + enum, enum_names = dynamic_enum(**enum_setting) + obj['enum'] = enum + obj['enumNames'] = enum_names return obj data = json.loads(scenario_json_schema, object_hook=dynamic_enum_apply) diff --git a/misc/config_tools/scenario_config/jsonschema/converter.py b/misc/config_tools/scenario_config/jsonschema/converter.py index 47417b82a..1df17530a 100644 --- a/misc/config_tools/scenario_config/jsonschema/converter.py +++ b/misc/config_tools/scenario_config/jsonschema/converter.py @@ -340,11 +340,13 @@ class XS2JS: # get description if 'xs:annotation' in element: + annotation = element['xs:annotation'] + # title - js_ele['title'] = element['xs:annotation'].get('@acrn:title', name) + js_ele['title'] = annotation.get('@acrn:title', name) # documentation - documentation: str = element['xs:annotation'].get('xs:documentation', None) + documentation: str = annotation.get('xs:documentation', None) if documentation is None or documentation.strip() == '': documentation = '' if documentation: @@ -352,13 +354,14 @@ class XS2JS: js_ele['description'] = documentation # dynamic enum - if '@acrn:options' in element['xs:annotation'] and 'dynamicEnum' in self.features: + if '@acrn:options' in annotation and 'dynamicEnum' in self.features: dynamic_enum = { 'type': 'dynamicEnum', 'function': 'get_enum', 'source': 'board_xml', - 'selector': element['xs:annotation']['@acrn:options'], - 'sorted': element['xs:annotation'].get('@acrn:options-sorted-by', None) + 'selector': annotation['@acrn:options'], + 'name-selector': annotation['@acrn:option-names'] if '@acrn:option-names' in annotation else annotation['@acrn:options'], + 'sorted': annotation.get('@acrn:options-sorted-by', None) } # enum should be applied to array items instead of array itself if 'items' in js_ele: @@ -367,10 +370,10 @@ class XS2JS: js_ele['enum'] = dynamic_enum # widget and its options - self.convert_widget_config(element['xs:annotation'], js_ele) + self.convert_widget_config(annotation, js_ele) # Error messages - self.convert_errormsg_config(element['xs:annotation'], js_ele) + self.convert_errormsg_config(annotation, js_ele) properties[name] = js_ele diff --git a/misc/config_tools/schema/VMtypes.xsd b/misc/config_tools/schema/VMtypes.xsd index 89b9dcc71..ce4ef09be 100644 --- a/misc/config_tools/schema/VMtypes.xsd +++ b/misc/config_tools/schema/VMtypes.xsd @@ -46,7 +46,11 @@ + acrn:options="//processors//thread/cpu_id/text()" + acrn:option-names="if (count(distinct-values(//processors//thread/core_type)) > 1) + then (for $thread in //processors//thread return concat($thread/cpu_id, ' (', if ($thread/core_type = 'Core') then 'P-Core' else 'E-Core', ')')) + else //processors//thread/cpu_id/text()" + acrn:options-sorted-by="int"> ID of the pCPU that this VM's vCPU is allowed to pin to.