1#! /usr/bin/python3 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 31import argparse 32import re 33import sys 34import tempfile 35import os 36import logging 37import subprocess 38 39import EddParser 40from PFWScriptGenerator import PfwScriptTranslator 41import hostConfig 42 43 44def parseArgs(): 45 argparser = argparse.ArgumentParser(description="Parameter-Framework XML \ 46 Settings file generator.\n\ 47 Exit with the number of (recoverable or not) \ 48 error that occured.") 49 argparser.add_argument('--toplevel-config', 50 help="Top-level parameter-framework configuration file. Mandatory.", 51 metavar="TOPLEVEL_CONFIG_FILE", 52 required=True) 53 argparser.add_argument('--criteria', 54 help="Criteria file, in '<type> <name> : <value> <value...>' \ 55 format. Mandatory.", 56 metavar="CRITERIA_FILE", 57 type=argparse.FileType('r'), 58 required=True) 59 argparser.add_argument('--initial-settings', 60 help="Initial XML settings file (containing a \ 61 <ConfigurableDomains> tag", 62 nargs='?', 63 default=None, 64 metavar="XML_SETTINGS_FILE") 65 argparser.add_argument('--add-domains', 66 help="List of single domain files (each containing a single \ 67 <ConfigurableDomain> tag", 68 metavar="XML_DOMAIN_FILE", 69 nargs='*', 70 dest='xml_domain_files', 71 default=[]) 72 argparser.add_argument('--add-edds', 73 help="List of files in EDD syntax (aka \".pfw\" files)", 74 metavar="EDD_FILE", 75 type=argparse.FileType('r'), 76 nargs='*', 77 default=[], 78 dest='edd_files') 79 argparser.add_argument('--schemas-dir', 80 help="Directory of parameter-framework XML Schemas for generation \ 81 validation", 82 default=None) 83 argparser.add_argument('--target-schemas-dir', 84 help="Ignored. Kept for retro-compatibility") 85 argparser.add_argument('--validate', 86 help="Validate the settings against XML schemas", 87 action='store_true') 88 argparser.add_argument('--verbose', 89 action='store_true') 90 91 return argparser.parse_args() 92 93def parseCriteria(criteriaFile): 94 # Parse a criteria file 95 # 96 # This file define one criteria per line; they should respect this format: 97 # 98 # <type> <name> : <values> 99 # 100 # Where <type> is 'InclusiveCriterion' or 'ExclusiveCriterion'; 101 # <name> is any string w/o whitespace 102 # <values> is a list of whitespace-separated values, each of which is any 103 # string w/o a whitespace 104 criteria_pattern = re.compile( 105 r"^(?P<type>(?:Inclusive|Exclusive)Criterion)\s*" \ 106 r"(?P<name>\S+)\s*:\s*" \ 107 r"(?P<values>.*)$") 108 criterion_inclusiveness_table = { 109 'InclusiveCriterion' : "inclusive", 110 'ExclusiveCriterion' : "exclusive"} 111 112 all_criteria = [] 113 for line_number, line in enumerate(criteriaFile): 114 match = criteria_pattern.match(line) 115 if not match: 116 raise ValueError("The following line is invalid: {}:{}\n{}".format( 117 criteriaFile.name, line_number, line)) 118 119 criterion_name = match.groupdict()['name'] 120 criterion_type = match.groupdict()['type'] 121 criterion_values = re.split("\s*", match.groupdict()['values']) 122 123 criterion_inclusiveness = criterion_inclusiveness_table[criterion_type] 124 125 all_criteria.append({ 126 "name" : criterion_name, 127 "inclusive" : criterion_inclusiveness, 128 "values" : criterion_values}) 129 130 return all_criteria 131 132def parseEdd(EDDFiles): 133 parsed_edds = [] 134 135 for edd_file in EDDFiles: 136 try: 137 root = EddParser.Parser().parse(edd_file) 138 except EddParser.MySyntaxError as ex: 139 logging.critical(str(ex)) 140 logging.info("EXIT ON FAILURE") 141 exit(2) 142 143 try: 144 root.propagate() 145 except EddParser.MyPropagationError as ex: 146 logging.critical(str(ex)) 147 logging.info("EXIT ON FAILURE") 148 exit(1) 149 150 parsed_edds.append((edd_file.name, root)) 151 return parsed_edds 152 153def generateDomainCommands(logger, all_criteria, initial_settings, xml_domain_files, parsed_edds): 154 # create and inject all the criteria 155 logger.info("Creating all criteria") 156 for criterion in all_criteria: 157 yield ["createSelectionCriterion", criterion['inclusive'], 158 criterion['name']] + criterion['values'] 159 160 yield ["start"] 161 162 # Import initial settings file 163 if initial_settings: 164 logger.info("Importing initial settings file {}".format(initial_settings)) 165 yield ["importDomainsWithSettingsXML", initial_settings] 166 167 # Import each standalone domain files 168 for domain_file in xml_domain_files: 169 logger.info("Importing single domain file {}".format(domain_file)) 170 yield ["importDomainWithSettingsXML", domain_file] 171 172 # Generate the script for each EDD file 173 for filename, parsed_edd in parsed_edds: 174 logger.info("Translating and injecting EDD file {}".format(filename)) 175 translator = PfwScriptTranslator() 176 parsed_edd.translate(translator) 177 for command in translator.getScript(): 178 yield command 179 180def main(): 181 logging.root.setLevel(logging.INFO) 182 args = parseArgs() 183 184 all_criteria = parseCriteria(args.criteria) 185 186 # 187 # EDD files (aka ".pfw" files) 188 # 189 parsed_edds = parseEdd(args.edd_files) 190 191 # We need to modify the toplevel configuration file to account for differences 192 # between development setup and target (installation) setup, in particular, the 193 # TuningMwith ode must be enforced, regardless of what will be allowed on the target 194 fake_toplevel_config = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".xml", 195 prefix="TMPdomainGeneratorPFConfig_") 196 197 install_path = os.path.dirname(os.path.realpath(args.toplevel_config)) 198 hostConfig.configure(infile=args.toplevel_config, 199 outfile=fake_toplevel_config, 200 structPath=install_path) 201 fake_toplevel_config.close() 202 203 # Create the connector. Pipe its input to us in order to write commands; 204 # connect its output to stdout in order to have it dump the domains 205 # there; connect its error output to stderr. 206 connector = subprocess.Popen(["domainGeneratorConnector", 207 fake_toplevel_config.name, 208 'verbose' if args.verbose else 'no-verbose', 209 'validate' if args.validate else 'no-validate', 210 args.schemas_dir], 211 stdout=sys.stdout, stdin=subprocess.PIPE, stderr=sys.stderr) 212 213 initial_settings = None 214 if args.initial_settings: 215 initial_settings = os.path.realpath(args.initial_settings) 216 217 for command in generateDomainCommands(logging, all_criteria, initial_settings, 218 args.xml_domain_files, parsed_edds): 219 connector.stdin.write('\0'.join(command)) 220 connector.stdin.write("\n") 221 222 # Closing the connector's input triggers the domain generation 223 connector.stdin.close() 224 connector.wait() 225 os.remove(fake_toplevel_config.name) 226 return connector.returncode 227 228# If this file is directly executed 229if __name__ == "__main__": 230 exit(main()) 231