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 31import EddParser 32from PFWScriptGenerator import PfwScriptTranslator 33import hostConfig 34 35import argparse 36import re 37import sys 38import tempfile 39import os 40import logging 41import subprocess 42 43def 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 91def 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 130def 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 151def 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 178def 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 228if __name__ == "__main__": 229 exit(main()) 230