• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2
3#
4# Copyright 2018, The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import EddParser
20from PFWScriptGenerator import PfwScriptTranslator
21import hostConfig
22
23import argparse
24import re
25import sys
26import tempfile
27import os
28import logging
29import subprocess
30import xml.etree.ElementTree as ET
31
32#
33# In order to build the XML Settings file at build time, an instance of the parameter-framework
34# shall be started and fed with all the criterion types/criteria that will be used by
35# the engineconfigurable.
36# This scripts allows generates the settings from the same audio_criterion_types.xml /
37# audio_criteria.xml files used at run time by the engineconfigurable
38#
39
40def parseArgs():
41    argparser = argparse.ArgumentParser(description="Parameter-Framework XML \
42        Settings file generator.\n\
43        Exit with the number of (recoverable or not) error that occured.")
44    argparser.add_argument('--toplevel-config',
45            help="Top-level parameter-framework configuration file. Mandatory.",
46            metavar="TOPLEVEL_CONFIG_FILE",
47            required=True)
48    argparser.add_argument('--criteria',
49            help="Criteria file, in XML format: \
50                  in '<criteria> \
51                          <criterion name="" type=""/> \
52                      </criteria>' \
53        format. Mandatory.",
54            metavar="CRITERIA_FILE",
55            type=argparse.FileType('r'),
56            required=True)
57    argparser.add_argument('--criteriontypes',
58            help="Criterion types XML file, in \
59            '<criterion_types> \
60                <criterion_type name="" type=<inclusive|exclusive> values=<value1,value2,...>/> \
61             </criterion_types>' \
62        format. Mandatory.",
63            metavar="CRITERION_TYPE_FILE",
64            type=argparse.FileType('r'),
65            required=False)
66    argparser.add_argument('--initial-settings',
67            help="Initial XML settings file (containing a \
68        <ConfigurableDomains>  tag",
69            nargs='?',
70            default=None,
71            metavar="XML_SETTINGS_FILE")
72    argparser.add_argument('--add-domains',
73            help="List of single domain files (each containing a single \
74        <ConfigurableDomain> tag",
75            metavar="XML_DOMAIN_FILE",
76            nargs='*',
77            dest='xml_domain_files',
78            default=[])
79    argparser.add_argument('--add-edds',
80            help="List of files in EDD syntax (aka \".pfw\" files)",
81            metavar="EDD_FILE",
82            type=argparse.FileType('r'),
83            nargs='*',
84            default=[],
85            dest='edd_files')
86    argparser.add_argument('--schemas-dir',
87            help="Directory of parameter-framework XML Schemas for generation \
88        validation",
89            default=None)
90    argparser.add_argument('--target-schemas-dir',
91            help="Ignored. Kept for retro-compatibility")
92    argparser.add_argument('--validate',
93            help="Validate the settings against XML schemas",
94            action='store_true')
95    argparser.add_argument('--verbose',
96            action='store_true')
97
98    return argparser.parse_args()
99
100#
101# Parses audio_criterion_types.xml / audio_criteria.xml files used at run time by the
102# engineconfigurable and outputs a dictionnary of criteria.
103# For each criteria, the name, type (aka inclusive (bitfield) or exclusive (enum), the values
104# are provided.
105#
106def parseCriteriaAndCriterionTypes(criteriaFile, criterionTypesFile):
107    # Parse criteria and criterion types XML files
108    #
109    criteria_tree = ET.parse(criteriaFile)
110    logging.info("Importing criteriaFile {}".format(criteriaFile))
111    criterion_types_tree = ET.parse(criterionTypesFile)
112    logging.info("Importing criterionTypesFile {}".format(criterionTypesFile))
113
114    criteria_root = criteria_tree.getroot()
115    criterion_types_root = criterion_types_tree.getroot()
116
117    all_criteria = []
118    for criterion in criteria_root.findall('criterion'):
119        criterion_name = criterion.get('name')
120        type_name = criterion.get('type')
121        logging.info("Importing criterion_name {}".format(criterion_name))
122        logging.info("Importing type_name {}".format(type_name))
123
124        for criterion_types in criterion_types_tree.findall('criterion_type'):
125            criterion_type_name = criterion_types.get('name')
126            if criterion_type_name == type_name:
127                criterion_inclusiveness = criterion_types.get('type')
128
129                criterion_values = []
130
131                values_node = criterion_types.find('values')
132                if values_node is not None:
133                    for value in values_node.findall('value'):
134                        criterion_values.append(value.get('literal'))
135
136                if len(criterion_values) == 0:
137                    criterion_values.append('')
138
139                logging.info("Importing criterion_type_name {}".format(criterion_type_name))
140                logging.info("Importing criterion_inclusiveness {}".format(criterion_inclusiveness))
141                logging.info("Importing criterion_values {}".format(criterion_values))
142
143                all_criteria.append({
144                    "name" : criterion_name,
145                    "inclusive" : criterion_inclusiveness,
146                    "values" : criterion_values})
147                break
148
149    return all_criteria
150
151#
152# Parses the Edd files (aka .pfw extension file), which is a simplified language to write the
153# parameter framework settings.
154#
155def parseEdd(EDDFiles):
156    parsed_edds = []
157
158    for edd_file in EDDFiles:
159        try:
160            root = EddParser.Parser().parse(edd_file)
161        except EddParser.MySyntaxError as ex:
162            logging.critical(str(ex))
163            logging.info("EXIT ON FAILURE")
164            exit(2)
165
166        try:
167            root.propagate()
168        except EddParser.MyPropagationError, ex :
169            logging.critical(str(ex))
170            logging.info("EXIT ON FAILURE")
171            exit(1)
172
173        parsed_edds.append((edd_file.name, root))
174    return parsed_edds
175
176#
177# Generates all the required commands to be sent to the instance of parameter-framework launched
178# at runtime to generate the XML Settings file.
179# It takes as input the collection of criteria, the domains and the simplified settings read from
180# pfw.
181#
182def generateDomainCommands(logging, all_criteria, initial_settings, xml_domain_files, parsed_edds):
183        # create and inject all the criteria
184        logging.info("Creating all criteria")
185        for criterion in all_criteria:
186            yield ["createSelectionCriterion", criterion['inclusive'],
187                   criterion['name']] + criterion['values']
188
189        yield ["start"]
190
191        # Import initial settings file
192        if initial_settings:
193            logging.info("Importing initial settings file {}".format(initial_settings))
194            yield ["importDomainsWithSettingsXML", initial_settings]
195
196        # Import each standalone domain files
197        for domain_file in xml_domain_files:
198            logging.info("Importing single domain file {}".format(domain_file))
199            yield ["importDomainWithSettingsXML", domain_file]
200
201        # Generate the script for each EDD file
202        for filename, parsed_edd in parsed_edds:
203            logging.info("Translating and injecting EDD file {}".format(filename))
204            translator = PfwScriptTranslator()
205            parsed_edd.translate(translator)
206            for command in translator.getScript():
207                yield command
208
209#
210# Entry point of the domain generator.
211#       -Parses Criterion types and criteria files
212#       -Parses settings written in simplified pfw language.
213#       -Launches a parameter-framework
214#       -Translates the settings into command that can be interpreted by parameter-framework.
215#       -Use the exports command and output them in XML Settings file.
216#
217def main():
218    logging.root.setLevel(logging.INFO)
219    args = parseArgs()
220
221    all_criteria = parseCriteriaAndCriterionTypes(args.criteria, args.criteriontypes)
222
223    #
224    # EDD files (aka ".pfw" files)
225    #
226    parsed_edds = parseEdd(args.edd_files)
227
228    # We need to modify the toplevel configuration file to account for differences
229    # between development setup and target (installation) setup, in particular, the
230    # TuningMwith ode must be enforced, regardless of what will be allowed on the target
231    fake_toplevel_config = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=".xml",
232                                                       prefix="TMPdomainGeneratorPFConfig_")
233
234    install_path = os.path.dirname(os.path.realpath(args.toplevel_config))
235    hostConfig.configure(
236            infile=args.toplevel_config,
237            outfile=fake_toplevel_config,
238            structPath=install_path)
239    fake_toplevel_config.close()
240
241    # Create the connector. Pipe its input to us in order to write commands;
242    # connect its output to stdout in order to have it dump the domains
243    # there; connect its error output to stderr.
244    connector = subprocess.Popen(["domainGeneratorConnector",
245                            fake_toplevel_config.name,
246                            'verbose' if args.verbose else 'no-verbose',
247                            'validate' if args.validate else 'no-validate',
248                            args.schemas_dir],
249                           stdout=sys.stdout, stdin=subprocess.PIPE, stderr=sys.stderr)
250
251    initial_settings = None
252    if args.initial_settings:
253        initial_settings = os.path.realpath(args.initial_settings)
254
255    for command in generateDomainCommands(logging, all_criteria, initial_settings,
256                                       args.xml_domain_files, parsed_edds):
257        connector.stdin.write('\0'.join(command))
258        connector.stdin.write("\n")
259
260    # Closing the connector's input triggers the domain generation
261    connector.stdin.close()
262    connector.wait()
263    os.remove(fake_toplevel_config.name)
264    return connector.returncode
265
266# If this file is directly executed
267if __name__ == "__main__":
268    sys.exit(main())
269