Home | History | Annotate | Download | only in xmlGenerator
      1 #! /usr/bin/python
      2 #
      3 # Copyright (c) 2011-2015, Intel Corporation
      4 # All rights reserved.
      5 #
      6 # Redistribution and use in source and binary forms, with or without modification,
      7 # are permitted provided that the following conditions are met:
      8 #
      9 # 1. Redistributions of source code must retain the above copyright notice, this
     10 # list of conditions and the following disclaimer.
     11 #
     12 # 2. Redistributions in binary form must reproduce the above copyright notice,
     13 # this list of conditions and the following disclaimer in the documentation and/or
     14 # other materials provided with the distribution.
     15 #
     16 # 3. Neither the name of the copyright holder nor the names of its contributors
     17 # may be used to endorse or promote products derived from this software without
     18 # specific prior written permission.
     19 #
     20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
     21 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     22 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     23 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
     24 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     25 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     26 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     27 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     29 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 import EddParser
     32 from PFWScriptGenerator import PfwScriptTranslator
     33 import hostConfig
     34 
     35 import argparse
     36 import re
     37 import sys
     38 import tempfile
     39 import os
     40 import logging
     41 import subprocess
     42 
     43 def parseArgs():
     44     argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
     45         Settings file generator.\n\
     46         Exit with the number of (recoverable or not) error that occured.")
     47     argparser.add_argument('--toplevel-config',
     48             help="Top-level parameter-framework configuration file. Mandatory.",
     49             metavar="TOPLEVEL_CONFIG_FILE",
     50             required=True)
     51     argparser.add_argument('--criteria',
     52             help="Criteria file, in '<type> <name> : <value> <value...>' \
     53         format. Mandatory.",
     54             metavar="CRITERIA_FILE",
     55             type=argparse.FileType('r'),
     56             required=True)
     57     argparser.add_argument('--initial-settings',
     58             help="Initial XML settings file (containing a \
     59         <ConfigurableDomains>  tag",
     60             nargs='?',
     61             default=None,
     62             metavar="XML_SETTINGS_FILE")
     63     argparser.add_argument('--add-domains',
     64             help="List of single domain files (each containing a single \
     65         <ConfigurableDomain> tag",
     66             metavar="XML_DOMAIN_FILE",
     67             nargs='*',
     68             dest='xml_domain_files',
     69             default=[])
     70     argparser.add_argument('--add-edds',
     71             help="List of files in EDD syntax (aka \".pfw\" files)",
     72             metavar="EDD_FILE",
     73             type=argparse.FileType('r'),
     74             nargs='*',
     75             default=[],
     76             dest='edd_files')
     77     argparser.add_argument('--schemas-dir',
     78             help="Directory of parameter-framework XML Schemas for generation \
     79         validation",
     80             default=None)
     81     argparser.add_argument('--target-schemas-dir',
     82             help="Ignored. Kept for retro-compatibility")
     83     argparser.add_argument('--validate',
     84             help="Validate the settings against XML schemas",
     85             action='store_true')
     86     argparser.add_argument('--verbose',
     87             action='store_true')
     88 
     89     return argparser.parse_args()
     90 
     91 def parseCriteria(criteriaFile):
     92     # Parse a criteria file
     93     #
     94     # This file define one criteria per line; they should respect this format:
     95     #
     96     # <type> <name> : <values>
     97     #
     98     # Where <type> is 'InclusiveCriterion' or 'ExclusiveCriterion';
     99     #       <name> is any string w/o whitespace
    100     #       <values> is a list of whitespace-separated values, each of which is any
    101     #                string w/o a whitespace
    102     criteria_pattern = re.compile(
    103         r"^(?P<type>(?:Inclusive|Exclusive)Criterion)\s*" \
    104         r"(?P<name>\S+)\s*:\s*" \
    105         r"(?P<values>.*)$")
    106     criterion_inclusiveness_table = {
    107         'InclusiveCriterion' : "inclusive",
    108         'ExclusiveCriterion' : "exclusive"}
    109 
    110     all_criteria = []
    111     for line_number, line in enumerate(criteriaFile):
    112         match = criteria_pattern.match(line)
    113         if not match:
    114             raise ValueError("The following line is invalid: {}:{}\n{}".format(
    115                 criteriaFile.name, line_number, line))
    116 
    117         criterion_name = match.groupdict()['name']
    118         criterion_type = match.groupdict()['type']
    119         criterion_values = re.split("\s*", match.groupdict()['values'])
    120 
    121         criterion_inclusiveness = criterion_inclusiveness_table[criterion_type]
    122 
    123         all_criteria.append({
    124             "name" : criterion_name,
    125             "inclusive" : criterion_inclusiveness,
    126             "values" : criterion_values})
    127 
    128     return all_criteria
    129 
    130 def parseEdd(EDDFiles):
    131     parsed_edds = []
    132 
    133     for edd_file in EDDFiles:
    134         try:
    135             root = EddParser.Parser().parse(edd_file)
    136         except EddParser.MySyntaxError as ex:
    137             logging.critical(str(ex))
    138             logging.info("EXIT ON FAILURE")
    139             exit(2)
    140 
    141         try:
    142             root.propagate()
    143         except EddParser.MyPropagationError, ex :
    144             logging.critical(str(ex))
    145             logging.info("EXIT ON FAILURE")
    146             exit(1)
    147 
    148         parsed_edds.append((edd_file.name, root))
    149     return parsed_edds
    150 
    151 def generateDomainCommands(logging, all_criteria, initial_settings, xml_domain_files, parsed_edds):
    152         # create and inject all the criteria
    153         logging.info("Creating all criteria")
    154         for criterion in all_criteria:
    155             yield ["createSelectionCriterion", criterion['inclusive'],
    156                    criterion['name']] + criterion['values']
    157 
    158         yield ["start"]
    159 
    160         # Import initial settings file
    161         if initial_settings:
    162             logging.info("Importing initial settings file {}".format(initial_settings))
    163             yield ["importDomainsWithSettingsXML", initial_settings]
    164 
    165         # Import each standalone domain files
    166         for domain_file in xml_domain_files:
    167             logging.info("Importing single domain file {}".format(domain_file))
    168             yield ["importDomainWithSettingsXML", domain_file]
    169 
    170         # Generate the script for each EDD file
    171         for filename, parsed_edd in parsed_edds:
    172             logging.info("Translating and injecting EDD file {}".format(filename))
    173             translator = PfwScriptTranslator()
    174             parsed_edd.translate(translator)
    175             for command in translator.getScript():
    176                 yield command
    177 
    178 def main():
    179     logging.root.setLevel(logging.INFO)
    180     args = parseArgs()
    181 
    182     all_criteria = parseCriteria(args.criteria)
    183 
    184     #
    185     # EDD files (aka ".pfw" files)
    186     #
    187     parsed_edds = parseEdd(args.edd_files)
    188 
    189     # We need to modify the toplevel configuration file to account for differences
    190     # between development setup and target (installation) setup, in particular, the
    191     # TuningMwith ode must be enforced, regardless of what will be allowed on the target
    192     fake_toplevel_config = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".xml",
    193                                                        prefix="TMPdomainGeneratorPFConfig_")
    194 
    195     install_path = os.path.dirname(os.path.realpath(args.toplevel_config))
    196     hostConfig.configure(
    197             infile=args.toplevel_config,
    198             outfile=fake_toplevel_config,
    199             structPath=install_path)
    200     fake_toplevel_config.close()
    201 
    202     # Create the connector. Pipe its input to us in order to write commands;
    203     # connect its output to stdout in order to have it dump the domains
    204     # there; connect its error output to stderr.
    205     connector = subprocess.Popen(["domainGeneratorConnector",
    206                             fake_toplevel_config.name,
    207                             'verbose' if args.verbose else 'no-verbose',
    208                             'validate' if args.validate else 'no-validate',
    209                             args.schemas_dir],
    210                            stdout=sys.stdout, stdin=subprocess.PIPE, stderr=sys.stderr)
    211 
    212     initial_settings = None
    213     if args.initial_settings:
    214         initial_settings = os.path.realpath(args.initial_settings)
    215 
    216     for command in generateDomainCommands(logging, all_criteria, initial_settings,
    217                                        args.xml_domain_files, parsed_edds):
    218         connector.stdin.write('\0'.join(command))
    219         connector.stdin.write("\n")
    220 
    221     # Closing the connector's input triggers the domain generation
    222     connector.stdin.close()
    223     connector.wait()
    224     os.remove(fake_toplevel_config.name)
    225     return connector.returncode
    226 
    227 # If this file is directly executed
    228 if __name__ == "__main__":
    229     exit(main())
    230