aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDavid Wagner <david.wagner@intel.com>2015-01-07 18:20:10 +0100
committerDavid Wagner <david.wagner@intel.com>2015-01-28 20:02:43 +0100
commitf01cb8127ce34f758a043b4ef1c12695035bc632 (patch)
tree3ce40d4391951b069b49d1a4ccc3c55de1530b1c /tools
parent03b951fa8ac82daf8fb3f0cee7d47a7baff9f359 (diff)
downloadexternal_parameter-framework-f01cb8127ce34f758a043b4ef1c12695035bc632.zip
external_parameter-framework-f01cb8127ce34f758a043b4ef1c12695035bc632.tar.gz
external_parameter-framework-f01cb8127ce34f758a043b4ef1c12695035bc632.tar.bz2
xmlGenerator: new generator using the python bindings
This new script (domainGenerator.py) uses both the newly-refactored EddParser.py module as well as the new python bindings to generate XML Settings through the parameter-framework interface. This script can take the following data as input: - the toplevel config file (mandatory) - the 'criteria' file (mandatory) - an initial settings file to be amended (optional) - zero, one, or several 'domain' files (each containing a single domain) - zero, one, or several files in the EDD syntax (aka ".pfw" files) Call the script with the "--help" argument for detailed help. It implements its own derived class of PfwBaseTranslator, calling the parameter-framework python bindings. Any error happening within the binding or below (e.g. the pfw core) is collected and forwarded to the user. Change-Id: Id11123e083ed2a487800e737007a99541949a4bb Signed-off-by: David Wagner <david.wagner@intel.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/xmlGenerator/Android.mk13
-rwxr-xr-xtools/xmlGenerator/domainGenerator.py313
2 files changed, 326 insertions, 0 deletions
diff --git a/tools/xmlGenerator/Android.mk b/tools/xmlGenerator/Android.mk
index f98077e..549668a 100644
--- a/tools/xmlGenerator/Android.mk
+++ b/tools/xmlGenerator/Android.mk
@@ -68,6 +68,19 @@ LOCAL_IS_HOST_MODULE := true
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
+LOCAL_MODULE := domainGenerator.py
+LOCAL_MODULE_OWNER := intel
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+LOCAL_REQUIRED_MODULES := \
+ _PyPfw \
+ EddParser.py \
+ PfwBaseTranslator.py \
+ hostConfig.py
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
LOCAL_MODULE := portAllocator.py
LOCAL_MODULE_OWNER := intel
LOCAL_SRC_FILES := $(LOCAL_MODULE)
diff --git a/tools/xmlGenerator/domainGenerator.py b/tools/xmlGenerator/domainGenerator.py
new file mode 100755
index 0000000..83c71ea
--- /dev/null
+++ b/tools/xmlGenerator/domainGenerator.py
@@ -0,0 +1,313 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2011-2014, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation and/or
+# other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors
+# may be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import PyPfw
+import EddParser
+from PfwBaseTranslator import PfwBaseTranslator, PfwException
+import hostConfig
+
+import argparse
+import re
+import sys
+import tempfile
+import os
+import logging
+
+def wrap_pfw_error_semantic(func):
+ def wrapped(*args, **kwargs):
+ ok, error = func(*args, **kwargs)
+ if not ok:
+ raise PfwException(error)
+
+ return wrapped
+
+class PfwTranslator(PfwBaseTranslator):
+ """Generates calls to the Pfw's python bindings"""
+
+ def __init__(self, pfw_instance, error_handler):
+ super(PfwTranslator, self).__init__()
+ self._pfw = pfw_instance
+ self._error_handler = error_handler
+
+ def _handleException(self, ex):
+ if isinstance(ex, PfwException):
+ # catch and handle translation errors...
+ self._error_handler(ex, self._getContext())
+ else:
+ # ...but let any other error fall through
+ raise ex
+
+ @wrap_pfw_error_semantic
+ def _doCreateDomain(self, name):
+ return self._pfw.createDomain(name)
+
+ @wrap_pfw_error_semantic
+ def _doSetSequenceAware(self):
+ return self._pfw.setSequenceAwareness(self._ctx_domain, True)
+
+ @wrap_pfw_error_semantic
+ def _doAddElement(self, path):
+ return self._pfw.addConfigurableElementToDomain(self._ctx_domain, path)
+
+ @wrap_pfw_error_semantic
+ def _doCreateConfiguration(self, name):
+ return self._pfw.createConfiguration(self._ctx_domain, name)
+
+ @wrap_pfw_error_semantic
+ def _doSetElementSequence(self, paths):
+ return self._pfw.setElementSequence(self._ctx_domain, self._ctx_configuration, paths)
+
+ @wrap_pfw_error_semantic
+ def _doSetRule(self, rule):
+ return self._pfw.setApplicationRule(self._ctx_domain, self._ctx_configuration, rule)
+
+ @wrap_pfw_error_semantic
+ def _doSetParameter(self, path, value):
+ ok, _, error = self._pfw.accessConfigurationValue(
+ self._ctx_domain, self._ctx_configuration, path, value, True)
+
+ return ok, error
+
+
+class PfwTranslationErrorHandler:
+ def __init__(self):
+ self._errors = []
+ self._hasFailed = False
+
+ def __call__(self, error, context):
+ sys.stderr.write("Error in context {}:\n\t{}\n".format(context, error))
+ self._hasFailed = True
+
+ def hasFailed(self):
+ return self._hasFailed
+
+# If this file is directly executed
+if __name__ == "__main__":
+ logging.root.setLevel(logging.INFO)
+
+ argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
+ Settings file generator")
+ argparser.add_argument('--toplevel-config',
+ help="Top-level parameter-framework configuration file. Mandatory.",
+ metavar="TOPLEVEL_CONFIG_FILE",
+ required=True)
+ argparser.add_argument('--criteria',
+ help="Criteria file, in '<type> <name> : <value> <value...>' \
+ format. Mandatory.",
+ metavar="CRITERIA_FILE",
+ type=argparse.FileType('r'),
+ required=True)
+ argparser.add_argument('--initial-settings',
+ help="Initial XML settings file (containing a \
+ <ConfigurableDomains> tag",
+ metavar="XML_SETTINGS_FILE")
+ argparser.add_argument('--add-domains',
+ help="List of single domain files (each containing a single \
+ <ConfigurableDomain> tag",
+ metavar="XML_DOMAIN_FILE",
+ nargs='+',
+ dest='xml_domain_files',
+ default=[])
+ argparser.add_argument('--add-edds',
+ help="List of files in EDD syntax (aka \".pfw\" files)",
+ metavar="EDD_FILE",
+ type=argparse.FileType('r'),
+ nargs='+',
+ default=[],
+ dest='edd_files')
+ argparser.add_argument('--schemas-dir',
+ help="Directory of parameter-framework XML Schemas for generation \
+ validation",
+ default=None)
+ argparser.add_argument('--target-schemas-dir',
+ help="Directory of parameter-framework XML Schemas on target \
+ machine (may be different than generating machine). \
+ Defaults to \"Schemas\"",
+ default="Schemas")
+ argparser.add_argument('--validate',
+ help="Validate the settings against XML schemas",
+ action='store_true')
+ argparser.add_argument('--verbose',
+ action='store_true')
+
+ args = argparser.parse_args()
+
+ #
+ # Criteria file
+ #
+ # This file define one criteria per line; they should respect this format:
+ #
+ # <type> <name> : <values>
+ #
+ # Where <type> is 'InclusiveCriterion' or 'ExclusiveCriterion';
+ # <name> is any string w/o whitespace
+ # <values> is a list of whitespace-separated values, each of which is any
+ # string w/o a whitespace
+ criteria_pattern = re.compile(
+ r"^(?P<type>(?:Inclusive|Exclusive)Criterion)\s*" \
+ r"(?P<name>\S+)\s*:\s*" \
+ r"(?P<values>.*)$")
+ criterion_inclusiveness_table = {
+ 'InclusiveCriterion' : True,
+ 'ExclusiveCriterion' : False}
+ all_criteria = []
+
+ # Parse the criteria file
+ for line_number, line in enumerate(args.criteria, 1):
+ match = criteria_pattern.match(line)
+ if not match:
+ raise ValueError("The following line is invalid: {}:{}\n{}".format(
+ args.criteria.name, line_number, line))
+
+ criterion_name = match.groupdict()['name']
+ criterion_type = match.groupdict()['type']
+ criterion_values = re.split("\s*", match.groupdict()['values'])
+
+ criterion_inclusiveness = criterion_inclusiveness_table[criterion_type]
+
+ all_criteria.append({
+ "name" : criterion_name,
+ "inclusive" : criterion_inclusiveness,
+ "values" : criterion_values})
+
+ #
+ # EDD files (aka ".pfw" files)
+ #
+ parsed_edds = []
+ for edd_file in args.edd_files:
+ try:
+ root = parser = EddParser.Parser().parse(edd_file, args.verbose)
+ except EddParser.MySyntaxError as ex:
+ logging.critical(str(ex))
+ logging.info("EXIT ON FAILURE")
+ exit(2)
+
+ try:
+ root.propagate()
+ except EddParser.MyPropagationError, ex :
+ logging.critical(str(ex))
+ logging.info("EXIT ON FAILURE")
+ exit(1)
+
+ parsed_edds.append((edd_file.name, root))
+
+ # We need to modify the toplevel configuration file to account for differences
+ # between development setup and target (installation) setup, in particular, the
+ # TuningMode must be enforced, regardless of what will be allowed on the target
+ with tempfile.NamedTemporaryFile(mode='w') as fake_toplevel_config:
+ install_path = os.path.dirname(os.path.realpath(args.toplevel_config))
+ hostConfig.configure(
+ infile=args.toplevel_config,
+ outfile=fake_toplevel_config,
+ structPath=install_path)
+ fake_toplevel_config.flush()
+
+ # Create a new Pfw instance
+ pfw = PyPfw.ParameterFramework(fake_toplevel_config.name)
+
+ # create and inject all the criteria
+ logging.info("Creating all criteria")
+ for criterion in all_criteria:
+ criterion_type = pfw.createSelectionCriterionType(criterion['inclusive'])
+
+ for numerical, literal in enumerate(criterion['values']):
+ if criterion['inclusive']:
+ # inclusive criteria are "bitfields"
+ numerical = 1 << numerical
+
+ ok = criterion_type.addValuePair(numerical, literal)
+ if not ok:
+ logging.critical("valuepair {}/{} rejected for {}".format(
+ numerical, literal, criterion['name']))
+ exit(1)
+
+ # we don't need the reference to the created criterion type; ignore the
+ # return value
+ pfw.createSelectionCriterion(criterion['name'], criterion_type)
+
+ # Set failure conditions
+ pfw.setFailureOnMissingSubsystem(False)
+ pfw.setFailureOnFailedSettingsLoad(False)
+ if args.validate:
+ pfw.setValidateSchemasOnStart(True)
+ if args.schemas_dir is not None:
+ schemas_dir = args.schemas_dir
+ else:
+ schemas_dir = os.path.join(install_path, "Schemas")
+ pfw.setSchemaFolderLocation(schemas_dir)
+
+ # Finally, start the Pfw
+ ok, error = pfw.start()
+ if not ok:
+ logging.critical("Error while starting the pfw: {}".format(error))
+ exit(1)
+
+ ok, error = pfw.setTuningMode(True)
+ if not ok:
+ logging.critical(error)
+ exit(1)
+
+ # Import initial settings file
+ if args.initial_settings:
+ initial_settings = os.path.realpath(args.initial_settings)
+ logging.info(
+ "Importing initial settings file {}".format(initial_settings))
+ ok, error = pfw.importDomainsXml(initial_settings, True, True)
+ if not ok:
+ logging.critical(error)
+ exit(1)
+
+ # Import each standalone domain files
+ for domain_file in args.xml_domain_files:
+ logging.info("Importing single domain file {}".format(domain_file))
+ ok, error = pfw.importSingleDomainXml(os.path.realpath(domain_file), False)
+ if not ok:
+ logging.critical(error)
+ exit(1)
+
+ # Parse and inject each EDD file
+ error_handler = PfwTranslationErrorHandler()
+ translator = PfwTranslator(pfw, error_handler)
+
+ for filename, parsed_edd in parsed_edds:
+ logging.info("Translating and injecting EDD file {}".format(filename))
+ parsed_edd.translate(translator)
+ if error_handler.hasFailed():
+ logging.error("Error while importing parsed EDD files.\n")
+ exit(1)
+
+ # dirty hack: we change the schema location (right before exporting the
+ # domains) to their location on the target (which may be different than on
+ # the machine that is generating the domains)
+ pfw.setSchemaFolderLocation(args.target_schemas_dir)
+
+ # Export the resulting settings to the standard output
+ ok, domains, error = pfw.exportDomainsXml("", True, False)
+ sys.stdout.write(domains)